C++對(duì)象的內(nèi)存結(jié)構(gòu)的實(shí)現(xiàn)
在C++中,對(duì)象的內(nèi)存結(jié)構(gòu)并非由語言標(biāo)準(zhǔn)強(qiáng)制規(guī)定具體細(xì)節(jié)(不同編譯器可能有細(xì)微差異),但遵循一些通用規(guī)則。其內(nèi)存布局主要受成員變量、虛函數(shù)、繼承關(guān)系等因素影響,核心是為了高效存儲(chǔ)數(shù)據(jù)和支持面向?qū)ο蟮暮诵奶匦裕ㄈ绶庋b、繼承、多態(tài))。
一、基礎(chǔ)情況:無繼承、無虛函數(shù)的對(duì)象
對(duì)于一個(gè)僅包含非靜態(tài)成員變量的簡單類,對(duì)象的內(nèi)存結(jié)構(gòu)本質(zhì)上是成員變量的有序集合,不包含成員函數(shù)(成員函數(shù)存放在代碼區(qū),被所有對(duì)象共享)。
示例:
class Person {
private:
int age; // 4字節(jié)
char gender; // 1字節(jié)
double height; // 8字節(jié)
public:
void print() {} // 成員函數(shù),不占對(duì)象內(nèi)存
};
內(nèi)存結(jié)構(gòu):
對(duì)象的內(nèi)存中僅包含 age、gender、height 三個(gè)成員變量,按聲明順序排列,但會(huì)受到內(nèi)存對(duì)齊影響(編譯器為提高訪問效率,會(huì)在成員間插入填充字節(jié))。
假設(shè)按8字節(jié)對(duì)齊(常見編譯器默認(rèn)),內(nèi)存布局可能為:
偏移量 內(nèi)容
0-3 age(4字節(jié))
4-7 填充(4字節(jié),因gender僅1字節(jié),需對(duì)齊到下一個(gè)8字節(jié)邊界)
8-8 gender(1字節(jié))
9-15 填充(7字節(jié),為height的8字節(jié)對(duì)齊)
16-23 height(8字節(jié))
總大小:24字節(jié)(而非4+1+8=13字節(jié),填充字節(jié)是為了讓CPU更高效地訪問多字節(jié)類型)。
二、含靜態(tài)成員的對(duì)象
靜態(tài)成員(變量/函數(shù))屬于類級(jí)別的共享資源,不存儲(chǔ)在對(duì)象的內(nèi)存中,而是存放在全局?jǐn)?shù)據(jù)區(qū)(或靜態(tài)存儲(chǔ)區(qū)),所有對(duì)象共享同一份。
示例:
class Person {
private:
int age; // 非靜態(tài)成員,屬于對(duì)象
static int count; // 靜態(tài)成員,不屬于對(duì)象
public:
static void incr() {} // 靜態(tài)函數(shù),不屬于對(duì)象
};
int Person::count = 0; // 靜態(tài)成員的定義
內(nèi)存結(jié)構(gòu):
對(duì)象的內(nèi)存中只包含非靜態(tài)成員age,count和incr()存放在全局區(qū),與對(duì)象內(nèi)存無關(guān)。
三、含虛函數(shù)的對(duì)象(支持多態(tài))
當(dāng)類中包含虛函數(shù)(或繼承了虛函數(shù))時(shí),對(duì)象會(huì)額外包含一個(gè)虛函數(shù)表指針(vptr),用于支持多態(tài)特性。
核心概念:
- 虛函數(shù)表(vtable):每個(gè)包含虛函數(shù)的類會(huì)有一個(gè)全局唯一的vtable,本質(zhì)是一個(gè)函數(shù)指針數(shù)組,存儲(chǔ)該類所有虛函數(shù)的地址。
- 虛函數(shù)表指針(vptr):每個(gè)對(duì)象都會(huì)包含一個(gè)vptr(通常在對(duì)象內(nèi)存的開頭),指向所屬類的vtable。當(dāng)通過基類指針調(diào)用虛函數(shù)時(shí),編譯器會(huì)通過vptr找到vtable,再調(diào)用實(shí)際的函數(shù)(實(shí)現(xiàn)動(dòng)態(tài)綁定)。
示例:
class Animal {
public:
virtual void eat() {} // 虛函數(shù)
int weight; // 成員變量
};
內(nèi)存結(jié)構(gòu):
偏移量 內(nèi)容 0-7 vptr(8字節(jié),指向Animal的vtable) 8-11 weight(4字節(jié),int類型)
- vtable的內(nèi)容:
[&Animal::eat](存儲(chǔ)eat()函數(shù)的地址)。 - 若派生類重寫了虛函數(shù):Cat類的vtable會(huì)替換為
class Cat : public Animal { public: void eat() override {} // 重寫虛函數(shù) };[&Cat::eat],Cat對(duì)象的vptr指向該vtable,實(shí)現(xiàn)“基類指針調(diào)用派生類函數(shù)”的多態(tài)。
四、繼承關(guān)系下的對(duì)象內(nèi)存結(jié)構(gòu)
繼承會(huì)導(dǎo)致派生類對(duì)象包含基類子對(duì)象(基類的成員變量+可能的vptr),再加上派生類自己的成員變量。
1. 單繼承(無虛函數(shù)覆蓋)
class Base {
public:
int a;
};
class Derived : public Base {
public:
int b;
};
Derived對(duì)象的內(nèi)存結(jié)構(gòu):
偏移量 內(nèi)容
0-3 a(基類成員)
4-7 b(派生類成員)
2. 單繼承(含虛函數(shù))
class Base {
public:
virtual void f() {}
int a;
};
class Derived : public Base {
public:
virtual void g() {} // 新增虛函數(shù)
int b;
};
Derived對(duì)象的內(nèi)存結(jié)構(gòu):
偏移量 內(nèi)容
0-7 vptr(指向Derived的vtable)
8-11 a(基類成員)
12-15 b(派生類成員)
Derived的vtable內(nèi)容:[&Base::f, &Derived::g](包含基類和派生類的虛函數(shù))。
3. 多繼承(最復(fù)雜的情況)
多繼承時(shí),派生類對(duì)象會(huì)包含所有基類的子對(duì)象,每個(gè)基類若有虛函數(shù),可能對(duì)應(yīng)一個(gè)vptr(導(dǎo)致對(duì)象內(nèi)存中存在多個(gè)vptr)。
示例:
class Base1 {
public:
virtual void f() {}
int a;
};
class Base2 {
public:
virtual void g() {}
int b;
};
class Derived : public Base1, public Base2 {
public:
int c;
};
Derived對(duì)象的內(nèi)存結(jié)構(gòu)(可能):
偏移量 內(nèi)容
0-7 vptr1(指向Derived的vtable1,對(duì)應(yīng)Base1的虛函數(shù))
8-11 a(Base1的成員)
12-19 vptr2(指向Derived的vtable2,對(duì)應(yīng)Base2的虛函數(shù))
20-23 b(Base2的成員)
24-27 c(Derived的成員)
- vtable1:
[&Derived::f](繼承自Base1的虛函數(shù)) - vtable2:
[&Derived::g](繼承自Base2的虛函數(shù))
多繼承可能導(dǎo)致指針調(diào)整:當(dāng)用Base2*指向Derived對(duì)象時(shí),指針會(huì)自動(dòng)偏移到Base2子對(duì)象的起始位置(上例中偏移12字節(jié))。
4. 虛繼承(解決菱形繼承問題)
菱形繼承(兩個(gè)派生類繼承同一基類,再被一個(gè)類繼承)會(huì)導(dǎo)致基類成員在最終派生類中存在多份副本(數(shù)據(jù)冗余+二義性)。虛繼承通過讓基類子對(duì)象只存一份解決此問題。
示例(菱形繼承):
class A { public: int x; };
class B : virtual public A { public: int y; }; // 虛繼承A
class C : virtual public A { public: int z; }; // 虛繼承A
class D : public B, public C { public: int w; };
D對(duì)象的內(nèi)存結(jié)構(gòu)(簡化):
偏移量 內(nèi)容
0-7 vptr_B(B的虛表指針)
8-11 y(B的成員)
12-19 vptr_C(C的虛表指針)
20-23 z(C的成員)
24-27 w(D的成員)
28-31 x(A的成員,僅一份,被B和C共享)
- 虛繼承通過在派生類中添加“虛基類指針”(或偏移量)定位共享的基類子對(duì)象,避免數(shù)據(jù)冗余。
總結(jié)
C++對(duì)象的內(nèi)存結(jié)構(gòu)核心由以下部分構(gòu)成(按影響優(yōu)先級(jí)):
- 非靜態(tài)成員變量:按聲明順序排列,受內(nèi)存對(duì)齊影響。
- 虛函數(shù)表指針(vptr):當(dāng)類有虛函數(shù)時(shí)存在,用于多態(tài),數(shù)量取決于繼承關(guān)系(多繼承可能有多個(gè)vptr)。
- 基類子對(duì)象:繼承時(shí)包含,順序與繼承聲明一致,虛繼承會(huì)優(yōu)化為共享副本。
靜態(tài)成員、成員函數(shù)不占用對(duì)象內(nèi)存,存放在全局區(qū)或代碼區(qū)。不同編譯器(如GCC、MSVC)在細(xì)節(jié)上(如vptr位置、對(duì)齊規(guī)則)可能有差異,但核心邏輯一致。
到此這篇關(guān)于C++對(duì)象的內(nèi)存結(jié)構(gòu)的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)C++對(duì)象內(nèi)存結(jié)構(gòu)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- C++動(dòng)態(tài)內(nèi)存分配的核心機(jī)制與最佳實(shí)踐(從對(duì)象生命周期到智能管理)
- C++利用對(duì)象池優(yōu)化內(nèi)存管理解決MISRA報(bào)警的代碼詳解
- C++內(nèi)存對(duì)象布局小測試
- c++對(duì)象內(nèi)存布局示例詳解
- C++ 面向?qū)ο蟪绦蛟O(shè)計(jì)--內(nèi)存分區(qū)詳解
- 關(guān)于C++對(duì)象繼承中的內(nèi)存布局示例詳解
- 由static_cast和dynamic_cast到C++對(duì)象占用內(nèi)存的全面分析
- 淺談C++對(duì)象的內(nèi)存分布和虛函數(shù)表
- C++對(duì)象內(nèi)存分布詳解(包括字節(jié)對(duì)齊和虛函數(shù)表)
- 淺談C++中派生類對(duì)象的內(nèi)存布局
相關(guān)文章
Qt數(shù)據(jù)庫應(yīng)用之實(shí)現(xiàn)通用數(shù)據(jù)庫清理
項(xiàng)目如果需要存儲(chǔ)很多日志記錄比如運(yùn)行日志,時(shí)間長了記錄數(shù)量非常多,數(shù)據(jù)庫體積不斷增大,對(duì)應(yīng)數(shù)據(jù)庫表的增刪改查的效率不斷降低,因此需要將早期的數(shù)據(jù)清理。本文將詳細(xì)介紹一下通用數(shù)據(jù)庫清理的實(shí)現(xiàn),需要的可以參考一下2022-02-02
基于QT設(shè)計(jì)一個(gè)春聯(lián)自動(dòng)生成器
春節(jié)是中國最隆重的傳統(tǒng)節(jié)日,一到過年家家戶戶肯定是要貼春聯(lián);在春節(jié)前夕,會(huì)用大紅紙張,加上濃墨書寫祝福詞語。本文將利用Qt框架設(shè)計(jì)一個(gè)春聯(lián)自動(dòng)生成器,需要的可以參考一下2022-01-01
C++實(shí)現(xiàn)LeetCode(187.求重復(fù)的DNA序列)
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(187.求重復(fù)的DNA序列),本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07
Qt數(shù)據(jù)庫應(yīng)用之通用數(shù)據(jù)庫同步
數(shù)據(jù)庫同步的主要功能是將本地的數(shù)據(jù)庫記錄同步到遠(yuǎn)程的數(shù)據(jù)庫。本文將利用Qt實(shí)現(xiàn)通用數(shù)據(jù)庫同步功能,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2022-03-03
C語言模擬實(shí)現(xiàn)strstr函數(shù)的示例代碼
strstr是C語言中的函數(shù),作用是返回字符串中首次出現(xiàn)子串的地址。本文將用C語言模擬實(shí)現(xiàn)strstr函數(shù),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2022-07-07
舉例講解C語言的fork()函數(shù)創(chuàng)建子進(jìn)程的用法
fork函數(shù)是Linux下一個(gè)近乎專有的C語言函數(shù),因?yàn)槭褂脮r(shí)需要調(diào)用unistd.h這個(gè)頭文件,這里我們就在Linux環(huán)境下舉例講解C語言的fork()函數(shù)創(chuàng)建子進(jìn)程的用法,需要的朋友可以參考下2016-06-06
Linux下Select多路復(fù)用實(shí)現(xiàn)簡易聊天室示例
大家好,本篇文章主要講的是Linux下Select多路復(fù)用實(shí)現(xiàn)簡易聊天室示例,感興趣的同學(xué)趕快來看一看吧,對(duì)你有幫助的話記得收藏一下,方便下次瀏覽2021-12-12
C++實(shí)現(xiàn)順序表的常用操作(插入刪出查找輸出)
實(shí)現(xiàn)順序表的插入,刪除,查找,輸出操作在C語言中經(jīng)常用到。下面小編給大家整理實(shí)現(xiàn)代碼,一起看下吧2016-08-08

