C++“处理多个基类的虚拟函数”
Posted
技术标签:
【中文标题】C++“处理多个基类的虚拟函数”【英文标题】:C++ "Virtual functions handling on multiple base classes" 【发布时间】:2012-08-27 17:59:53 【问题描述】:我需要从两个不同的基类CBaseA和CBaseB派生一个子类CDerived。
另外,我需要在派生类上调用双亲的虚函数。因为我想稍后在一个向量中管理不同类型的对象(这不是这个最小代码示例的一部分),我需要从基类指针调用虚拟函数到派生类对象:
#include <iostream>
#include <stdlib.h>
class CBaseA
public:
virtual void FuncA() std::cout << "CBaseA::FuncA()" << std::endl; ;
;
class CBaseB
public:
virtual void FuncB() std::cout << "CBaseB::FuncB()" << std::endl; ;
;
class CDerived : public CBaseB, public CBaseA
;
int main( int argc, char* argv[] )
// An object of the derived type:
CDerived oDerived;
// A base class pointer to the object, as it could later
// be stored in a general vector:
CBaseA* pAHandle = reinterpret_cast<CBaseA*>( &oDerived );
// Calling method A:
pAHandle->FuncA();
return 0;
问题:但在我的计算机上运行时,调用的是 FuncB() 而不是 FuncA()。如果我“翻转”父类声明,我会得到正确的结果,即
class CDerived : public CBaseA, public CBaseB
但这并不能解决我的问题,因为我无法确定将调用哪个函数。
所以我的问题是: 我做错了什么以及处理此类问题的正确方法是什么?
(顺便说一下,我使用的是 g++ 4.6.2)
【问题讨论】:
【参考方案1】:CBaseA* pAHandle = reinterpret_cast<CBaseA*>( &oDerived );
不要使用reinterpret_cast
执行到基类的转换。不需要演员表;转换是隐式的:
CBaseA* pAHandle = &oDerived;
要转换为派生类,如果已知对象属于目标类型,则使用static_cast
;如果不是,则使用dynamic_cast
。
您对reinterpret_cast
的使用会产生未定义的行为,因此您会看到“奇怪”的行为。 reinterpret_cast
的正确用法很少,而且它们都不涉及类层次结构中的转换。
【讨论】:
事实上,没有正确使用 reinterpret_cast 不涉及在对它进行任何操作之前将其转换回旧类型。 @DirkHolsople:还有其他有效的用途。例如,任何对象都可以重新解释为字节数组。 我指的是便携式使用。将对象重新解释为字节数组本质上是特定于平台的。 标准涵盖的有趣转换:5.2.10.4 & 5:将指针转换为整数类型并返回。 5.2.10.11:将T
对象转换为T&
或T&&
。 9.2.20:“指向标准布局结构对象的指针,使用 reinterpret_cast 适当转换,指向其初始成员(或者如果该成员是位字段,则指向它所在的单元),反之亦然。 " 26.4.4: 对复杂常见的实现,可以帮助您了解发生了什么。
内存中的CBaseA是这样的
+---------+
| __vptrA |
+---------+
内存中的CBaseB是这样的
+---------+
| __vptrB |
+---------+
CDrived 看起来像这样:
+---------+
&oDerived-> | __vptrB |
| __vptrA |
+---------+
如果您只是将 &oDerived 分配给 CBaseA*,编译器会放置代码来添加偏移量,以便您拥有
+---------+
&oDerived--->| __vptrB |
pAHandle---->| __vptrA |
+---------+
在执行过程中,程序在 __vptrA 中找到指向 A 虚函数的指针。如果您将 static_cast 或 dynamic_cast pAHandle 返回到 CDerived(甚至 dynamic_cast pAHandle 到 CBaseA),编译器会将代码减去偏移量,以便结果指向对象的开头(dynamic_cast 会找到关于减去 vtable 以及指向虚函数的指针)。
当你将 &oDerived 重新解释为 CBaseA* 时,编译器不会放置这样的代码来调整指针,你会得到
+---------+
pAHandle, &oDerived--->| __vptrB |
| __vptrA |
+---------+
在执行过程中,程序在 __vptrB 中查找 A 虚函数,却找到了 B 虚函数。
【讨论】:
谢谢,这有助于我更好地理解类指针转换问题。这条信息在互联网上很难找到。以上是关于C++“处理多个基类的虚拟函数”的主要内容,如果未能解决你的问题,请参考以下文章
调用非虚拟基方法时,C++ 中的虚拟继承是不是有任何惩罚/成本?