【深度探索C++物件模型】第一章 關於對象

來源:互聯網
上載者:User

標籤:指標   south   c++   prot   rtu   val   luci   用途   複製   

第一章 關於對象(Object Lessons)—— 本書Stanley B.Lippman  一、前言    什麼是 C++ 物件模型:簡單的說。就是 C++ 中物件導向的底層實現機制。

    本書組織:    第 1 章,關於對象(Object Lessons),介紹 C++ 對象的基礎概念,給讀者一個粗略的瞭解。    第 2 章,建構函式語意學(The Semantics of Constructors),建構函式什麼時候會被編譯器合成?它給我們的程式效率帶來了如何的影響?    第 3 章。Data語意學(The Semantics of Data),討論 data members 的處理。    第 4 章,Function語意學(The Semantics of Function),討論類的各種成員函數,特別是 Virtual 。

    第 5 章,構造、析構、拷貝語意學(Semantics of Construction, Destruction, and Copy),探討怎樣支援 class 物件模型,以及 object 的生命週期。

    第 6 章,運行期語意學(Runtime Semantics),暫時對象的生與死,new 與 delete 的支援。

    第 7 章,在物件模型的頂端(On the Cusp of the Object Model),專註於 exception handling, template support, runtime type identification(RTTI)。    讀完此書,或者此系列blogs,會讓你對 C++ 的 class 有更深的瞭解。你將知道虛函數的實現方式,以及它所帶來的負擔。等等等等,這裡有你想知道關於 class 的一切。

        在 C 語言中,“資料”和“處理資料的操作(函數)”是分開來聲明的。也就是說,語言本身並沒有支援“資料和函數”之間的關聯性。

我們把這樣的程式方法稱為“程式性的”。比如。我們聲明一個 struct Point3d:

typedef struct _Point3d{    float x;    float y;    float z;} Point3d;
    欲列印一個 Point3D,我們可能須要這樣一個函數:
void Point3d_print( const Point3d* pd ){    printf("(%g, %g, %g)", pd->x, pd->y, pd->z);}//%g和%G是實數的輸出格式符號。它是自己主動選擇%f和%e兩種格式中較短的格式輸出。而且不輸出數字後面沒有意義的零。
    在 C++ 中,你可能會這樣來設計一個雙層或者三層的Point3D:
class Point {public:    Point( float x = 0.0 ) : _x(x) {}
    float x() { return _x; }    void x( float val ) { _x = val; }    // ...protected:    float _x;};
class Point2d : public Point{public:    Point2d( float x = 0.0, float y = 0.0 ) : Point( x ), _y( y ) {}    // ...protected:    float _y;}
class Point3d : public Point2d{public:    Point3d( float x = 0.0, float y = 0.0, float z = 0.0 ) : Point2d( x, y ), _z( z ) {}    // ...protected:    float _z;}
    從軟體project的眼光來看,物件導向的特徵。使得 C++ 比 C 看起來似乎更好。C 相對而言,更精瘦和簡易。C++ 看起來似乎更複雜,但並不意味著 C++ 不更有威力。    當一個 Point3d 轉換到 C++ 之後,第一個可能會問的問題是: 加上了封裝之後,布局成本添加了多少呢?答案是: class Point3d 並沒有添加成本。

三個 data members 直接內涵在每個 class Object 之中。而 成員函數(member functions)儘管在 class 的聲明之內,但卻不會出如今 class 的對象實體(Object)中。每個非 inline member function 僅僅會誕生一個函數實體。而 inline function。會在其每個使用者身上產生一個函數實體。

後面你將看到,C++ 在布局和存取時間上基本的負擔 是由 virtual 引起的。包含 虛函數 以及 虛基類。 二、C++ 的物件模型    首先,C++ 中。

    2種成員變數(class data members):靜態(static) 和 非靜態(non-static);    3種成員函數(class member functions):靜態、非靜態 和 虛擬(virtual)。
    我們來看這麼一個類:
class Point {public:    Point( float valx );    virtual ~Point();        float x() const;    static int PointCount();
protected:    virtual ostream& print( ostream &os ) const;
    float _x;    static int _point_count;};
    那這個 class Point 在機器中將會被怎麼表示呢?這有沒有引起你的求知慾?    【注】原書這裡介紹了  簡單物件模型 和  表格驅動的物件模型 。這裡跳過這兩個,直接看 C++ 物件模型。    在 C++ 物件模型中。     非靜態(non-static)成員變數 被配置於每個 class object 之內;     靜態(static)成員變數 則被存放在全部 class object 之外。也就是全域資料區。(問:假設是這樣。我們的 class 怎麼樣去全域資料區找到屬於它的 static 成員變數?別急,後面會有答案)。     靜態和非靜態成員函數,也被配置於 每個 class 的實體之外。     虛函數的配置方法是:         1. 每個 class 產生出一堆指向 virtual functions 的指標,並把這些指標放在表格之中。這個表。既是所謂的 虛函數表(virtual table), 或  vtbl;        2. 每個 class 的實體(object) 被加入了一個指標。指向 相關的(注意不一定是同一個) virtual table。

通常這個指標被稱為 vptr

vptr 的設定和重設都有每個 class 的 建構函式、解構函式、拷貝以及複製運算子。每個 class 所關聯的 type_info object( 用以支援 runtime type identification, RTTI )也經由 virtual table 被指出來,一般是放在表格的第一個 slot 處。


 三、C++ 怎樣支援多態    1. 經由一組隱含的轉化操作。比如。把一個 衍生類別 的指標轉化為一個指向其 public base type 的指標:        shape* ps = new circle();    2. 經由 virtual functions 機制:        ps->rotate();    3. 經由 dynamic_cast 和 typeid 運算子:        if ( circle *pc = dynamic_cast< circle* >(ps) )...    多態的主要用途。是經由一個共同的介面。來影響類型的封裝,我們一般會把這個介面定義在一個抽象基類裡面。然後再在衍生類別裡重寫這個介面。

四、須要多少記憶體來表現一個 class object?    猜想以下的代碼的 sizeof 結果會是?    .eg.1.

class Base{public:    Base();    ~Base();};// sizeof(Base) = ?
    .eg.2.
class Base{public:    Base();    ~Base();
protected:    double m_Double;    int m_Int;    char m_BaseName;};// sizeof(Base) = ?
    到底須要多少記憶體,才幹表現一個 class 的 object 呢?一般而言有:     1. 其 非靜態成員變數( non-static data members ) 的總和大小。     2. 加上不論什麼因為 記憶體對齊 的需求而填補上去的控制項。     3. 加上為了支援 virtual 而由內部產生的不論什麼額外的負擔。
    此外,須要注意的是。一個指標(或是一個 reference)。無論它僅僅想哪一種資料類型。指標本身所需記憶體大小是固定的。比方。在 win32下,一個指標的大小就是4個位元組(byte)。    問題的答案:
第一題: 答案是1。class Base 裡僅僅有建構函式和解構函式,由前面的內容所知,class 的 member functions 並不存放在 class 以及事實上例內,因此,sizeof(Base) = 1。是的,結構不是0,而是1,原因是由於,class 的不同執行個體,在記憶體中的地址各不同樣,一個位元組僅僅是作為預留位置,用來給不同的執行個體分配不同的記憶體位址的。第二題:答案是16。double 類型的變數佔用8個位元組。int 佔了4個位元組,char 僅僅佔一個位元組。但這裡它會按 int 進行對齊,Base 內部會填補3個位元組的記憶體大小。

最後的大小就是 8 + 4 + 1 + 3 = 16。

大家能夠調整三個成員變數的位置,看看結果會有什麼不同。

  五、指標的類型
Base* p_Base;int*    p_Int;vector<string> * p_vs;
    請問,一個指向 Base class 的指標和一個指向 int 的指標是怎樣產生不同的呢?    1. 以記憶體需求的觀點來說,沒有不同。在32位機器上,它們都須要4個位元組的記憶體空間。    2. “指向不同記憶體的各指標”間的差異。在於其所定址出來的 object 的類型不同。

    也就是說,“指標類型”會教導編譯器怎樣解釋某個特定地址中的記憶體內容及其涵蓋大小。

比方:一個指向 int 的指標,如果其地址是 1000。在32位及其上。將涵蓋地址空間 1000~1003.    那麼。一個指向地址 1000 的 void* 的指標,將涵蓋如何的地址空間呢?沒錯,我們並不知道。這就是為什麼一個類型為 void* 的指標。僅僅可以含有一個地址,而不可以通過它操作所指的 object 的緣故。    所以。轉型(cast)事實上是一種編譯器指令,它所做的,並非改變指標所含的真正地址,而是教導編譯器該去怎樣解釋指標所涵蓋的地址空間。 六、小結    第一章——關於對象。本章初步介紹了C++的物件模型是如何的,後面的章節將繼續討論這個物件模型的底層實現機制。    在讀完本篇文章之後。你應該理解:

  • 怎樣計算 sizeof(classA) 的大小;
  • 瞭解 class 的記憶體布局。    
    在下一章——建構函式語意學中。我們將瞭解關於類的建構函式的很多其它知識。

【深度探索C++物件模型】第一章 關於對象

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.