1、c++的布局和存取時間成本。
封裝並未給c++帶來任何的空間或執行期的不良後果,c++在布局和存取時間上的主要額外負擔由虛擬化引起。包括:
1)virtualfunction機制。用以支援一個有效率的“執行期綁定”。
2)virtualbaseclass機制。用以實現“多次出現在繼承體系中的baseclass,有一個的單一而被共用的執行個體”。2、虛擬繼承
虛擬繼承是多重繼承中特有的概念。虛擬基類是為解決多重繼承而出現的。
在虛擬繼承的情況下,基類不管在繼承串鏈中被派生多少次,永遠只會存在一個執行個體。優勢主要體現在菱形繼承當中。虛擬繼承與普通繼承不同的是,虛擬繼承可以防止出現diamond繼承時,一個衍生類別中同時出現了兩個基類的子物件。也就是說,為了保證這一點,在虛擬繼承情況下,基類子物件的布局是不同於普通繼承的。因此,它需要多出一個指向基類子物件的指標。classiostream:publicistream,publicostream{...};classistream:virtualpublicios{...};classostream:virtualpublicios{...};
如圖所示菱形繼承,在iostream之中就只有虛基類ios的一個執行個體。。。
1)時間:繼承類對象訪問虛基類對象中的成員時通過間接引用完成,增加引用定址時間。
2)空間:由於共用所以不必要在對象記憶體中儲存多份虛基類子物件的拷貝,較之多繼承節省空間的。3、c++支援多態的方法
1)經由一組隱式的轉化操作。例如把一個衍生類別指標轉化為一個指向其基類的指標(也就是基類指標操作子類對象):shape*ps=newcircle();
2)經由虛函數機制:ps->rotate();
3)經由dynamic_cast和typeid運算子:if(circle*pc=dynamic_cast<circle*>(ps))...
在c++中,多態表示:以一個基類指標或引用,定址出一個衍生類別對象。多態的主要用途是經由一個共用的介面來影響類型的封裝,共用介面以虛函數機制引發,在執行期根據對象的真正類型解析出到底是哪一個函數執行個體被調用。
識別一個類是否支援多態,就是看它是否有虛函數。Vtbl中的虛函數一定在編譯期間獲知,其函數的個數、位置和地址是固定不變的,執行期間不能增、改、刪。
執行期三步完成虛函數調用:1)由vptr找到vtbl;2)定位vtbl中的slot(索引值);3)通過該索引下的值調函數。4、C++編譯器產生預設建構函式的四種情況
c++新手的兩個誤解:
1)任何類如果沒有定義預設建構函式,就會被合成出來一個。
2)編譯器合成出來的預設建構函式會顯式設定“類內每一個資料成員的預設值”。
上述兩種說法都是錯誤的。
C++編譯器產生預設建構函式的四種情況:
1)類成員中有成員是類對象,並且該成員的類含有預設建構函式。那麼C++編譯器會給這個類也產生一個預設建構函式,用來調用其成員對象的建構函式,完成該成員的初始化構造。如果這個成員的類沒有給出預設建構函式,那麼C++編譯器也不會產生該類的預設建構函式。
2)這個類的基類有預設建構函式。那麼C++編譯器也會幫你產生該衍生類別的預設建構函式,以調用基類的預設建構函式,完成基類的初始化。如果基類沒有提供這個預設構造的函數,那麼編譯器也不會為衍生類別產生預設的建構函式(這裡包括兩層意思,第一,基類沒有任何形式建構函式;第二,基類存在其他形式的非預設建構函式,這種類型就是編譯不過的,道理很明顯)。
3)類中存在虛函數(新定義或繼承而得到)。那麼C++編譯器會為你產生預設建構函式,在編譯期產生虛表和虛表指標。
4)存在虛基類(有直接虛擬基類或繼承鏈上有虛基類)。那麼C++編譯器會為你產生預設建構函式,以初始化虛基類表(vbtable)。
這四種情況之外,且沒有聲明任何constructor的類,可以說它有無用的建構函式,但實際上它根本就不會被構建出來。5、位逐次拷貝(bitwisecopysemantics)
Defaultconstructors和copyconstructors在必要的時候才由編譯器產生出來。“必要”即意指當class不展現bitwisecopysemantics時。
也就是,如果class中展現了位逐次拷貝,編譯器就不會產生出拷貝建構函式。當沒有產生拷貝構造的時候,我們的類怎麼產生呢。就是通過bitwisecopy來搞定,也就是將源類中成員變數中的每一位都逐次拷貝到目標類中,這樣我們的類就構造出來了。
類在下述四種情況下不展現位逐次拷貝(這時才會而且有必要產生拷貝構造):
1)類中含有成員類對象,並且此類對象含有預設建構函式。即:有“對象”成員,而非只有基礎資料型別 (Elementary Data Type)成員。
2)基類帶有拷貝建構函式。
3)類中存在虛函數(新定義或繼承而得到)。重新設定vptr,並且,如果該對象是第一個對象的話還會構造vtbl。
4)存在虛基類(有直接虛擬基類或繼承鏈上有虛基類)。
前兩種情況下,都是需要產生該類的拷貝構造去調用類成員對象或基類的拷貝建構函式。
後兩種情況下,需要編譯器來完成虛函數表(vbtl)的初始化和虛表指標(vptr)的初始化,所以如果沒有顯式的定義建構函式,需要編譯器構造預設的建構函式。6、必須使用成員初始化列表的情況
1)初始化參考型別成員;
2)初始化const成員;
3)基類建構函式有參數;
4)成員類對象的建構函式有參數。
初始化順序由聲明順序決定,而與初始化列表中的順序無關,因此編譯器會對初始化列表一一處理並有可能重新排序。7、繼承對類的資料成員的影響
1、簡單無多態的繼承。不會增加記憶體空間以及存取上的額外負擔。
2、加上多態(有虛函數)後,會有額外時間空間開銷:
1)產生一個和類有關的虛表,用來存放聲明的每一個虛函數地址。表中元素個數一般而言是被聲明的虛函數個數,再加上一個或兩個slots用以支援運行時類型識別(RTTI)。
2)在每一個類對象匯入一個虛表指標,提供執行期連結,是每一個對象找到相應的虛表。
3)加強建構函式,使它能夠為虛表指標設定初值,指向類的虛表。
4)加強解構函式,以處理vptr。8、多重繼承的資料布局
繼承關係如下:
那麼相應的資料布局為:
9、Static成員函數特性:
1)沒有this指標
2)不能直接存取class中的非靜態成員;
3)不能被聲明為const、volatile或virtual;
4)可以不用對象訪問。
Static成員函數,由於沒有this,所以可以作為callback函數的候選,或者作為線程的主函數。10、繼承體系下帶有資料成員的類的構造過程
建構函式的調用順序:從root到leaf,從left到right。
1)虛基類的建構函式,從左至右,從最頂層到最底層。它和非虛基類不同:是由最底層子類調用的。
2)非虛基類的建構函式,按照基類被聲明順序從左至右調用。它與虛基類的不同:是由直接子類調用的。
3)如果類中有虛表指標,則設定vptr初值;若增加有新的虛函數或者覆蓋基類虛函數,則修改vtbl內的資訊。
4)成員變數以其聲明順序進行初始化構造。
5)建構函式中,使用者自訂的代碼(usercode)最後被執行。