C++對象模型是比較重要的一個知識點,學習C++對象的內存模型,就可以明白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;
};
假如使用Base類作為測試,那Base類在內存中占用幾個字節呢?
window7 vs2013測試結果:
centos7 64位 vim測試結果:
通過以上兩個測試結果圖分析可知,一個類的占用內存大小由以下成員決定:
測試代碼如下:
#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類的大致內存布局如下:
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;
}
輸出結果為:
空類中什麼都沒有,但是定義一個空類類型的變量(實例),每個實例在內存中都有一個獨一無二的地址,為了達到這個目的,編譯器往往會給一個空類隱含的加一個字節,這樣空類在實例化後在內存得到了獨一無二的地址。
(1)從太空角度看類初始化順序
基類初始化 – 子類初始化
(2)從空中角度看類初始化順序
基類靜態成員 – 子類靜態成員 – 基類成員變量 –基類構造函數 – 子類成員變量 – 子類構造函數
(3)站到地上看類初始化順序
基類靜態成員 – 子類靜態成員 – (設置v_ptr/基類成員變量 ) –基類構造函數 – (設置v_ptr/子類成員變量) – 子類構造函數
多重繼承中支持虛函數,其復雜度圍繞在第二個以及後繼的基類身上,以及“必須在執行期調整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;
};
關於什麼是虛擬繼承請點擊:虛繼承-百度百科。
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;
};
#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原因