Cpp 对象模型探索 / 继承关系下的虚函数手动调用
一、多態(tài)機(jī)理
#include <iostream>class Father { public:virtual void Func1(){std::cout << "Fahter::Func1()" << std::endl;}virtual void Func2(){std::cout << "Fahter::Func2()" << std::endl;}virtual void Func3(){std::cout << "Fahter::Func3()" << std::endl;}private:size_t count_; };class Son : public Father { public:virtual void Func2(){std::cout << "Son::Func2()" << std::endl;} };using FUNC = void (*)();int main() {Father *pfather = new Father;Son *pson = new Son;// 得到指向虛函數(shù)表指針的地址。long *pvptr_father = (long *)pfather;long *pvptr_son = (long *)pson;// 得到指向虛函數(shù)表的指針。 long *vptr_father = (long *)(*pvptr_father); long *vptr_son = (long *)(*pvptr_son); FUNC func1_father, func2_father, func3_father;func1_father = (FUNC) * (vptr_father + 0); // 得到虛函數(shù) Father::Func1() 的地址。func2_father = (FUNC) * (vptr_father + 1); // 得到虛函數(shù) Father::Func2() 的地址。func3_father = (FUNC) * (vptr_father + 2); // 得到虛函數(shù) Father::Func2() 的地址。func1_father();func2_father();func3_father();std::cout << std::endl;FUNC func1_son, func2_son, func3_son;func1_son = (FUNC) * (vptr_son + 0); // 得到虛函數(shù) Father::Func1() 的地址。func2_son = (FUNC) * (vptr_son + 1); // 得到虛函數(shù) Son::Func2() 的地址。func3_son = (FUNC) * (vptr_son + 2); // 得到虛函數(shù) Father::Func2() 的地址。func1_son();func2_son();func3_son();return 0; }結(jié)果:
Fahter::Func1() Fahter::Func2() Fahter::Func3()Fahter::Func1() Son::Func2() Fahter::Func3()二、分析
? ? ? ?上圖介紹了C++多態(tài)的實(shí)現(xiàn)機(jī)理。注意:虛函數(shù)表屬于類,虛函數(shù)表指針屬于對象。
? ? ? ?子類繼承父類時(shí),會得到和父類相同的虛函數(shù)表,該表記載著代碼區(qū)中屬于該類的虛函數(shù)的起始地址。當(dāng)子類沒有重寫虛函數(shù)時(shí),程序調(diào)用子類的虛函數(shù),就相當(dāng)于調(diào)用父類的虛函數(shù)。當(dāng)子類重寫了父類的虛函數(shù)時(shí),相當(dāng)于將子類的虛函數(shù)表中的相應(yīng)虛函數(shù)的地址修改為子類虛函數(shù)的首地址,從而完成了多態(tài)!
三、對象切割
Son sn; Father fa = sn; fa.Func1(); fa.Func2(); fa.Func3();結(jié)果:
Fahter::Func1() Fahter::Func2() Fahter::Func3()? ? ? ?對于多態(tài),使用指針或者引用,用父類調(diào)用子類的函數(shù)達(dá)到工廠模式的目的。倘若用子類直接賦值給父類,會有什么結(jié)果呢。上述代碼就展示了最后的結(jié)果,并沒有調(diào)用子類的函數(shù),而是直接調(diào)用了父類的函數(shù)。
? ? ? ?原因是直接用子類給父類對象賦值,子類中的屬于父類部分內(nèi)容會被編譯器自動切割出來并拷貝給了父類。
? ? ? ?實(shí)際上上述操作一共干了兩件事情,
? ? ? ?1、生成 Father 對象。
? ? ? ?2、用 sn 來初始化 Father 對象。顯然,編譯器并沒有將 sn 的虛函數(shù)表指針覆蓋掉 fa 的虛函數(shù)表指針。
四、總結(jié)
1、類只有包含了虛函數(shù)才存在虛函數(shù)表(只有一個(gè)),同屬于一個(gè)類的對象共享虛函數(shù)表,但是有各自的vptr(虛函數(shù)表指針)。
2、父類含有虛函數(shù),子類就會有一個(gè)虛函數(shù)表,但是子類的虛函數(shù)表只是和父類的虛函數(shù)表中的內(nèi)容相同,所在的內(nèi)存位置不同。
3、多態(tài)是子類賦值給父類的指針或者引用。若直接對象賦值,是對象切割的范疇。
?
(SAW:Game Over!)
總結(jié)
以上是生活随笔為你收集整理的Cpp 对象模型探索 / 继承关系下的虚函数手动调用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数据结构与算法 / 排序算法(2)
- 下一篇: 数据结构与算法 / 排序算法(3)