[讀書筆記] - 《深度探索C++對象模型》第1章 關於對象

1.C++在佈局以及存取時間上主要的額外負擔是由virtual引起,包括:

1>virtual function機制:用以支持一個有效率的“執行期綁定”(runtime binding).

2>virtual base class: 用以實現“多次出現在繼承體系中的“base class, 有一個單一而被共享的實體”。

 

2.在C++中,有兩種class data members: static和nonstatic,以及三種class member functions: static、nonstatic和virtual。

 

3.C++對象模型。在此模型中,Nonstatic data members被置於每一個class object之內,static data members則被存放在所有的class object之外。Static和non static function members也被放在所有的class object之外。Virtual functions則以兩個步驟支持之:

1>每一個class產生出一堆指向virtual functions的指針,放在表格之中。這個表格被稱爲virtual table(vtbl)。

2>每一個class object被添加了一個指針,指向相關的virtual table。通常這個指針被稱爲vptr。vptr的設定(setting)和重置(resetting)都由每一個class的constructor、destructor和copy assignment運算符自動完成。每一個class所關聯的type_info object(用以支持runtime type identificatio,RTTI)也經由virtual table被指出來,通常是放在表格的第一個slot處。

class Point
{
public:
    Point(flot xval);
    virtual ~Point();

    float x() const;
    static int PointCount();

protected:
    virtual ostream& print(ostream& os) const;
    float _x;
    static int _point_count;
};

 

4.虛擬繼承。

class iostream:
    public istream,
    public ostream
{...};

class istream: virtual public ios {...};
class ostream: virtual public ios {...};

在虛擬繼承的情況下,base class不管在繼承串鏈中被派生(derived)多少次,永遠只會存在一個實體(稱爲subobject)。例如iostream之中就只有virtual ios base class的一個實體。

 

5.C++中凡處於同一個access section的數據,必定保證以其聲明次序出現在內存佈局當中。然後被放置在多個access sections中的各筆數據,排列次序就不一定了。

 

6.C++以下列方法支持多態:

1>經由一組隱含的轉化操作。例如把一個derived class指針轉化爲一個指向public base type的指針:

shape *ps = new circle();

2>經由virtual function機制:

ps->rotate();

3>經由dynamic_casttypeid運算符:

if ( circle* pc = dynamic_cast<circle*>(ps) ) 
    ...

 

7.需要多少內存才能夠表現一個class object? 一般而言要有:

1>其nonstatic data members的總和大小;

2>加上任何由於alignment的需求而填補(padding)上去的空間(可能存在於members之間,也可能存在於集合體邊界);

3>加上爲了支持virtual而由內部產生的任何額外負擔(overhead)。

 

8.指針的類型。一個指向ZooAnimal的指針是如何地與一個指向整數的指針或一個指向template Array(如下,與一個String一併產生)的指針有所不同呢?

ZooAnimal* px;
int* pi;
Array<String>* pta;

以內存需求的觀點來說,沒有什麼不同。它們三個都需要有足夠的內存來放置一個機器地址(通常是個word)。“指向不同類型之各指針”間的差異,既不在其指針表示法不同,也不在其內容(代表一個地址)不同,而是在其所尋址出來的object類型不同。也就是說,“指針類型”會教導編譯器如何解釋某個特定地址中的內存內容及其大小:

1>一個指向地址1000的整數指針,在32位機器上,將涵蓋地址空間1000~1003;

2>如果String是傳統的8-bytes(包括一個4-bytes的字符指針和一個用來表示字符串長度的整數),那麼一個ZooAnimal指針將橫跨地址空間1000~1015(4+8+4)

那麼,一個指向地址1000而類型爲void*的指針,將涵蓋怎樣的地址空間呢?是的,我們不知道!這就是爲什麼一個類型爲void*的指針只能夠含有一個地址,而不能夠通過它操作所指之object的緣故。

所以,轉型(cast)其實是一種編譯器指令。大部分情況下它並不改變一個指針所含的真正的地址,它隻影響“被指出之內存的大小和其內容”的解釋方式。

 

9.加上多態之後

class Bear: public ZooAnimal
{
public:
    Bear();
    ~Bear();
    // ...
    void rotate();
    virtual void dance();
    // ...
protected:
    enum Dances { ... };
    Dances dances_known;
    int cell_block;
};

Bear b("Yogi");
Bear* pb = &b;
Bear& rb = *pb;

假設Bear object放在地址1000處,一個Bear指針和一個ZooAnimal指針有什麼不同?

Bear b;
ZooAnimal* pz = &b;
Bear* pb = &b;

它們每個都指向Bear object的第一個byte,其間的差別是,pb所涵蓋的地址包含整個Bear object,而pz所涵蓋的地址只包含Bear object中的ZooAnimal subobject。除了ZooAnimal subject中出現過的members,你不能夠使用pz來直接處理Bear的任何members,唯一例外是通過virtual機制:

 

10.總而言之,多態是一種威力強大的設計機制,允許你繼一個抽象的public接口之後,封裝相關的類型。需要付出的代價就是額外的間接性——不論是在“內存的獲得”或是在“類型的決斷”上。C++通過class的pointers和references來支持多態,這種程序設計風格就成爲“面向對象”。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章