溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

c++對象的內(nèi)存布局是怎樣的

發(fā)布時間:2021-10-25 13:29:41 來源:億速云 閱讀:149 作者:iii 欄目:開發(fā)技術(shù)

這篇文章主要講解了“c++對象的內(nèi)存布局是怎樣的”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“c++對象的內(nèi)存布局是怎樣的”吧!

簡單非多態(tài)的內(nèi)存布局

class X {
    int     x;
    float   xx;

public:
    X() {}
    ~X() {}

    void printInt() {}
    void printFloat() {}
};
  |                        |          
      |------------------------| <------ X class object memory layout
      |        int X::x        |
      |------------------------|  stack segment
      |       float X::xx      |       |   
      |------------------------|       |
      |                        |      \|/
      |                        |    
      |                        |
------|------------------------|----------------
      |         X::X()         | 
      |------------------------|       |   
      |        X::~X()         |       |
      |------------------------|      \|/
      |      X::printInt()     |  text segment
      |------------------------|
      |     X::printFloat()    |
      |------------------------|
      |                        |

在本示例中

  • 只有數(shù)據(jù)成員存儲在堆棧中,且其聲明順序或者存儲順序的行為與編譯器強相關(guān)

  • 所有其他方法(構(gòu)造函數(shù),析構(gòu)函數(shù)和編譯器擴展代碼)都進存儲在文本段。然后,這些方法將被調(diào)用并隱式地在調(diào)用對象的第一個參數(shù)中傳遞該指針。

this指針是一個隱含于每一個成員函數(shù)中的特殊指針。它是一個指向正在被該成員函數(shù)操作的對象,也就是要操作該成員函數(shù)的對象。this作用域是在類內(nèi)部,當對一個對象調(diào)用成員函數(shù)時,編譯程序先將對象的地址賦給this指針,編譯器會自動將對象本身的地址作為一個隱含參數(shù)傳遞給函數(shù)。也就是說,即使你沒有寫this指針,編譯器在編譯的時候也是加上this的,它作為非靜態(tài)成員函數(shù)的隱含形參。被調(diào)用的成員函數(shù)函數(shù)體內(nèi)所有對類成員的訪問,都會被轉(zhuǎn)化為“this->類成員”的方式。

針對第二點,我們類似于:

A x;
x.printInt();

其中,X::printInt()這個行為,在編譯器中,將處理為

printInt(const X* this)

那么,x.printInt()調(diào)用處理將最終成為

printInt(&x);

同時具有虛函數(shù)和靜態(tài)數(shù)據(jù)成員的內(nèi)存布局

class X {
    int         x;
    float       xx;
    static int  count;

public:
    X() {}
    virtual ~X() {}

    virtual void printAll() {}
    void printInt() {}
    void printFloat() {}
    static void printCount() {}
};

其內(nèi)存布局如下

  |                        |          
      |------------------------| <------ X class object memory layout
      |        int X::x        |
stack |------------------------|
  |   |       float X::xx      |                      
  |   |------------------------|      |-------|--------------------------|
  |   |         X::_vptr       |------|       |       type_info X        |
 \|/  |------------------------|              |--------------------------|
      |           o            |              |    address of X::~X()    |
      |           o            |              |--------------------------|
      |           o            |              | address of X::printAll() |
      |                        |              |--------------------------|
      |                        |
------|------------------------|------------
      |  static int X::count   |      /|\
      |------------------------|       |
      |           o            |  data segment           
      |           o            |       |
      |                        |      \|/
------|------------------------|------------
      |        X::X()          | 
      |------------------------|       |   
      |        X::~X()         |       |
      |------------------------|       | 
      |      X::printAll()     |      \|/ 
      |------------------------|  text segment
      |      X::printInt()     |
      |------------------------|
      |     X::printFloat()    |
      |------------------------|
      | static X::printCount() |
      |------------------------|
      |                        |
  • 所有非靜態(tài)數(shù)據(jù)成員都按照聲明的順序?qū)⒖臻g放入堆棧中,與前面的示例順序相同。

  • 靜態(tài)數(shù)據(jù)成員將空間放入內(nèi)存的數(shù)據(jù)段中。使用范圍解析運算符(即::)進行的訪問。但是在編譯之后,就沒有像作用域和名稱空間那樣的東西了。因為,它的名稱只是由編譯器執(zhí)行,所以所有內(nèi)容都由其絕對或相對地址引用。

  • 靜態(tài)數(shù)據(jù)成員將空間放入內(nèi)存的數(shù)據(jù)段中。使用范圍解析運算符(即::)進行的訪問。

  • 靜態(tài)方法進入文本段,并通過作用域解析運算符進行調(diào)用。

  • 對于virtual關(guān)鍵字,編譯器會自動將指向虛擬表的指針(vptr)插入對象內(nèi)存表示中。通常,虛擬表是在數(shù)據(jù)段中為每個類靜態(tài)創(chuàng)建的,但它也取決于編譯器的實現(xiàn)。

  • 在虛擬表中,第一個條目指向type_info對象,該對象包含與當前基類和其他基類的DAG(有向無環(huán)圖)相關(guān)的信息(如果從這些基類派生的信息)。

繼承對象的內(nèi)存布局

class X {
    int     x;
    string str;

public:
    X() {}
    virtual ~X() {}

    virtual void printAll() {}
};

class Y : public X {
    int     y;

public:
    Y() {}
    ~Y() {}

    void printAll() {}
};

其內(nèi)存布局信息如下

  |                              |          
      |------------------------------| <------ Y class object memory layout
      |          int X::x            |
stack |------------------------------|
  |   |              int string::len |
  |   |string X::str ----------------|
  |   |            char* string::str |         
 \|/  |------------------------------|      |-------|--------------------------|
      |           X::_vptr           |------|       |       type_info Y        |
      |------------------------------|              |--------------------------|
      |          int Y::y            |              |    address of Y::~Y()    |
      |------------------------------|              |--------------------------|
      |               o              |              | address of Y::printAll() |
      |               o              |              |--------------------------|
      |               o              |              
------|------------------------------|--------
      |           X::X()             | 
      |------------------------------|       |   
      |           X::~X()            |       |
      |------------------------------|       | 
      |         X::printAll()        |      \|/ 
      |------------------------------|  text segment
      |           Y::Y()             |
      |------------------------------|
      |           Y::~Y()            |
      |------------------------------|
      |         Y::printAll()        |
      |------------------------------|
      |      string::string()        |
      |------------------------------|
      |      string::~string()       |
      |------------------------------|
      |      string::length()        |
      |------------------------------|
      |               o              |
      |               o              |
      |               o              |
      |                              |
  • 在繼承模型中,基類和數(shù)據(jù)成員類是派生類的子對象。

  • 編譯器會在類的構(gòu)造函數(shù)中生成具有所有重寫的虛擬功能和為_vptr分配虛擬表的代碼的虛擬表。

具有多重繼承和虛擬功能的對象的內(nèi)存布局

class X {
public:
    int     x;
    
    virtual ~X() {}
    virtual void printX() {}
};

class Y {
public:
    int     y;
    
    virtual ~Y() {}
    virtual void printY() {}
};

class Z : public X, public Y {
public:
    int     z;
    
    ~Z() {}
    void printX() {}
    void printY() {}
    void printZ() {}
};

內(nèi)存布局如下

  |                              |          
      |------------------------------| <------ Z class object memory layout
stack |          int X::x            |         
  |   |------------------------------|                  |--------------------------|      
  |   |          X:: _vptr           |----------------->|       type_info Z        |
  |   |------------------------------|                  |--------------------------|
 \|/  |          int Y::y            |                  |    address of Z::~Z()    |
      |------------------------------|                  |--------------------------|
      |          Y:: _vptr           |------|           |   address of Z::printX() |
      |------------------------------|      |           |--------------------------|
      |          int Z::z            |      |           |--------GUARD_AREA--------|    
      |------------------------------|      |           |--------------------------|
      |              o               |      |---------->|       type_info Z        |
      |              o               |                  |--------------------------|
      |              o               |                  |    address of Z::~Z()    |
      |                              |                  |--------------------------|
------|------------------------------|---------         |   address of Z::printY() |
      |           X::~X()            |       |          |--------------------------|  
      |------------------------------|       |          
      |          X::printX()         |       |        
      |------------------------------|       |         
      |           Y::~Y()            |      \|/        
      |------------------------------|  text segment
      |          Y::printY()         |                
      |------------------------------|                
      |           Z::~Z()            |                
      |------------------------------|                
      |          Z::printX()         |                
      |------------------------------|                
      |          Z::printY()         |                
      |------------------------------|                
      |          Z::printZ()         |                
      |------------------------------|                
      |               o              |                
      |               o              |                
      |                              |

在多繼承層次結(jié)構(gòu)中,創(chuàng)建的虛擬表指針(vptr)的確切數(shù)目將為N-1,其中N代表類的數(shù)目。

如果嘗試使用任何基類指針調(diào)用Z類的方法,則它將使用相應(yīng)的虛擬表進行調(diào)用。如下例子所示:

Y *y_ptr = new Z;
y_ptr->printY(); // OK
y_ptr->printZ(); // Not OK, as virtual table of class Y doesn't have address of printZ() method

在上面的代碼中,y_ptr將指向完整Z對象內(nèi)類Y的子對象。

結(jié)果,調(diào)用任何方法,例如使用y_ptr-> printY()。 使用y_ptr的解析方式如下:

 ( *y_ptr->_vtbl[ 2 ] )( y_ptr )

虛繼承內(nèi)存布局

class X { int x; };
class Y : public virtual X { int y; };
class Z : public virtual X { int z; };
class A : public Y, public Z { int a; };

其布局如下:

  |                |          
 Y class  ------> |----------------| <------ A class object memory layout
sub-object        |   Y::y         |          
                  |----------------|             |------------------| 
                  |   Y::_vptr_Y   |------|      |    offset of X   | // offset(20) starts from Y 
 Z class  ------> |----------------|      |----> |------------------|     
sub-object        |   Z::z         |             |       .....      |     
                  |----------------|             |------------------|  
                  |   Z::_vptr_Z   |------|       
                  |----------------|      |        
 A sub-object --> |   A::a         |      |      |------------------| 
                  |----------------|      |      |    offset of X   | // offset(12) starts from Z
 X class -------> |   X::x         |      |----> |------------------|          
 shared           |----------------|             |       .....      |           
 sub-object       |                |             |------------------|
  • 具有一個或多個虛擬基類的派生類的內(nèi)存表示形式分為兩個區(qū)域:不變區(qū)域和共享區(qū)域。

  • 不變區(qū)域內(nèi)的數(shù)據(jù)與對象的起始位置保持固定的偏移量,而與后續(xù)派生無關(guān)。

  • 共享區(qū)域包含虛擬基類,并且隨后續(xù)派生和派生順序而波動。

感謝各位的閱讀,以上就是“c++對象的內(nèi)存布局是怎樣的”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對c++對象的內(nèi)存布局是怎樣的這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關(guān)知識點的文章,歡迎關(guān)注!

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

c++
AI