c++對(duì)象內(nèi)存布局示例詳解
前言
了解你所使用的編程語言究竟是如何實(shí)現(xiàn)的,對(duì)于C++程序員可能特別有意義。首先,它可以去除我們對(duì)于所使用語言的神秘感,使我們不至于對(duì)于編譯器干的活感到完全不可思議;尤其重要的是,它使我們?cè)贒ebug和使用語言高級(jí)特性的時(shí)候,有更多的把握。當(dāng)需要提高代碼效率的時(shí)候,這些知識(shí)也能夠很好地幫助我們。
簡單非多態(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ù)成員存儲(chǔ)在堆棧中,且其聲明順序或者存儲(chǔ)順序的行為與編譯器強(qiáng)相關(guān)
- 所有其他方法(構(gòu)造函數(shù),析構(gòu)函數(shù)和編譯器擴(kuò)展代碼)都進(jìn)存儲(chǔ)在文本段。然后,這些方法將被調(diào)用并隱式地在調(diào)用對(duì)象的第一個(gè)參數(shù)中傳遞該指針。
this指針是一個(gè)隱含于每一個(gè)成員函數(shù)中的特殊指針。它是一個(gè)指向正在被該成員函數(shù)操作的對(duì)象,也就是要操作該成員函數(shù)的對(duì)象。this作用域是在類內(nèi)部,當(dāng)對(duì)一個(gè)對(duì)象調(diào)用成員函數(shù)時(shí),編譯程序先將對(duì)象的地址賦給this指針,編譯器會(huì)自動(dòng)將對(duì)象本身的地址作為一個(gè)隱含參數(shù)傳遞給函數(shù)。也就是說,即使你沒有寫this指針,編譯器在編譯的時(shí)候也是加上this的,它作為非靜態(tài)成員函數(shù)的隱含形參。被調(diào)用的成員函數(shù)函數(shù)體內(nèi)所有對(duì)類成員的訪問,都會(huì)被轉(zhuǎn)化為“this->類成員”的方式。
針對(duì)第二點(diǎn),我們類似于:
A x; x.printInt();
其中,X::printInt()這個(gè)行為,在編譯器中,將處理為
printInt(const X* this)
那么,x.printInt()調(diào)用處理將最終成為
printInt(&x);
同時(shí)具有虛函數(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ù)段中。使用范圍解析運(yùn)算符(即::)進(jìn)行的訪問。但是在編譯之后,就沒有像作用域和名稱空間那樣的東西了。因?yàn)?,它的名稱只是由編譯器執(zhí)行,所以所有內(nèi)容都由其絕對(duì)或相對(duì)地址引用。
- 靜態(tài)數(shù)據(jù)成員將空間放入內(nèi)存的數(shù)據(jù)段中。使用范圍解析運(yùn)算符(即::)進(jìn)行的訪問。
- 靜態(tài)方法進(jìn)入文本段,并通過作用域解析運(yùn)算符進(jìn)行調(diào)用。
- 對(duì)于virtual關(guān)鍵字,編譯器會(huì)自動(dòng)將指向虛擬表的指針(vptr)插入對(duì)象內(nèi)存表示中。通常,虛擬表是在數(shù)據(jù)段中為每個(gè)類靜態(tài)創(chuàng)建的,但它也取決于編譯器的實(shí)現(xiàn)。
- 在虛擬表中,第一個(gè)條目指向type_info對(duì)象,該對(duì)象包含與當(dāng)前基類和其他基類的DAG(有向無環(huán)圖)相關(guān)的信息(如果從這些基類派生的信息)。
繼承對(duì)象的內(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ù)成員類是派生類的子對(duì)象。
- 編譯器會(huì)在類的構(gòu)造函數(shù)中生成具有所有重寫的虛擬功能和為_vptr分配虛擬表的代碼的虛擬表。
具有多重繼承和虛擬功能的對(duì)象的內(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)的虛擬表進(jìn)行調(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對(duì)象內(nèi)類Y的子對(duì)象。
結(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 | | |------------------|
- 具有一個(gè)或多個(gè)虛擬基類的派生類的內(nèi)存表示形式分為兩個(gè)區(qū)域:不變區(qū)域和共享區(qū)域。
- 不變區(qū)域內(nèi)的數(shù)據(jù)與對(duì)象的起始位置保持固定的偏移量,而與后續(xù)派生無關(guān)。
- 共享區(qū)域包含虛擬基類,并且隨后續(xù)派生和派生順序而波動(dòng)。
總結(jié)
了解內(nèi)存布局,對(duì)我們的項(xiàng)目開發(fā)會(huì)提供很大的便利,比如對(duì)coredump的調(diào)試
到此這篇關(guān)于c++對(duì)象內(nèi)存布局的文章就介紹到這了,更多相關(guān)c++對(duì)象內(nèi)存布局內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解析C++編程中如何使用設(shè)計(jì)模式中的狀態(tài)模式結(jié)構(gòu)
這篇文章主要介紹了如何在C++編程中適用設(shè)計(jì)模式中的狀態(tài)模式結(jié)構(gòu),狀態(tài)模式強(qiáng)調(diào)將特定狀態(tài)相關(guān)的邏輯分散到一些類的狀態(tài)類中,需要的朋友可以參考下2016-03-03C/C++中不同數(shù)據(jù)類型之間的轉(zhuǎn)換詳解
這篇文章主要介紹了C/C++中不同數(shù)據(jù)類型之間的轉(zhuǎn)換詳解,數(shù)據(jù)類型轉(zhuǎn)換是計(jì)算機(jī)編程中常見的操作,用于將一個(gè)數(shù)據(jù)類型轉(zhuǎn)換為另一個(gè)數(shù)據(jù)類型,本文將對(duì)不同數(shù)據(jù)類型之間的轉(zhuǎn)換作出說明,需要的朋友可以參考下2023-10-10java 中ArrayList與LinkedList性能比較
這篇文章主要介紹了java 中ArrayList與LinkedList性能比較的相關(guān)資料,需要的朋友可以參考下2017-03-03C語言實(shí)現(xiàn)各種排序算法實(shí)例代碼(選擇,冒泡,插入,歸并,希爾,快排,堆排序,計(jì)數(shù))
排序算法是算法之中相對(duì)基礎(chǔ)的,也是各門語言的必學(xué)的算法,這篇文章主要介紹了C語言實(shí)現(xiàn)各種排序算法(選擇,冒泡,插入,歸并,希爾,快排,堆排序,計(jì)數(shù))的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2021-10-10