說(shuō)明:
? <u>不是很清楚的點(diǎn)</u>,用下劃線。
? 解答,用斜體;
? 重點(diǎn),用粗體加粗;
第二章 Data 語(yǔ)意學(xué)
3.2 Data Member 的布局
非靜態(tài)成員變量在class object中的排列順序?qū)⒑推渎暶鞯捻樞蛞粯拥?。但C++ standard允許編譯器將多個(gè)access sections之中的data members自由排列,不必在乎他們的出現(xiàn)在class中的聲明順序。
3.3 Data Member 的存取
存取代價(jià):
每一個(gè)member 的存取許可(private public protected),以及與class的關(guān)聯(lián),并不會(huì)導(dǎo)致任何空間上或執(zhí)行時(shí)間上的額外負(fù)擔(dān)——不論是在個(gè)別的class objects 或是在static data member 本身。
static data members:
靜態(tài)數(shù)據(jù)成員(static data members) 被視為global變量,只有一個(gè)實(shí)體,存放在程序的data segment(數(shù)據(jù)段)之中,每次取static member 就會(huì)被內(nèi)部轉(zhuǎn)化為對(duì)該唯一的extern 實(shí)體的直接參考操作。若取一個(gè)static data member的地址,會(huì)得到一個(gè)數(shù)據(jù)類型的指針,而不是只想起class member的指針。
nonstatic data members:
只要程序猿在一個(gè)成員函數(shù)中直接處理一個(gè)非靜態(tài)成員變量,“隱式類對(duì)象(this指針)”就會(huì)出現(xiàn)。
欲對(duì)一個(gè)nonstatic data member 進(jìn)行存取操作,編譯器需要把this指針(class object的起始地址)加上data member的偏移量。
非靜態(tài)成員變量的偏移量在編譯時(shí)期就可以獲知,即使這個(gè)成員變量是屬于被派生的類。因此存取效率高。
3.4 繼承 與 Data Member
class Point2d{
public:
//....
private:
float x,y;
}
class Point3d{
public:
//....
private:
float x,y,z;
}
討論上述結(jié)構(gòu),與 “提供繼承結(jié)構(gòu)” 有什么不同?
下面分四種情況討論。
1.只要繼承不要多態(tài)
具體繼承(相對(duì)于虛擬繼承)并不會(huì)增加空間或存儲(chǔ)時(shí)間上的額外負(fù)擔(dān)。這種情況base class和derived class的objects都是從相同的地址開始,其差異只在于derived object 比較大,用以容納自建的靜態(tài)數(shù)據(jù)成員,把一個(gè)derived class object指定給base class 的指針或引用(一定要通過(guò)指針或引用),并不需要編譯器去調(diào)?;蛐薷牡刂?,提供了最佳執(zhí)行效率。
2.加上多態(tài)
即在繼承關(guān)系中,提供一個(gè)虛函數(shù)接口。
多態(tài)帶來(lái)了程序彈性,但這種彈性會(huì)帶來(lái)空間和存取時(shí)間的額外負(fù)擔(dān):
- 導(dǎo)入一個(gè)虛函數(shù)表 ,用來(lái)存儲(chǔ)它所聲明的每一個(gè)virtual functions的地址。
- 在每一個(gè)class object中導(dǎo)入一個(gè)vptr,提供執(zhí)行期的鏈接,使每一個(gè)object能夠找到相應(yīng)的virtual table。
- 加強(qiáng)constructor,使它能夠?yàn)関ptr設(shè)定初始值,讓它指向class 所對(duì)應(yīng)的virtual table 。
- 加強(qiáng)destructor,使它能夠消抹“指向class 相關(guān)virtual table”的vptr。
3.多重繼承
class point2d{
public:
//....
protected:
float x,y;
}
class Vertex{
public:
//....
protected:
Vertex *next;
}
class Vertex3d: public point2d, public Vertex{
//point2d是第一個(gè)base class,Vertex是第二個(gè)
public:
//....
protected:
float mumble;
}
對(duì)于一個(gè)多重派生對(duì)象,將其地址指定給“最左端(第一個(gè))base class的指針”,情況和單一繼承時(shí)相同,因?yàn)槎叨贾赶蛄讼嗤钠鹗嫉刂?;至于第二個(gè)或后面的base class 的地址指定操作,則需要將地址修改過(guò):加上(<u>或減去,如果是downcast</u>)介于中間的base class subobject(s)的大小。
如果要存取第二個(gè)(或后面)的base class 中的一個(gè)data member ,會(huì)增加額外的成本嗎?不需要付出額外的成本,因?yàn)閙embers的位置在編譯時(shí)期就固定了,因此存取member只是一個(gè)簡(jiǎn)單的offset的運(yùn)算。
4.虛擬繼承
這一部分比較難,并且各個(gè)編譯器實(shí)現(xiàn)方式不同。
class如果含有一個(gè)或多個(gè) virtual base class subobjects 將被分割為兩部分:一個(gè)不變區(qū)域和一個(gè)共享區(qū)域。
- 不變區(qū)域中的數(shù)據(jù),不管后繼如何衍化,總是能有固定的offset,這部分可以被直接存??;
- 至于共享區(qū)域,所表現(xiàn)的就是virtual base class subobject ,這個(gè)部分?jǐn)?shù)據(jù),其位置因?yàn)槊看闻缮僮鞫凶兓?,所以只能間接存取。
總結(jié):
