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

