深入理解虛表之非虛擬繼承及虛擬繼承

非虛擬繼承

帶虛函數的類

class Base
{
public:
virtual void FunTest1()
{
cout<<"Base::FunTest1()"<<endl;
}
virtual void FunTest2()
{
cout<<"Base::FunTest2()"<<endl;
}
int _data1;
};

int main()
{
Base b;
b._data1 = 0x01;
return 0;
}

 

Base類沒有顯式定義自己的構造函數,此時編譯器會和成默認的構造函數,

 

 

合成的構造函數中主要完成在對象頭4個字節中填寫虛表地址:

 

Base類對象最後的模型如下:

 

注意:同一個類的對象共用同一個虛表

Base b1, b2, b3;

 

從上述的結果中可以得到印證。

 

 

【單繼承(派生類中沒有虛函數覆蓋)

class Base

{
public:
virtual void FunTest1()
{cout<<"Base::FunTest1()"<<endl;} 
virtual void FunTest2()
{cout<<"Base::FunTest2()"<<endl;}
           int _data1;
};

class Derive:public Base
{
public:
virtual void FunTest3()
{cout<<"Derive::FunTest3()"<<endl;}
virtual void FunTest4()
{cout<<"Derive::FunTest4()"<<endl;}
 int _data2;
};

//打印虛表

typedef void (*VtbFun)();
void PrintVtable()
{
cout<<"Derive類的虛函數表:"<<endl;
Derive d1;
d1._data1 = 0x01;
d1._data2 = 0x02;
int* pVTable = (int*)*(int*)&d1;
VtbFun FunTest = (VtbFun)*pVTable;
while(NULL != FunTest)
{
FunTest();
cout<<(int*)FunTest<<endl;
pVTable += 1;
FunTest = (VtbFun)*pVTable;
}
cout<<"虛表結束:"<<endl;
}

int main()
{
Base b1;
Derive d1;
return 0;
}

 

按照如上分析的順序,探索下單繼承下派生類對象模型以及虛表

首先看看編譯器爲派生類合成的缺省構造函數:

 

 

派生類構造函數中進行了如下事情:

 

 

Derive d1;

d1._data1 = 0x01;

d1._data2 = 0x02;

派生類最後的對象模型爲:

 

 

【單繼承(派生類中有虛函數覆蓋)

class Base

{
public:
virtual void FunTest1()
{
cout<<"Base::FunTest1()"<<endl;
}
virtual void FunTest2()
{
cout<<"Base::FunTest2()"<<endl;
}
int _data1;
};

class Derive:public Base
{
public:
virtual void FunTest1()
{
cout<<"Derive::FunTest1()"<<endl;
}
virtual void FunTest3()
{
cout<<"Derive::FunTest3()"<<endl;
}
virtual void FunTest4()
{
cout<<"Derive::FunTest4()"<<endl;
}
int _data2;
};

int main()
{
PrintVtable();
return 0;
}

 

派生類對象模型及虛表建議規則:

 

 

【多繼承(派生類不覆蓋基類虛函數)

class Base

{
public:
virtual void FunTest1()
{
cout<<"Base::FunTest1()"<<endl;
}
virtual void FunTest2()
{
cout<<"Base::FunTest2()"<<endl;
}
int _data1;
};

class Base1
{
public:

virtual void FunTest3()
{
cout<<"Base1::FunTest3()"<<endl;
}
virtual void FunTest4()
{
cout<<"Base1::FunTest4()"<<endl;
}
int _data2;
};

class Derive:public Base, public Base1
{
public:
virtual void FunTest5()
{
cout<<"Derive::FunTest5()"<<endl;
}
int _data3;
};

int main()
{
cout<<"sizeof(Derive) = "<<sizeof(Derive)<<endl;
Derive d;
d._data1 = 0x01;
d._data2 = 0x02;
d._data3 = 0x03;
PrintVtable();
return 0;
}

 

同樣:看看編譯器合成的派生類的對象做了什麼工作

 

 

觀察下派生類的對象模型和虛表的建立過程

 

 

從上面的結果可以看出,Derive類自己特有的虛函數直接添加在Base類對應虛函數表最後的位置,大家可將BaseBase1的順序交換驗證下。

 

【多繼承(派生類覆蓋基類虛函數)

class Base

{
public:
virtual void FunTest1()
{
cout<<"Base::FunTest1()"<<endl;
}
virtual void FunTest2()
{
cout<<"Base::FunTest2()"<<endl;
}
int _data1;
};

class Base1
{
public:
virtual void FunTest3()
{
cout<<"Base1::FunTest3()"<<endl;
}
virtual void FunTest4()
{
cout<<"Base1::FunTest4()"<<endl;
}
int _data2;
};
//這次將繼承列表中Base和Base1的位置互換
class Derive:public Base1, public Base

{
public:
virtual void FunTest1()
{
cout<<"Derive::FunTest1()"<<endl;
}
virtual void FunTest3()
{
cout<<"Derive::FunTest3()"<<endl;
}
virtual void FunTest5()
{
cout<<"Derive::FunTest5()"<<endl;
}
int _data3;
};

int main()
{
           PrintVtable();
return 0;
}

 

此時派生類的對象模型和虛表的結構:

 

 

虛擬繼承

//沒有虛函數覆蓋,但派生類有自己的虛函數

class Base

{
public:
virtual void FunTest1()
{
cout<<"Base::FunTest1()"<<endl;
}
virtual void FunTest2()
{
cout<<"Base::FunTest2()"<<endl;
}
int _data1;
};

class Derive:virtual public Base
{
public:
virtual void FunTest3()
{
cout<<"Derive::FunTest3()"<<endl;
}
virtual void FunTest4()
{
cout<<"Derive::FunTest4()"<<endl;
}
int _data2;
};

 

虛擬繼承編譯器爲派生類合成的默認構造函數分析

 

編譯器爲派生類合成的默認構造函數任務分析:

虛擬繼承派生類對象模型分析

說明:使用環境VS2010,不同版本編譯器可能會有差異

     說明:使用環境VS2010,不同版本編譯器可能會有差異


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章