《深入探索C++物件模型》讀後總結
第一章:關於物件
1:C++每個類物件的記憶體大小由:類中非靜態成員記憶體的和,指向虛擬函式表的指標和記憶體對齊的記憶體三者共同決定。
記憶體如何對齊,我以前寫過一篇博文裡講的很詳細。
2:虛擬函式表裡存放的是虛擬函式的記憶體地址,物件呼叫虛擬函式就是通過虛擬函式表找到它的地址從而呼叫它,這也是多型的實現原理。
3:struct和class除了預設的許可權不同外,其它基本一樣;在只使用資料成員時用struct
第二章:建構函式語音學
C++的編譯器,在你沒有定義建構函式的情況下,會自動生成一個預設建構函式,但這個建構函式可能是trivial(無用的)即相當於沒有生成;也可能是nontrivial(有用的),有用的預設建構函式才是真正的被編譯器合成出來,有用的建構函式也不是會顯示設定所有成員的預設值。
有以下四種情況合成的預設構成是有用的,也就是真正合成了的
- 類中含有一個類物件,而且這個類物件含有預設構成函式,因為這樣編譯會自動合成預設建構函式來呼叫這個類物件的構成函式,就是有用的了
- 含有虛擬函式,合成的預設建構函式會產生虛擬函式表來存放所有虛擬函式的地址和指向這個虛擬函式表的指標
- 繼承自一個有預設建構函式的基類,合成預設建構函式裡要呼叫基類的預設建構函式去初始化基類部分
- 繼承自一個虛基類,要初始化虛基類表和指向虛基類表的指標,虛基類表裡放的是虛基類裡成員的偏移。
編譯器還會自動合成copy constructor,編譯自動合成的拷貝建構函式是位逐次拷貝的,若類中含有指標或引用時,位逐次拷貝可能不安全。自動合成沒有位逐次拷貝的情況也是上面說的那四處情況。
程式轉化,編譯器會把我們寫的程式轉化它自己的認識的方式來執行。比如引數初始化,返回值初始化等,其中重要的是NRV優化,即不用拷貝建構函式,就是把返回值放入形參的引用中。
若不想使用系統合成的,可以宣告一個拷貝建構函式但不定義,把它設為私有。
第三章:Data語音學
這章主要講的是類物件的記憶體佈局
1.每一個類物件的記憶體由非靜態成員,記憶體對齊和虛指標組成。整個物件的存放位置是呢是根據它在程式中的位置而定,其中的靜態成員存放在靜態儲存區,類似於靜態資料成員不屬於物件而屬於類了,非虛擬函式呢類似全域性函式,不過它有一個隱式的第一個引數this指標,指向呼叫它的物件。
2.在繼承的過程中,非靜態的資料都會繼承到子類中,複雜的是虛擬函式表的變化和指向虛擬函式的指標。
- 在單一的普通繼承中派生類的虛擬函式表會繼承基類的虛擬函式表並且會新增子類虛擬函式的地址,若重寫了,則子類虛擬函式的地址會覆蓋掉虛擬函式表中被重寫的函式地址。這樣在呼叫虛擬函式通過虛擬函式表時會產生多型。在這種情況下繼承後派生類只有一個虛擬函式表
- 在多繼承中,虛擬函式會有多個虛擬函式表,派生類自己的虛擬函式的地址會新增在繼承序列的最左邊那個類的虛擬函式表中
- 在虛繼承中,若派生類的虛擬函式完全重寫了則只有一個虛擬函式表,否則會有多個。
第四章:函式語意學
主要說的就是虛擬函式
在繼承過程中子類的虛擬函式表會有三種變化:
1.從基類中拷貝一份虛擬函式表
2:在虛擬函式表中新增一個自己的slot
3:在虛擬函式表中佔有基類的slot,即重寫。
第五章:構造、析構、拷貝語意學
1:構造,初始化類的物件,在單一繼承中很好理解,就是先初始化基類,再初始化派生類,依次推下去,但在虛擬繼承中,會導致重複的構造虛基類,解決方法:在每個派生類有條件的構造虛基類,若條件為false則不構造虛基類。
2:析構的順序跟構造相反,一層一層的析構,析構完派生類,剩下基類,繼續析構基類
3:拷貝過程中也會遇到虛基類多次重複拷貝,沒有什麼太好的解決方法,只有儘量抑制虛基類的拷貝或虛基類中不要有成員
第六章:執行期間語意學
1:在物件剛開始宣告時編譯內部會呼叫它的建構函式,在結束時會呼叫它的解構函式
2:全域性物件和區域性物件它們的析構時期不一樣
3:new是從堆區開闢一個記憶體空間,並返回那個空間的指標,要和delete配套使用,使用的過程中,注意異常安全。
4:臨時性物件的產生和銷燬
第七章:站在物件模型的尖端
1:模板,當類或函式中有模板時,編譯會很少的發現錯誤,因為它的型別檢查等其它檢查在模板例項化時才開始
2:模板類的成員函式,只有在用到時才會被例項化
3:模板中名稱決議法:當會模板例項化無關時,用模板宣告區域的名稱;當與模板例項化有關時,用模板例項區域的名字
4:異常處理的三步,try throw catch