本周介紹的是C++中末了剩下幾個(gè)重要概念,分別是
- 對(duì)象模型
- const
- new和delete重載
對(duì)象模型
對(duì)象模型里面包含了虛表和虛指針,這個(gè)又回到了以前分析過(guò)的對(duì)象在C++內(nèi)存中的分配。如果class里面有非虛函數(shù),那么這個(gè)非虛函數(shù)就會(huì)在編譯時(shí)作為靜態(tài)指針?lè)诺絚lass的內(nèi)存中去。class中如果有虛函數(shù),那么這個(gè)時(shí)候就會(huì)產(chǎn)生很多可能了。虛函數(shù)編譯時(shí)的指針不直接指向函數(shù)體本身,它指向一個(gè)表(虛表),這個(gè)虛表存儲(chǔ)了class中虛函數(shù)的個(gè)數(shù),在調(diào)用時(shí),會(huì)將this指針通過(guò)隱藏參數(shù)的方式傳遞進(jìn)來(lái)。每個(gè)帶有虛函數(shù)的class都會(huì)包含一根虛指針,這根指針就在這個(gè)類(lèi)內(nèi)存的頭部存放。調(diào)用的時(shí)候,碰到對(duì)虛函數(shù)的調(diào)用,就會(huì)到對(duì)象的頭部去找到指針?biāo)赶虻奶摫碇腥?,然后按順序查找到所需要調(diào)用的虛函數(shù)之后,將this指針傳遞進(jìn)去。
const
const關(guān)鍵字從前只是強(qiáng)調(diào),凡是不打算更改成員變量的成員函數(shù),都應(yīng)該加const。因?yàn)?,如果使用者聲明了一個(gè)const對(duì)象,只是調(diào)用這個(gè)對(duì)象的某些不更改數(shù)據(jù)的函數(shù)時(shí),編譯器就會(huì)報(bào)錯(cuò)。
但本周介紹的是更好更通用的一個(gè)用法,這里面隱藏著一個(gè)編譯器給定的一個(gè)規(guī)則。之前自己寫(xiě)的代碼好像也是這么寫(xiě)的,但并不知道為什么編譯能通過(guò)。
我是寫(xiě)過(guò)一個(gè)叫做vector的class
template <typename T, unsigned int N>
class vector {
public:
T &operator[](unsigned int index) {
if (index >= 0 && index < N) return component[index];
std::cout << "Error, index out of range";
exit(1);
};
T operator[](unsigned int index) const {
if (index >= 0 && index < N) return component[index];
return T(0);
};
private:
T component[N];
unsigned int _dim;
};
當(dāng)時(shí)只是想著用引用的時(shí)候會(huì)比較高效,但同時(shí)也要考慮到const對(duì)象的傳值,所以加了一個(gè)const的成員函數(shù)。
本周我們知道了如果一個(gè)class在編寫(xiě)的時(shí)候,同時(shí)提供了const和非const的成員函數(shù),那么編譯器會(huì)將const成員函數(shù)與const對(duì)象合用,將非const成員函數(shù)與非const對(duì)象合用。
那這件事就很好理解了。需要高效的時(shí)候傳引用來(lái)快速訪問(wèn)數(shù)據(jù),需要安全的時(shí)候傳const值來(lái)保護(hù)數(shù)據(jù)。嗯,完美。
new和delete重載
原來(lái)new和delete也可以重載。打開(kāi)了新世界的大門(mén)。
當(dāng)然,new操作會(huì)被編譯器自動(dòng)拆分為三個(gè)動(dòng)作
- 調(diào)用重載的new
- 將對(duì)象指針指向new回來(lái)的內(nèi)存地址
- 調(diào)用構(gòu)造函數(shù)
delete操作就分為兩步了
- 調(diào)用析構(gòu)函數(shù)
- 將對(duì)象指針傳給重載的delete
new操作符的重載必須寫(xiě)成class::operator new(size_t size);這種形式,而且第一個(gè)參數(shù)必須是size_t類(lèi)型。并且可以有多個(gè)不同參數(shù)列的new操作重載。與此對(duì)應(yīng)的,就可以有多個(gè)不同參數(shù)列的delete操作重載與new操作重載對(duì)應(yīng)。當(dāng)指定的new重載操作調(diào)用后遇到構(gòu)造函數(shù)執(zhí)行錯(cuò)誤,就會(huì)調(diào)用與之對(duì)應(yīng)的delete操作將申請(qǐng)回來(lái)的內(nèi)存還回去。
于是,new操作就給了我們一個(gè)偷偷更改分配內(nèi)存空間大小和如何返回內(nèi)存地址的方法了。用這個(gè)方法,可以自己編寫(xiě)內(nèi)存池,來(lái)動(dòng)態(tài)管理內(nèi)存。
也有一個(gè)辦法可以繞過(guò)class中重載的operator new(),那就是調(diào)用全局的::operator new()操作。
比如
Fruit *f = ::operator new Fruit();