對(duì)象模型:虛函數(shù)表(vtbl)與虛表指針(vptr)
class A {
virtual void func() {
std::cout << "A::func()" << std::endl;
}
};
class B : public A {
virtual void func() {
std::cout << "B::func()" << std::endl;
}
};
int main() {
A *p = new B;
p->func(); // 輸出結(jié)果: B::func()
return 0;
}
我們知道贺拣,C++中,可以通過(guò)虛函數(shù)來(lái)實(shí)現(xiàn)多態(tài)性矗愧,而虛函數(shù)是通過(guò)虛函數(shù)表與虛表指針來(lái)進(jìn)行實(shí)現(xiàn)的御吞。
對(duì)于每個(gè)擁有虛函數(shù)的類來(lái)說(shuō),在創(chuàng)建對(duì)象時(shí)瓦哎,除了為對(duì)象的成員變量開(kāi)辟內(nèi)存空間砸喻,還會(huì)在對(duì)象的頭部存儲(chǔ)一個(gè)虛表指針,用于指向一張?zhí)摵瘮?shù)表蒋譬,這張函數(shù)表中存儲(chǔ)了這個(gè)對(duì)象的虛函數(shù)地址割岛。這樣一來(lái),當(dāng)我們要調(diào)用這個(gè)對(duì)象的虛函數(shù)時(shí)羡铲,就會(huì)通過(guò)這張?zhí)摵瘮?shù)表來(lái)查找到相應(yīng)的虛函數(shù)蜂桶,因此,即是我們是通過(guò)對(duì)象的父類指針來(lái)進(jìn)行調(diào)用也切,也能夠調(diào)用到對(duì)象真正的類型中的函數(shù)扑媚,因?yàn)楹瘮?shù)是儲(chǔ)存在對(duì)象中的。
通過(guò)這種方式雷恃,C++實(shí)現(xiàn)了多態(tài)疆股。對(duì)于虛函數(shù)的存儲(chǔ)方式,當(dāng)我們進(jìn)行多重繼承時(shí)倒槐,會(huì)有所不同旬痹。
重寫(xiě)(override)
在C++11中,增加了一個(gè)用在虛函數(shù)身上的關(guān)鍵字:override
class A {
virtual void func() {
std::cout << "A::func()" << std::endl;
}
};
class B : public A {
virtual void func() override {
std::cout << "B::func()" << std::endl;
}
};
如上所示讨越,這個(gè)關(guān)鍵字可以加在虛函數(shù)尾两残,用來(lái)表示這個(gè)函數(shù)對(duì)父類的同名函數(shù)構(gòu)成override,編譯器會(huì)幫忙檢查是否真的構(gòu)成override把跨,如果沒(méi)有人弓,則編譯時(shí)會(huì)報(bào)錯(cuò),這有助于發(fā)現(xiàn)編碼中的錯(cuò)誤着逐。
重寫(xiě)崔赌、重載意蛀、隱藏
class A {
virtual void func() {
std::cout << "A::func()" << std::endl;
}
virtual void func(int a) {
std::cout << "A::func(int)" << std::endl;
}
};
class B : public A {
virtual void func(double a) override { // error
std::cout << "B::func(double)" << std::endl;
}
};
如上所示,這種情況其實(shí)不構(gòu)成override健芭,會(huì)編譯報(bào)錯(cuò)县钥。此時(shí)A類的func(int a)對(duì)func()構(gòu)成重載,而B(niǎo)類的func(double a)對(duì)A類的兩個(gè)func形成了函數(shù)隱藏慈迈,通過(guò)B類將無(wú)法訪問(wèn)A類的兩個(gè)func若贮。
this指針
通過(guò)對(duì)象調(diào)用對(duì)象身上的方法,此時(shí)函數(shù)體本身如何知道該對(duì)哪個(gè)對(duì)象進(jìn)行操作吩翻?此時(shí)函數(shù)體靠的就是this指針來(lái)識(shí)別是對(duì)哪個(gè)對(duì)象進(jìn)行操作兜看。對(duì)象在調(diào)用成員方法時(shí),會(huì)自動(dòng)將自己作為this指針傳入成員函數(shù)狭瞎,從而使函數(shù)能夠?qū)@個(gè)對(duì)象進(jìn)行操作细移。類的static函數(shù)沒(méi)有傳入this指針,所以static函數(shù)不能操作成員變量熊锭,只能操作類的靜態(tài)變量弧轧。
class A {
void func1() const {}
void func2() volatile {}
static void func3() const {} // error
};
我們可以通過(guò)指定一個(gè)函數(shù)為const來(lái)表示這個(gè)函數(shù)不會(huì)修改對(duì)象內(nèi)容,其原理就是加上const之后傳入的this指針是const的碗殷,因此就無(wú)法通過(guò)這個(gè)this指針來(lái)修改對(duì)象的成員變量了精绎。同理,函數(shù)后加volatile的實(shí)現(xiàn)原理也是this指針是volatile的锌妻。static函數(shù)后面加const會(huì)編譯報(bào)錯(cuò)代乃,就是因?yàn)閟tatic函數(shù)沒(méi)有this指針的原因。