《深度探索c 物件模型》學習筆記

2021-07-12 05:22:57 字數 3864 閱讀 7664

封裝並未給c++帶來任何的空間或執行期的不良後果,c++在布局和訪問時間上的主要額外負擔由虛擬化引起。包括:

1)virtual function機制。用以支援乙個有效率的「執行期繫結」。

2)virtual base class機制。用以實現「多次出現在繼承體系中的base class,有乙個的單一而被共享的例項」。

虛擬繼承是多重繼承中特有的概念。虛擬基類是為解決多重繼承而出現的。

在虛擬繼承的情況下,基類不管在繼承串鏈中被派生多少次,永遠只會存在乙個例項。優勢主要體現在菱形繼承當中。虛擬繼承與普通繼承不同的是,虛擬繼承可以防止出現diamond繼承時,乙個派生類中同時出現了兩個基類的子物件。也就是說,為了保證這一點,在虛擬繼承情況下,基類子物件的布局是不同於普通繼承的。因此,它需要多出乙個指向基類子物件的指標

如圖所示菱形繼承,在iostream之中就只有虛基類ios的乙個例項!!!

1)經由一組隱式的轉化操作。例如把乙個派生類指標轉化為乙個指向其基類的指標(也就是基類指標操作子類物件):

shape *ps =new circle();
2)經由虛函式機制:

ps->rotate();
3)經由dynamic_cast和typeid運算子:

if(circle *pc = dynamic_cast(ps))...
在c++中,多型表示:以乙個基類指標或引用,定址出乙個派生類物件。多型的主要用途是經由乙個共享的介面來影響型別的封裝,共享介面以虛函式機制引發,在執行期根據物件的真正型別解析出到底是哪乙個函式例項被呼叫。

識別乙個類是否支援多型,就是看它是否有虛函式。vtbl中的虛函式一定在編譯期間獲知,其函式的個數、位置和位址是固定不變的,執行期間不能增、改、刪。

執行期三步完成虛函式呼叫:1)由vptr找到vtbl;2)定位vtbl中的slot(索引值);3)通過該索引下的值調函式。

c++新手的兩個誤解:

1)任何類如果沒有定義預設建構函式,就會被合成出來乙個。

2)編譯器合成出來的預設建構函式會顯式設定「類內每乙個資料成員的預設值」。

上述兩種說法都是錯誤的!

c++ 編譯器生成預設建構函式的四種情況:

1)類成員中有成員是類物件,並且該成員的類含有預設建構函式。那麼c++編譯器會給這個類也生成乙個預設建構函式,用來呼叫其成員物件的建構函式,完成該成員的初始化構造。如果這個成員的類沒有給出缺省建構函式,那麼c++編譯器也不會生成該類的預設建構函式。

2)這個類的基類有預設建構函式。那麼c++編譯器也會幫你生成該派生類的預設建構函式,以呼叫基類的預設建構函式,完成基類的初始化。如果基類沒有提供這個預設構造的函式,那麼編譯器也不會為派生類生成預設的建構函式(這裡包括兩層意思,第一,基類沒有任何形式建構函式;第二,基類存在其他形式的非預設建構函式,這種型別就是編譯不過的,道理很明顯)。

3)類中存在虛函式(新定義或繼承而得到)。那麼c++編譯器會為你生成預設建構函式,在編譯期生成虛表和虛表指標。

4)存在虛基類(有直接虛擬基類或繼承鏈上有虛基類)。那麼c++編譯器會為你生成預設建構函式,以初始化虛基類表(vbtable)。

這四種情況之外,且沒有宣告任何constructor的類,可以說它有無用的建構函式,但實際上它根本就不會被構建出來。

default constructors 和 copy constructors 在必要的時候才由編譯器產生出來。「必要」即意指當class不展現bitwise copy semantics時。

也就是,如果class中展現了位逐次拷貝,編譯器就不會產生出拷貝建構函式。當沒有產生拷貝構造的時候,我們的類怎麼產生呢。就是通過 bitwise copy 來搞定,也就是 將源類中成員變數中的每一位都逐次拷貝到目標類中,這樣我們的類就構造出來了。

類在下述四種情況下不展現位逐次拷貝(這時才會而且有必要生成拷貝構造):

1)類中含有成員類物件,並且此類物件含有預設建構函式。即:有「物件」成員,而非只有基本資料型別成員。

2)基類帶有拷貝建構函式。

3)類中存在虛函式(新定義或繼承而得到)。重新設定vptr,並且,如果該物件是第乙個物件的話還會構造vtbl。

4)存在虛基類(有直接虛擬基類或繼承鏈上有虛基類)

前兩種情況下,都是需要生成該類的拷貝構造去呼叫類成員物件或基類的拷貝建構函式。

後兩種情況下,需要編譯器來完成虛函式表(vbtl)的初始化和虛表指標(vptr)的初始化,所以如果沒有顯式的定義建構函式,需要編譯器構造預設的建構函式。

1)初始化引用型別成員;

2)初始化const成員;

3)基類建構函式有引數;

4)成員類物件的建構函式有引數。

初始化順序由宣告順序決定,而與初始化列表中的順序無關,因此編譯器會對初始化列表一一處理並有可能重新排序。

1、 簡單無多型的繼承。不會增加記憶體空間以及訪問上的額外負擔。

2、加上多型(有虛函式)後,會有額外時間空間開銷:

1)生成乙個和類有關的虛表,用來存放宣告的每乙個虛函式位址。表中元素個數一般而言是被宣告的虛函式個數,再加上乙個或兩個slots用以支援執行時型別識別(rtti)。

2)在每乙個類物件匯入乙個虛表指標,提供執行期鏈結,是每乙個物件找到相應的虛表。

3)加強建構函式,使它能夠為虛表指標設定初值,指向類的虛表。

4)加強析構函式,以處理vptr。

繼承關係如下:

那麼相應的資料布局為:

1)沒有this指標

2)不能直接訪問class中的非靜態成員;

3)不能被宣告為const、volatile或virtual;

4)可以不用物件訪問。

static成員函式,由於沒有this,所以可以作為callback函式的候選,或者作為執行緒的主函式。

建構函式的呼叫順序:從root到leaf,從left到right

1)虛基類的建構函式,從左到右,從最頂層到最底層。它和非虛基類不同:是由最底層子類呼叫的。

2)非虛基類的建構函式,按照基類被宣告順序從左到右呼叫。它與虛基類的不同:是由直接子類呼叫的。

3)如果類中有虛表指標,則設定vptr初值;若增加有新的虛函式或者覆蓋基類虛函式,則修改vtbl內的資訊。

4)成員變數以其宣告順序進行初始化構造。

5)建構函式中,使用者自定義的**(user code)最後被執行。

《深度探索C 物件模型》學習筆記

1.轉型其實是一種編譯器指令,大部分情況下它並不改變乙個指標所含的真正位址,它只影響 被指出之記憶體的大小和內容 的解釋方式.2.global objects的記憶體保證會在程式啟動的時候被清為0,local objects配置於程式的堆疊中,heap objects配置於自由空間中,都不一定會被清...

深度探索c 物件模型學習筆記之物件

在c 中,有兩種資料成員 靜態的和非靜態的,以及三種類成員函式 靜態的 非靜態的和虛函式。已知如下class point宣告 class point 考慮乙個問題,class point在機器中是怎麼表現的呢?簡單物件模型 簡單物件模型,是為了降低c 編譯器的設計複雜度,賠上了空間和執行器的效率而開...

《深度探索c 物件模型》學習筆記 附錄

1.關於整個書,我覺得如果能夠把各種類的 sizeof 搞定,就肯定 ok了。所以請參看另一篇文章 sizeof 大全 物件在記憶體中的布局 不考慮 padding 太複雜 只使用 int,並假設 sizeof int 4byte ide 是vc6.0 類 記憶體 類 記憶體 1 class a c...