1. 程式人生 > >C++對象的內存分布和虛函數表

C++對象的內存分布和虛函數表

關鍵字 lin 指針 ostream 提取 發生 布局 派生 重載

c++中一個類中無非有四種成員:靜態數據成員和非靜態數據成員,靜態函數和非靜態函數。

1.非靜態數據成員被放在每一個對象體內作為對象專有的數據成員。
2.靜態數據成員被提取出來放在程序的靜態數據區內,為該類所有對象共享,因此只存在一份。
3.靜態和非靜態成員函數最終都被提取出來放在程序的代碼段中並為該類所有對象共享,因此每一個成員函數也只能存在一份代碼實體。在c++中類的成員函數都是保存在靜態存儲區中的 ,那靜態函數也是保存在靜態存儲區中的,他們都是在類中保存同一個憊份。
因此,構成對象本身的只有數據,任何成員函數都不隸屬於任何一個對象,非靜態成員函數與對象的關系就是綁定,綁定的中介就是this指針

。成員函數為該類所有對象共享,不僅是處於簡化語言實現、節省存儲的目的,而且是為了使同類對象有一致的行為。同類對象的行為雖然一致,但是操作不同的數據成員。

#include"iostream.h"
class A
{
private:
 int x;
 int y;
public:
 void sety()
 {
  y=5;
 }
 void print()
 {
  cout<<"x="<<x<<endl<<"y="<<y<<endl;
 }
};
void main()
{
  A a;
  a.sety();
  a.print();
  
int *p=(int *)&a;//對象a的內存模型裏面,只有非靜態數據成員,所以是合理的。 *p=6; a.print(); }

4.單繼承的對象的內存布局,第一個為函數指針vtbl,其後為成員且先基類後子類,虛函數表裏包含了所有的虛函數的地址,以NULL結束。虛函數如果子類有重寫,就由子類的重新的代替

技術分享圖片

<一>上述類圖的解釋:B的v2覆蓋了A的v2,C的v1覆蓋了A的v1,C的v3覆蓋了B的v3.

註意:發生覆蓋的特征有:

1) 不同的範圍(分別位於派生類與基類);

2) 函數名字相同

3) 參數相同

4) 基類函數必須

有virtual關鍵字

成員函數被重載的特征
(1)相同的範圍(在同一個類中);
(2)函數名字相同;
(3)參數不同;
(4)virtual 關鍵字可有可無
隱藏”是指派生類的函數屏蔽了與其同名的基類函數,規則如下
(1)如果派生類的函數與基類的函數同名,但是參數不同。此時,不論有無virtual關鍵字,基類的函數將被隱藏(註意別與重載混淆)。
(2)如果派生類的函數與基類的函數同名,並且參數也相同,但是基類函數沒有virtual 關鍵字。此時,基類的函數被隱藏(註意別與覆蓋混淆)

<二>類C的對象的內存模型為:

技術分享圖片

單繼承的對象的布局,第一個為虛函數表指針vtbl,其後為成員且先基類後子類,虛函數表裏包含了所有的虛函數的地址,以NULL結束。虛函數如果子類有重寫,就由子類的重新的代替。
5.與單繼承相同的是所有的虛函數都包含在虛函數表中,所不同的多重繼承有多個虛函數表,當子類對父類的虛函數有重寫時,子類的函數替換父類的函數在對應的虛函數位置,當子類有新的虛函數時,這些虛函數被加在第一個虛函數表的後面

技術分享圖片

<一>對類圖的解釋:D的v3覆蓋了B3的v3,另外D有一個新的虛函數vD。

<二>class D 的內存模型

技術分享圖片

總結:與單繼承相同的是所有的虛函數都包含在虛函數表中,所不同的多重繼承有多個虛函數表,當子類對父類的虛函數有重寫時,子類的函數覆蓋父類的函數在對應的虛函數位置,當子類有新的虛函數時,這些虛函數被加在第一個虛函數表的後面

6.虛繼承,使公共的基類在子類中只有一份,我們看到虛繼承在多重繼承的基礎上多了vbtable來存儲到公共基類的偏移。

技術分享圖片

<一>類圖的解釋:D2覆蓋了B的vB,GD覆蓋了D1的vD1同時也覆蓋B的vB,GD也有自己的虛函數vGD。

<二> class GD的內存模型

技術分享圖片

總結:虛繼承,使公共的基類在子類中只有一份,我們看到虛繼承在多重繼承的基礎上多了vbtable來存儲到公共基類的偏移

C++對象的內存分布和虛函數表