1

C++ の仮想継承に問題があります。

次のようなクラス階層があります。

class Base
{
public:
    virtual void Func(){};
    int BaseValue;
};

class Derived : virtual public Base
{
public:
    void Func(){};
    virtual void Func2(){};
    int DerivedValue;
};

これは健全にコンパイルされますが、メモリ構造について少し混乱しています。

sizeof(Derived)==20つまり、次の結果が必要です。

  1. BaseValue および DerivedValue - 8 バイト
  2. ポインタはBaseクラスのメンバオフセットを表す (仮想継承の特徴)--4バイト
  3. ポインタは Base クラスの仮想関数テーブルを示します - 4 バイト
  4. Pointer は、Func2()クラス Derived にのみ属する仮想関数を示します-4 バイト (私に関する限り、非仮想基本クラスを持たず、独自の仮想関数を取得する派生クラスには、独自の仮想テーブルが必要です)

合計で 20 バイトになります。

Xcode 4.6 は の異なる結果を生成しますがsizeof(Derived)==16、間違っていますか?

4

1 に答える 1

1

ポインターは、基本クラスの仮想関数テーブルを示します--4 バイト
ポインターは、Derived クラスにのみ属する仮想関数 Func2() を示します-4 バイト (私の知る限り、非仮想基本クラスを持たない派生クラス)独自の仮想関数を取得し、独自の仮想テーブルを持つ必要があります)

ああ、私は今問題を見ます。それは、仮想関数テーブルがどのように機能するかということではありません。が定義されると、コンパイラは仮想テーブルが必要であることを認識し、実装を指す1 つのポインター ( ) を使用Baseして の仮想テーブルを生成します。が定義されると、コンパイラはそれが から継承していることに気づき、を指すと を指すの 2 つのポインタを持つの関数テーブルを生成します。BaseFuncBase::FuncDerivedBaseBaseFuncDerived::FuncFunc2Derived::Func2

次に、 のインスタンスBaseが作成された場合、そのテーブルを指していると言及したのは関数テーブル ポインターでBaseあり、 へのすべての呼び出しFuncは にリダイレクトされBase::Funcます。

のインスタンスDerivedが作成された場合、その内部Baseオブジェクトの仮想関数テーブル ポインターはDerived代わりにテーブルを指します。 Baseはポインターにアクセスする方法しか知りませんFuncが、そのFuncポインターは現在を指してDerived::Funcいるため、それが get と呼ばれるものです。別のテーブルを指していることに気づきません。コードでは、次のようになります。

using voidFunctionType = void(*)();

struct BaseVTable {
    voidFunctionType Func;
}BaseVTableGlobal; 

struct Base {
    Base() :vTable(&BaseVTableGlobal) {}
    void Func() {vTable->Func();}

    BaseVTable* vTable; //4 bytes
    int BaseValue; //4 bytes
}; //total is 8 bytes

struct DerivedVTable : public BaseVTable  {
    voidFunctionType Func;
    voidFunctionType Func2;
}DerivedVTableGlobal; 

//inherits 8 bytes, +4 for virtual inheritance = 12
struct Derived : virtual public Base { 
    Derived() :Base() {vTable = &DerivedVTableGlobal;} //the shared vTable points at DerivedVTableGlobal
    void Func() {vTable->Func();} //base knows about Func, so this is easy
    void Func2() {((DerivedVTable*)vTable)->Func2();} //base doesn't know about Func2

    int DerivedValue; //4 bytes
}; //16 bytes total

したがって、XCodeは正しいです。 Derived仮想関数テーブルの「ハイジャック」Baseであり、実際、仮想関数がその魔法を行う方法は まさにそれです。

(あらゆる場所での仮定、明確に定義されたものはなく、仮想的な継承が物事を複雑にするなど)

于 2013-08-22T19:51:18.037 に答える