程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 深入理解C++對象模型,深入理解c對象模型

深入理解C++對象模型,深入理解c對象模型

編輯:C++入門知識

深入理解C++對象模型,深入理解c對象模型


  C++對象模型是比較重要的一個知識點,學習C++對象的內存模型,就可以明白C++中的多態原理、類的初始化順序問題、類的大小問題等。

1 C++對象模型基礎

1.1 C++對象中都有哪些東東

C++對象中包括以下內容:

  • 靜態常量
  • 成員變量
  • 成員函數
  • 虛函數
  • 純續函數
  • ...

以下是一個對象的定義:

class Base
{
    static int b_s;
public:
    void function() { }
    virtual void v_function() {
        cout << "Base v_function()" << endl;
    }
private:
    int b_a;
    int b_b;
};

1.2 一個C++對象有多大

  假如使用Base類作為測試,那Base類在內存中占用幾個字節呢?

window7 vs2013測試結果:

centos7 64位 vim測試結果:

  通過以上兩個測試結果圖分析可知,一個類的占用內存大小由以下成員決定:

  • 其非靜態成員的總和大小
  • 加上任何由於對齊的需求而填補(padding)上去的空間
    • 如果類中只有非靜態成員,比如char c; 則其大小為1。
    • 如果除了char c;外,還有int a; 則其大小為8
  • 加上為了支持virtual而由內部產生的額外負擔

1.3 C++中一些重要的語法糖

  • 靜態常量整數成員(double就不行)在class內部直接初始化
  • 靜態成員只能在類外初始化,且初始化時不加static
  • 基類夠構造函數中調用virtual函數實際調用的是基類中的virtual函數(這點和Java不同)
  • const成員函數:不修改類成員數據

 

2 C++對象內存布局

測試代碼如下:

#include <iostream>

using namespace std;

class Base
{
    static int b_s;
public:
    void function() { }
    virtual void v_function() {
        cout << "Base v_function()" << endl;
    }

    int b_a;
    int b_b;
};
int Base::b_s = 0;
int main(int argc, char **argv)
{
    Base base;

    base.b_a = 1;
    base.b_b = 2;
    cout << "size: " << sizeof(base) << endl;

    int *p = (int *) &base;
    cout << *p << endl;
    p++;
    cout << *p << endl;
    p++;
    cout << *p << endl;

    system("pause");
    return 0;
}

輸出結果為:

  輸出結果中的1和2為類中b_a和b_b成員的值,11459700表示一個地址,改地址包含有虛表的信息。Base類的大致內存布局如下:

2.1 一個空類大小為多少呢

class Empty { };

int main(int argc, char **argv)
{
    Empty empty;
    Empty emptys[10];

    cout << sizeof(empty) << endl;
    cout << sizeof(emptys) << endl;

    system("pause");
    return 0;
}

輸出結果為:

  空類中什麼都沒有,但是定義一個空類類型的變量(實例),每個實例在內存中都有一個獨一無二的地址,為了達到這個目的,編譯器往往會給一個空類隱含的加一個字節,這樣空類在實例化後在內存得到了獨一無二的地址。

2.2 C++對象繼承體系下的類初始化關系

(1)從太空角度看類初始化順序

  基類初始化 – 子類初始化

(2)從空中角度看類初始化順序

  基類靜態成員 – 子類靜態成員 – 基類成員變量 –基類構造函數 – 子類成員變量 – 子類構造函數

(3)站到地上看類初始化順序

  基類靜態成員 – 子類靜態成員 – (設置v_ptr/基類成員變量 ) –基類構造函數 – (設置v_ptr/子類成員變量) – 子類構造函數

 

3 C++類中的虛表

3.1 虛表指針及虛表結構

3.2 多重繼承下的虛表

  多重繼承中支持虛函數,其復雜度圍繞在第二個以及後繼的基類身上,以及“必須在執行期調整this指針”。

class Base1 {
public:
    Base1();
    virtual ~Base1();
    virtual void speakClearly();
    virtual Base1 *clone() const;
protected:
    float data_Base1;
};
class Base2 {
public:
    Base2();
    virtual ~Base2();
    virtual void number();
    virtual Base2 *clone() const;
protected:
    float data_Base2;
};
class Derived : public Base1, public Base2 {
public:
    Derived();
    virtual ~Derived();
    virtual Derived *clone() const;
protected:
    float data_Derived;
};

3.3 虛擬繼承下的虛表結構

  關於什麼是虛擬繼承請點擊:虛繼承-百度百科。

class Point2d {
public:
    Point2d(float = 0.0, float = 0.0);
    virtual ~Point2d();

    virtual void mumble();
    virtual float z();
    // ... other code
protected:
    float _x, _y;
};

class Point3d : virtual public Point2d {
public:
    Point3d(float = 0.0, float = 0.0, float = 0.0);
    ~Point3d();
    float z();
protected:
    float _z;
};

3.4 虛表指針什麼時候賦值

#include <iostream>

using namespace std;

class Base
{
public:
    Base() {
        cout << "Base()" << endl;
        show();
        int *p = &b;
        cout << "Base::b: " << p << endl;
        p--;
        cout << "Base::vptr: " << *p << endl;
        cout << "*Base::vptr: " << *(int *)*p << endl;
        cout << endl;
    }

    virtual void show() {
        cout << "Base::show()" << endl;
    }
public:
    int b;
};
class Derived : public Base
{
public:
    Derived()
    {
        cout << "Derived()" << endl;
        show();
        int *p = &b;
        cout << "Derived::b: " << p << endl;
        p--;
        cout << "Derived::vptr: " << *p << endl;
        cout << "*Derived::vptr: " << *(int *)*p << endl;
        cout << endl;
    }
    virtual void show() {
        cout << "Derived::show()" << endl;
    }
private:
    int d;
};

int main(int argc, char **argv)
{
    Base base;
    Derived derived;

    system("pause");
    return 0;
}

輸出結果為:

  從輸出結果中可以得出,子類在構造過程中虛表指針會被賦值2次。初始化如下:

基類靜態成員 – 子類靜態成員 – (設置v_ptr/基類成員變量 ) –基類構造函數 – (設置v_ptr/子類成員變量) – 子類構造函數

 

參考:

  1、《深度探索C++對象模型》

  2、c++空類實例大小不是0原因

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved