1. 程式人生 > >《深度探索C++物件模型》讀書筆記第一章:關於物件

《深度探索C++物件模型》讀書筆記第一章:關於物件

有點尷尬,這個筆記已經記過一遍了。重新來過的原因是我讀到第五章左右覺得自己對這本書認識不夠深刻,暫時就沒有記筆記。今天正式把書通讀了一遍了,然後現在讀第二遍,這一遍我每章都會寫一篇部落格總結,只總結重點,精要,所以會簡短一些。我會以條款的形式來總結。

  1. 從C到C++,使用 class 封裝資料一般並不會增加成本。資料成員就像在 C struct 之中一樣,而成員函式不在 class 內部,不管 inline 或是 non-inline 都只會誕生一個函式實體。C++在佈局及存取時間上的主要負擔由 virtual 引起,即virtual function 機制以及 virtual base class
    。此外還有多重繼承下,發生在一個派生類和其第二或後繼的基類之間的轉換
  2. C++物件模型:簡單說就是非靜態資料成員放在每個 class 的內部,靜態資料成員、靜態或非靜態函式放在類的外部;虛擬函式機制的支援是在每個 class 內部維護一個 vptr 指向 vtbl,vtbl 存放所有虛擬函式指標。 vptr 的設定和重置都由每一個 class 的建構函式、解構函式和拷貝構造/賦值函式自動完成。每一個 class 的 typeinfo 也放在 vtbl 之中(用來支援 RTTI),vtbl 中還有 virtual base class  subobject 的 offset 值。
  3. C++中儘量不要對 class 使用柔性陣列,因為如果該 class 有多個訪問許可權,或者從另一個 class 派生而來,或者定義有多個 virtual function,可能不能順利轉化。C++中處於同一訪問許可權的資料必然依宣告次序出現在記憶體佈局中,放置在不同訪問許可權中資料的先後就不一定了;同樣的道理,基類和派生類資料成員的佈局以及 vptr 的位置也為該伎倆打上一個問號(實際上目前編譯器 vptr 一般位於頭部,基類資料在派生類資料成員之前,所以後兩點視編譯器而定吧)。所以要避免使用柔性陣列,避免覆蓋。
  4. C++中多型僅存在一個個的 public class 體系中。Nonpublic 的派生行為(非公有繼承實際上可以通過 reinterpret_cast 強轉派生類指標為基類指標實現多型)和 void* 指標可以說是多型,但它們沒有被語言明確支援,必須由程式設計師通過轉型操作來管理(這裡不談編譯時多型,如函式過載,模板技術等)。
  5. 主要有三種方法支援多型:(1)基類使用引用或指標接收派生類。(2)可呼叫虛擬函式觸發多型。(3)經由 dynamic_cast 和 tyepid 運算子。
  6. class 的大小由三點決定:(1)其非靜態資料成員的大小。(2)記憶體對齊。(3)為支援 virtual 產生的額外負擔(無非是 vptr 和 virtual base class suboject)。
  7. 當一個 基類物件被直接初始化為(或是被指定為)一個派生類物件時,派生類物件就會被切割,僅剩基類型別大小的記憶體。多型不再呈現,而一個嚴格的編譯器可以在編譯時期解析一個 ”通過該物件而觸發的虛擬函式“ 呼叫操作(編譯器夠嚴格,解析出發生了切割,就不採用多型了),因而迴避 virtual 機制。如果虛擬函式被定義為 inline,此時一個非虛擬函式+ inline 導致效率更高。(不過可惜,可能你想要的多型因為誤用失敗了)。
總結:         總而言之,多型是一種威力強大的設計機制,允許通過將派生類的指標賦給基類指標(或基類持有派生類引用),讓基類可以根據當前賦值給它的子類的特性以不同的方式運作。需要付出的代價就是額外的間接性,包括記憶體的獲得(虛表等)和型別的決斷(RTTI)兩方面。         C++不僅支援 OO(object-oriented,面向物件,也支援 OB(object-based,基於物件)。一個基於物件的設計可能比一個面向物件設計速度更快而且空間更緊湊。速度快是因為所有的函式引發操作都在編譯期解析完成,物件建構起來不需要設定 virtual 機制;空間緊湊則是因為每一個類物件不需要 負擔傳統上為了支援 virtual 機制而需要的額外負擔。不過,OB 設計比較沒有彈性。 第一章就先到這裡了,這只是第一章,這本書的價值,可見一斑。