class Base1 {
virtual void fun1() { cout << "Base1::fun1()" << endl; }
virtual void func1() { cout << "Base1::func1()" << endl; }
};
class Base2 {
virtual void fun1() { cout << "Base2::fun1()" << endl; }
virtual void func1() { cout << "Base2::func1()" << endl; }
};
class Test:public Base1,public Base2
{
public:
virtual void test(){cout<<"Test";}
};
typedef void(*Fun)(void);
int main()
{
Test objTest;
Fun pFun = NULL;
pFun = (Fun)*((int*)*(int*)((int*)&objTest+0)+0); pFun();
pFun = (Fun)*((int*)*(int*)((int*)&objTest+0)+1); pFun();
//The following isnt supposed to print Test::test() right?
pFun = (Fun)*((int*)*(int*)((int*)&objTest+0)+2); pFun();
pFun = (Fun)*((int*)*(int*)((int*)&objTest+1)+0); pFun();
pFun = (Fun)*((int*)*(int*)((int*)&objTest+1)+1); pFun();
//Isnt the following supposed to print Test:test() because the order of
construction object is Base1 followed by construction of Base2 followed by
construction of Test.
pFun = (Fun)*((int*)*(int*)((int*)&objTest+1)+2); pFun();
}
Test オブジェクトのサイズは 8 バイトです。したがって、この例から、オブジェクトが 2 つの 4 バイトの _vptr で構成されていることが明らかです。継承の順序はpublic Base1,public Base2
次のとおりであるため、オブジェクトは次のように作成する必要があります。
| _vptr to class Base1 vTable | -->this Base1 vtable should have 2 elements.
| _vptr to class Base2 vTable | -->this Base2 vtable should have 3 elements.
しかし、コード スニペットから、オブジェクトは次のように作成されているように見えます。
| _vptr to class Base1 vTable | -->this Base1 vtable should have 3 elements.
| _vptr to class Base2 vTable | -->this Base2 vtable should have 2 elements.
最初の vptr は、3 つの関数ポインター (1 番目のポイントBase1::fun1()
、2 番目のポイント、Base1::func1()
および 3 番目のポイント) の配列をポイントしTest::test()
ます。
派生オブジェクトは、Base+Derived で構成されます。つまり、バイトの最初のチャンクが Base オブジェクトであり、残りが Derived であることを意味します。もしそうなら、私たちの objTest の例では、2 番目は 3 つの関数ポインタ (1 番目と、および)_vptr
を指すはずです。しかし、代わりに、最初のが の関数ポインタを指していることがわかります。Base2::fun1()
Base2::func1()
Test::test()
_vptr
Test::test()
質問:
1.この動作はコンパイラ固有のものですか?
2.規格はこの動作について何か言及していますか? それとも私の理解は完全に間違っていますか?