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();
}
testof对象的sizeof是8个字节。
因此,从这个例子可以看出,该对象由两个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()
,第三个指向Test::test()
)。
派生对象由Base + Derived组成。这意味着第一个字节块是Base对象,剩下的是Derived。
如果是这样,那么在我们的objTest示例中,第二个_vptr
应该指向三个函数指针(1st到Base2::fun1()
,Base2::func1()
和Test::test()
)。但我们看到第一个_vptr
指向Test::test()
的函数指针。
问题:
1。此行为编译器是否具体?
2。标准是否提及有关此行为的任何内容?或者我的理解是完全错误的?
答案 0 :(得分:0)
如何构造vtable绝对是依赖于编译器的。当你有多个继承时会发生什么。特别是这两个vtable的生成顺序......
除了vtable之外还存储了其他东西 - 例如类型信息,因此可以进行动态投射。
标准所要求的只是&#34;虚拟功能工作&#34; (当然,它有成千上万的文字来描述&#34;工作&#34;意味着什么),但它的实现方式完全取决于编译器[和某种程度上的C ++库]。
答案 1 :(得分:0)
首先,请注意vtable超出了C ++标准的范围,因为标准只涉及符合程序的行为,而不是实现如何实现。
所有已知的实现都使用vtable来实现虚函数和RTTI,但是在更多“微妙”特性中存在重要的细微差别,如多重继承,协变返回和虚拟基础。
在更简单的情况下,可以扩展基类的vtable,因为基类子对象与派生对象位于同一地址;基类被称为“主要基础”。
在更复杂的情况下,派生类具有多个多态基类,每个类都有自己的vptr。一个基类将是主要基类,派生类将扩展其vtable。对象的布局是这样的,其他基类子对象将在派生对象中处于非零偏移,因此当通过这种非主要的vtable调用虚函数时,this
指针将需要调整基础;该vtable中引用的函数需要一个指向基类子对象或完整对象的指针:
this
指针,他们需要为派生调整做一个基础。由于需要进行调整,通过非主要基地调用虚拟功能的效率略低。编译器更喜欢使用主vptr。
编译器将使用从其他基类继承的所有虚函数扩展主基的vtable。