深度探索c++物件模型(一)_關於對象

來源:互聯網
上載者:User

原部落格地址:http://www.roading.org//develop/cpp/c對象面面觀.html

學習C++應該看過不少關於C與C++的口水貼,以及關於各種對比C與C++效率比較的文章, 最有影響力的恐怕當屬linus對C++的炮轟——《糟糕程式員的垃圾語言》。但無論如 何,我正喜歡著這樣一種垃圾,我當然對linus充滿敬意,但這不妨礙我口食垃圾而對其 仰慕。

無需太在意站在山巔的巨人們的言論,每個人都有不同的道路來追求真理。與其聽著Linus 的嗤笑之聲,不妨跟隨Lippman一起探索C++物件模型的“內心世界”。若要對事物褒貶,總 得先對其瞭解。唯有瞭解才能有負責的發聲。

——僅以此區區百餘字為之前記。

C++的額外成本

C++較之C的最大區別,無疑在於物件導向。類相較於C的struct不僅只包含了資料,同時還 包括了對於資料的操作。在語言層面上C++帶來了很多物件導向的新特性,類、繼承、多態 等等。新特性使得C++更加強大,但同時卻伴隨著空間布局和存取時間的額外成本。作為一 個以效率為目標的語言,C++對於物件導向的實現,其實不大,這些額外成本主要由 virtual引起,包括: virtual function 機制,用來支援“執行期綁定”。 virtual base class ——虛基類機制,以實現共用虛基類的 subobject。

除此之外C++沒有太多理由比C遲緩。

三種物件模型

C++類包含兩種資料成員:待用資料成員和非待用資料成員;同時包含成員函數,靜態函數 和虛函數三種成員函數,這些機制在C++對象是如何被表現的。下面有三種模型可以用以表 現它們——簡單物件模型、表格驅動物件模型以及C++物件模型。也許你沒興趣去瞭解有幾種 方式可以實現C++的物件模型,只想瞭解C++物件模型。然則,C++物件模型是在前兩種對象 模型上發展而來的,甚至於局部上直接用到前兩種物件模型。

假定有一個Point類,我們將用三種物件模型來表現它。Point類如下:

class Point  
public:  
    Point( float xval ); 
    virtual ~Point();      
    float x() const;  
    static int PointCount();
protected:  
    virtual ostream&  print( ostream &os ) const;
    float _x;
    static int _point_count;  
};
簡單物件模型

簡單物件模型:一個C++Object Storage Service了所有指向成員的指標,而成員本身不儲存在對象中。也 就是說不論資料成員還是成員函數,也不論這個是普通成員函數還是虛函數,它們都儲存 在對象本身之外,同時對象儲存指向它們的指標。


簡單物件模型對於編譯器來說雖然極盡簡單,但同時付出的代價是空間和執行期的效率.顯而 易見的是對於每一個成員都要額外搭上一個指標大小的空間以及對於每成員的操作都增加了 一個間接層。因此C++並沒有採用這樣一種物件模型,但是被用到了C++中“指向成員的指標” 的概念當中。

表格驅動物件模型


表格驅動模型則更絕,它將對象中所有的成員都抽離出來在外建表,而對象本身只儲存指向 這個表的指標。右圖可以看到,它將所有的資料成員抽離出來建成資料成員表,將所有的函 數抽取出來建成一張函數成員表,而對象本身只保持一個指向資料成員表的指標。

侯大大認為,在對象與成員函數表中間應當加一個虛箭頭,他認為這是Lippman的疏漏之處, 應當在對象中儲存指向函數成員表的指標。

然而我在這兒還是保留原書(而非譯本)的截圖,因為以我之拙見,不儲存指向成員函數表 的指標也沒有妨礙。因為形如float Point::x()的成員函數實際上相當於float x(Point*) 類型的普通函數,因此儲存指向成員函數表的指標當屬多此一舉。

當然C++也沒有採用這一種物件模型,但C++卻以此模型作為支援虛函數的方案。

C++物件模型

所有的非待用資料成員儲存在對象本身中。所有的待用資料成員、成員函數(包括靜態與非 靜態)都置於對象之外。另外,用一張虛函數表(virtual table)儲存所有指向虛函數的指 針,並在表頭附加上一個該類的type_info對象,在對象中則儲存一個指向虛函數表的指 針。如下圖:


class和struct

按照lippman的意思是,struct僅僅是給想學習C++的C程式員攀上高峰少一點折磨。但遺憾的 是當我開始學C++的時候這個問題給我帶來更多的疑惑。以我的認識class與struct僅限一個 預設的許可權(後者為public前者為private)的不同。有時我甚至覺得只有一點畸形,他們不 應當如此的相像,我甚至認為struct不應該被擴充,僅僅儲存它在C中的原意就好了。1

一個有意思的C技巧(但別在C++中使用)

在C中將一個一個元素的數組放在struct的末尾,可以令每個struct的對象擁有可變數組。 看代碼:

struct mumble { 

   /* stuff */ 

   char pc[ 1 ]; 

}; 

// grab a string from file or standardinput 

// allocate memory both for struct &string 

struct mumble *pmumb1 = ( struct mumble* ) 

   malloc(sizeof(struct mumble)+strlen(string)+1); 

strcpy( &mumble.pc, string );

這是一個很有意思的小技巧,但是別在C++中使用。因為C++的記憶體布局相對複雜。例如被繼 承,有虛函數… 問題將不可避免的發生。

三種編程典範 程式模型 ADT模型 物件導向模型

純粹使用一種典範編程,有莫大的好處,如果混雜多種典範編程有可能帶來意想不到的後果 ,例如將繼承類的對象賦值給基類對象,而妄想實現多態,便是一種ADT模型和物件導向模型 混合編程帶來嚴重後果的例子。

一個類的對象的記憶體大小包括: 所有非待用資料成員的大小。 由記憶體對齊而填補的記憶體大小。 為了支援virtual有內部產生的額外負擔。

如下類:

class ZooAnimal { 

public: 

   ZooAnimal(); 

   virtual ~ZooAnimal(); 

   virtual void rotate(); 

protected: 

   int loc; 

   String name; 

};

在32位電腦上所佔記憶體為16位元組:int四位元組,String8位元組(一個表示長度的整形,一個 指向字串的指標),以及一個指向虛函數表的指標vptr。對於繼承類則為基類的記憶體大小 加上本身資料成員的大小。在cfront中其記憶體布局如下圖:

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.