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。
<二>classD 的記憶體模型
總結:與單繼承相同的是所有的虛函數都包含在虛函數表中,所不同的多重繼承有多個虛函數表,當子類對父類的虛函數有重寫時,子類的函數覆蓋父類的函數在對應的虛函數位置,當子類有新的虛函數時,這些虛函數被加在第一個虛函數表的後面
6.虛繼承,使公用的基類在子類中只有一份,我們看到虛繼承在多重繼承的基礎上多了vbtable來儲存到公用基類的位移。
<一>類圖的解釋:D2覆蓋了B的vB,GD覆蓋了D1的vD1同時也覆蓋B的vB,GD也有自己的虛函數vGD。
<二>class GD的記憶體模型
總結:虛繼承,使公用的基類在子類中只有一份,我們看到虛繼承在多重繼承的基礎上多了vbtable來儲存到公用基類的位移