對(duì)象的內(nèi)存布局
在class和struct中,編譯器不會(huì)把數(shù)據(jù)程序員緊湊的包裹在一起,因?yàn)槊糠N數(shù)據(jù)都有其天然的對(duì)齊方式,供CPU高效的從內(nèi)存讀/寫(xiě)。對(duì)齊方式即內(nèi)存地址為對(duì)齊字節(jié)大小的倍數(shù),另外編譯器可能會(huì)為了數(shù)組的對(duì)齊,會(huì)在末端加入填充,以下是32位系統(tǒng)下各個(gè)類(lèi)型的大小(單位:字節(jié)):
int:4字節(jié);float:4字節(jié);double:8字節(jié);bool:1字節(jié);char:1字節(jié);short:2字節(jié);long:4字節(jié)或者8字節(jié)
例如下圖:
struct InefficientPacking
{
U32 mU1; //32位
F32 mF2; //32位
U8 mB3; //8位
I32 mI4; //32位
bool mB5; //8位
char* mP6; //32位
};

現(xiàn)在,我們重新考慮上圖的中struct InefficientPacking布局里的空隙。在class或struct中,當(dāng)把較小的數(shù)據(jù)類(lèi)型(如8位的bool)放置于較大類(lèi)型(如32位的float)之間,編譯器會(huì)加入填充(空隙),以保證所有成員都是正常地對(duì)齊的。當(dāng)聲明數(shù)據(jù)結(jié)構(gòu)時(shí),認(rèn)真對(duì)待對(duì)齊和包裹是個(gè)好習(xí)慣。如以下代碼及圖所示,只需簡(jiǎn)單地重新排列上述例子中的成員,就能省去了一些浪費(fèi)了的填充空間。
struct MoreEfficientPacking
{
U32 mU1; //32位(4字節(jié)對(duì)齊)
F32 mF2; //32位(4字節(jié)對(duì)齊)
I32 mI4; //32位(4字節(jié)對(duì)齊)
char* mP6; //32位(4字節(jié)對(duì)齊)
U8 mB3; //8位(1字節(jié)對(duì)齊)
bool mB5; //8位(1字節(jié)對(duì)齊)
};

在內(nèi)存布局上,C++的類(lèi)有別于C的結(jié)構(gòu)之處有二——繼承與虛函數(shù)。
當(dāng)B類(lèi)繼承自A類(lèi),內(nèi)存里B類(lèi)的數(shù)據(jù)成員會(huì)緊接A類(lèi)數(shù)據(jù)成員之后,如圖所示。

需要說(shuō)明的是,當(dāng)class中有虛函數(shù)的時(shí)候,或者是繼承的類(lèi)中有虛函數(shù)的時(shí)候,通常會(huì)在類(lèi)的布局最前端加入一個(gè)虛表指針,它指向名為虛函數(shù)表的一個(gè)數(shù)據(jù)結(jié)構(gòu),因?yàn)橹羔樖莍nt類(lèi)型的。
了解內(nèi)存布局的意義是,當(dāng)我們寫(xiě)類(lèi)和結(jié)構(gòu)體的時(shí)候,最優(yōu)化的處理方式是自己按照內(nèi)存布局規(guī)則把數(shù)據(jù)排列好,從而可以降低類(lèi)或者結(jié)構(gòu)體所占的大小。