ポインターは、基本クラスの仮想関数テーブルを示します--4 バイト
ポインターは、Derived クラスにのみ属する仮想関数 Func2() を示します-4 バイト (私の知る限り、非仮想基本クラスを持たない派生クラス)独自の仮想関数を取得し、独自の仮想テーブルを持つ必要があります)
ああ、私は今問題を見ます。それは、仮想関数テーブルがどのように機能するかということではありません。が定義されると、コンパイラは仮想テーブルが必要であることを認識し、実装を指す1 つのポインター ( ) を使用Base
して の仮想テーブルを生成します。が定義されると、コンパイラはそれが から継承していることに気づき、を指すと を指すの 2 つのポインタを持つの関数テーブルを生成します。Base
Func
Base::Func
Derived
Base
Base
Func
Derived::Func
Func2
Derived::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
であり、実際、仮想関数がその魔法を行う方法は まさにそれです。
(あらゆる場所での仮定、明確に定義されたものはなく、仮想的な継承が物事を複雑にするなど)