1. 程式人生 > 其它 >深度探索C++物件模型筆記--第一章

深度探索C++物件模型筆記--第一章

第一章. 關於物件

1.0 加上封裝後的佈局成本

  1. 一個類的成員函式不包含在obj中, 非行內函數只產生一個例項, 行內函數則是在被使用到的模組中產生例項.

  2. 虛擬函式 : 用於支援動態繫結.

  3. 虛基類 : 用於支援 在一個繼承體系中多次出現的基類有一個單一且被共享的例項.

1.1 C++物件模型

  1. 非靜態成員變數置於每一個類物件中.

  2. 靜態成員變數, 靜態 / 非靜態成員函式置於類物件外.

  3. 類中的每一個虛擬函式都有一個對應的指標, 並且這些指標置於一張表格中, 這張表格被稱為virtual table ( 虛表 / vtbl), 此外, 每一個類物件所關聯的type_info(用於支援RTTI)也被放置於vtbl中, 通常位於表格的首位.

  4. C++記憶體區域 : 堆, 棧, 靜態儲存段, 常量段, 程式段.

  5. 每一個類物件中會生成一個指向該類中vtbl的指標 virtual pointer ( 虛指標 / vptr), vptr的設定和重置都由類的 構造,析構,拷貝賦值 運算來自動完成.

  6. 對於這種模型, 如果應用程式程式碼本身未曾改變, 但所用到的類物件的非靜態成員變數有變動 (增加, 移除 或 更改), 那麼應用程式程式碼則需要重新編譯.

  7. 虛繼承 : 無論基類在繼承鏈中被派生多少次, 永遠只會存在一個例項 ( 子物件 / subobject ).

1.2 關鍵詞所帶來的差異

  1. 對於C++程式碼來說, 何時應該選用struct, 何時選用class? ( 當你覺得用struct更舒服的時候 ).

  2. 由於相容C程式碼, 在下述情況下, 應該選用struct而不是class, C++中凡處於同一個訪問域的資料, 是必定能保證以其宣告順序出現在記憶體佈局中的, 但被放置在多個訪問域中的各個變數, 其在記憶體佈局中排列的順序是不確定的.

    struct mumble
    {
    /*stuff*/;
    char pc[ 1 ]; //用來獲取變長字串
    }

    const char *ss = "HelloWorld";

    //code
    struct mumble *pmumb1 = ( struct mumble* )malloc( sizeof( struct mumble ) + strlen( ss ) + 1);
    strcpy( pmumb1->pc, ss );
  3. 把C和C++結合在一起的唯一可行方法是組合 : 當要傳遞一個複雜的類物件到一個C函式中的時候, struct可以將資料封裝起來, 並保證擁有與C相容的空間佈局.

1.3 物件所帶來的差異

  1. 只有通過指標或引用來處理繼承體系中的基類物件, 才支援面向物件模型程式設計所需的多型性質.

  2. C++中用於支援多型的方法: (1) 隱式型別轉換; (2) 虛擬函式; (3) dynamic_cast和typeid運算子.

  3. 多型的主要用途是經由一個共同的介面來影響型別的封裝, 介面通常被定義在一個抽象的基類中.

  4. 一般而言, 定義一個類物件的記憶體大小需要: (1) 非靜態成員變數記憶體大小總和; (2) 位元組對齊填充的pad; (3) 編譯器為了支援virtual而新增的內容.

  5. 不管指標指向哪一種資料型別, 其本身所需要的記憶體大小是固定的.

  6. 指標所指的資料型別, 會指導編譯器如何解釋某個特定地址中的記憶體內容及其大小.

  7. 型別轉換(cast)可以理解為一種編譯器指令, 大部分情況下它並不改變指標所含的真正地址, 而是影響 所指記憶體的大小和其內容 的解釋方式.

  8. 一個指向派生類物件的基類指標只能直接處理基類中的成員變數, 唯一能處理派生類成員的方法是通過virtual機制.

  9. 一個指標的型別將在編譯期間決定該指標固定可用的介面和該介面的訪問等級(public, protected, private).

  10. 編譯器必須確保如果一個物件中含有一個及以上的vptr, 這些vptr的內容不會被基類物件 初始化/修改.

  11. 指標或引用由於多型所造成的改變並不包括所該型別(對編譯器而言)的記憶體大小, 改變的是所指記憶體的大小和內容的解釋方式.

  12. 當一個基類物件被直接初始化為一個派生類物件時, 派生類物件就會被切割(sliced)以塞入較小的基類記憶體中.