2

2 つの異なる基本クラスCBaseACBaseBから子クラスCDerivedを派生させる必要があります。

さらに、派生クラスで両方の親の仮想関数を呼び出す必要があります。後で 1 つのベクトルで異なる型のオブジェクトを管理したいので (これはこの最小限のコード例の一部ではありません)、基底クラス ポインターから派生クラス オブジェクトへの仮想関数を呼び出す必要があります。

#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; 
}

問題:しかし、これを私のコンピューターで実行すると、 FuncA()の代わりにFuncB ()が呼び出されます。親クラスの宣言を「反転」すると、正しい結果が得られます。

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 |
+---------+

CDerived は次のようになります。

             +---------+
&oDerived->  | __vptrB |
             | __vptrA |
             +---------+

&oDerived を CBaseA* に単純に割り当てると、コンパイラはオフセットを追加するコードを配置するので、

             +---------+
&oDerived--->| __vptrB |
pAHandle---->| __vptrA |
             +---------+

実行中に、プログラムは __vptrA 内の仮想関数へのポインターを見つけます。static_cast または dynamic_cast pAHandle を CDerived に戻す (または dynamic_cast pAHandle を CBaseA に戻す) 場合、コンパイラはオフセットを減算するコードを配置して、結果がオブジェクトの開始点を指すようにします (dynamic_cast は、どのくらいの量を処理するかについての情報を見つけます)。仮想関数へのポインタとともに vtable を減算します)。

&oDerived を CBaseA* として再解釈_キャストすると、コンパイラはポインターを調整するためにそのようなコードを配置しません。

                       +---------+
pAHandle, &oDerived--->| __vptrB |
                       | __vptrA |
                       +---------+

実行中、プログラムは A 仮想関数の __vptrB を調べ、代わりに B 仮想関数を見つけました。

于 2012-08-27T18:41:12.817 に答える