DATA 語義學
這段代碼輸出什么?
#include <iostream>
using namespace std;
class A{ public:A(){ac='s';}private:char ac;};
class B:virtual public A{public:B(){a='e';}char a; };
class C:virtual public A{ };
class D:public B,public C
{
public:
D():A(),B(),C(){b=13;}
int b;
};
int main(){
D d;
cout<<"sizeof(A)="<<sizeof(A)<<endl;
cout<<"sizeof(B)="<<sizeof(B)<<endl;
cout<<"sizeof(C)="<<sizeof(C)<<endl;
cout<<"sizeof(D)="<<sizeof(D)<<endl;
cout<<endl;
cout<<"address of A's subobject in d"<<(A*)&d<<endl;
cout<<"address of B's subobject in d"<<(B*)&d<<endl;
cout<<"address of C's subobject in d"<<(C*)&d<<endl;
cout<<"address of D's subobject in d"<<(D*)&d<<endl;
cout<<endl;
int* p = (int*)(*((int*)&d));
cout<<"address of b's virtual base table="<<p<<endl;
cout<<"first member in b's virtual base table="<<*p<<endl;
cout<<"second member in b's virtual base table="<<*(p+1)<<endl;
cout<<"third member in b's virtual base table="<<*(p+2)<<endl;
cout<<endl;
p= (int*)*((int*)((C*)&d));
cout<<"address of c's virtual base class table= "<<p<<endl;
cout<<"first member in c's virtual base table="<< *p<<endl;
cout<<"second member in c's virtual base table="<<*(p+1)<<endl;
cout<<"third member in c's virtual base table="<<*(p+2)<<endl;
char *pchar= (char*)(&d)+4; //char型加4 注意A中的ac其實是私有變量,B不應該可以訪問到的,實際上通過強制轉換可以非法觸及她-。-
cout<<*pchar<<endl;
cout<<*(pchar+12)<<endl;
B b;
int *pint =(int*)(&b)+1; //int型+1
cout<<*((char*)(pint))<<endl;
pint = (int*)(&d)+3;
cout<<*(pint)<<endl;
}
結果是:
sizeof(A)=1
sizeof(B)=9
sizeof(C)=5
sizeof(D)=17
address of A's subobject in d0012FF74
address of B's subobject in d0012FF64
address of C's subobject in d0012FF6C
address of D's subobject in d0012FF64
address of b's virtual base table=00403350
first member in b's virtual base table=0
second member in b's virtual base table=16
third member in b's virtual base table=0
address of c's virtual base class table= 00403358
first member in c's virtual base table=0
second member in c's virtual base table=8
third member in c's virtual base table=0
e
s
e
13
1.語言本身造成的負擔:如virtual baseclass
2.對齊造成的負擔(對齊會單獨開題討論)
3.編譯器優化處理 A雖然是空的 but 為了讓A的兩個object在內存中得到不同的地址,編譯器給他加上了一個byte,但是B和C都沒有這一個byte呢?這是編譯器做了優化,允許省掉這一個byte
看上面的代碼”:
環境是vs2008 對齊設置為4字節
A的大小為1 因為有一個char 沒有對齊因為不是結構型對象,如果A沒有這個char大小依然是1的
B的大小為9 首先開始是它的virtual base class ptr,4個字節的指針,然后是他自己的member 1個char 此時為了保證其對象完整性編譯器對齊到4字節處也就是在第九個字節內放入基類的member
這樣如果我們把A=B B賦給A,傳輸可以從整4字節開始割出A即可
C的大小是5 沒什么好解釋
D的大小是17首先是B的8字節(4字節vbptr+1字節char member+3字節對齊)然后是C的4字節vbptr,然后是自己的member4字節最后是1字節的base class member,可以看到B和C的base class table中的項都是自己位置與這個member的offset 值
不同編譯器可能結果不同的,因為c++ standard 并沒有強制規定 base class subobjects的順序等
data member 是程序執行過程中的某種狀態:
static data member 是整個class 的狀態
non-static data member 是個別class-object 的狀態
c++對象盡量以空間優化和存取速度的考慮來表現non-static members ,并且和c的struct 數據配置的兼容性。
static data member 放在程序的一個global data segment 中,不會影響個別的class-object 大小
,在class沒有object 時已經存在,但是template中有些不同
-----DATA member 的綁定
始終把nested type 聲明 放在class 起始處,argument list 中的名稱會在第一次遇到時候被適當的決議完成,因此extern 和nested type names 之間的非直覺綁定操作還是會發生。
---- DATA 的存取
Point3d origin,*pt=&origin;
origin.x = 0.0; 和 pt->x=0.0 ; 有什么區別么??
如果x是靜態data member 則完全沒有區別 因為他們都在data segment 中和object無關
~nonstatic data member---------
如果point3d不包含虛擬繼承那么沒有差異
否則我們不能確定pt中必然指向哪一種因此不能在編譯器確定offset需要一些運行時候的計算抉擇,而origin則不同一定是某一個類型的所以沒有問題
多繼承或者單繼承都不會帶來訪問上的影響,因為他們都可以向c的結構體那樣在編譯時期確定各個member的offset。即使是多繼承pt指向第二個baseclass的data,由于member的位置在編譯時期就已經固定了,因此存取member只是一個offset運算,像單一繼承那樣簡單,不管是指針,reference或者object都一樣
只有virtual base class 會帶來一些損耗,因為他使得對象模型變得復雜了
如果我們在一個類中加入了virtual func 會發生什么~~~
1. 會產生一個virtual table,存放每一個virtual func地址以及rtti支持的type_info
2.class object 內都加入一個vptr,提供執行期的鏈接
3.加強ctor 設定vpr初值
4.加強dtor 消除vptr 從dirived class 到 base class
虛擬繼承:
他必須支持某種形式的“shared subobject”繼承
那么一個object會分成一個不變局部和一個共享局部的數據,共享局部就是virtual base class subobject,他的位置會因為每次派生操作發生變化(例如一個virtual base class subobject的位置在不同級別的繼承體系中的位置是不確定的,不像多繼承單繼承那樣有固定的offset),所以他只能間接存取,因此產生了效率缺損.(間接是指的是他只能首先讀出他的指針,然后根據指針的內容取到他)
所以在虛擬繼承基類中最好不要有data member
-------指向DATAmember 的指針
#include <iostream>
using namespace std;
class A
{
public:
int a;
};
int main(){
int A::* p=&A::a;
cout<<p<<endl;
}
輸出 1
因為為了防止&A::a和 int A::*a = 0;一樣把他加了1。
虛擬繼承帶來的主要沖擊是,妨礙了優化的有效性,因為每一層虛擬繼承都帶來了一個額外層次的間接性,在編譯器中存取 類似point::x的操作pb.*bx
會被轉化為 &pb->vbcPoint+bx
而不是轉換成 &pB +bx
額外的間接性會降低“把所有操作都搬移到緩存器中執行”優化能力,也就是降低了局部性~