2

我需要从两个不同的基类CBaseACBaseB派生一个子类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)

4

2 回答 2

7
CBaseA* pAHandle = reinterpret_cast<CBaseA*>( &oDerived );

不要reinterpret_cast用于执行到基类的转换。不需要演员表;转换是隐式的:

CBaseA* pAHandle = &oDerived;

对于转换为派生类,static_cast如果已知对象属于目标类型或dynamic_cast不是,则使用。

您的使用会reinterpret_cast产生未定义的行为,因此会产生您看到的“奇怪”行为。很少有正确的使用,reinterpret_cast并且它们都不涉及类层次结构内的转换。

于 2012-08-27T18:01:32.890 回答
2

常见的实现,可以帮助您了解发生了什么。

内存中的 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 虚函数。

于 2012-08-27T18:41:12.817 回答