虚函数与纯虚函数以及虚函数表之间的关系
生活随笔
收集整理的這篇文章主要介紹了
虚函数与纯虚函数以及虚函数表之间的关系
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
1、虛函數
????????簡單地說,那些被virtual關鍵字修飾的成員函數,就是虛函數。C++中虛函數的作用主要是實現多態機制。所謂多態就是用父類指針指向子類對象,然后通過父類指針調用實際子類的成員函數,這種技術可以讓父類指針有“多種形態”。
2、純虛函數
????????在虛函數形參后面寫上=0,則該函數為純虛函數。純虛函數沒有函數體;純虛函數只有函數的名字而不具備函數的功能,不能被調用。純虛函數的作用是在基類中為其派生類保留一個函數的名字,以便派生類根據需要對他進行定義。如果在一個類中聲明了純虛函數,在其派生類中沒有對其函數進行定義,則該虛函數在派生類中仍然為純虛函數。
3、虛函數表
????????虛函數是通過虛函數表來實現的,在這個表中,主要是一個類的虛函數的地址表,這張表解決了繼承、覆蓋的問題。在有虛函數的實例中這張表被分配在這個實例的內存中,所以當我們用父類指針來操作一個子類的時候,這張虛函數表就像地圖一樣指明實際所應該調用的函數。
eg:
class Base{ public:virtual void f(){cout<<"Base::f"<<endl;}virtual void g(){cout<<"Base::g"<<endl;}virtual void h(){cout<<"Base::h"<<endl;} };typedef void(*Fun)(void); Base b; Fun pFun = NULL; cout<<"虛函數表的地址:"<<(int*)(&b)<<endl; cout<<"虛函數表的第一個函數地址:"<<(int*)*(int*)(&b)<<endl; pFun = (Fun)*((int*)*(int*)(&b)); pFun();/* 實際運行經果如下:(Windows XP+VS2003, Linux 2.6.22 + GCC 4.1.3) 虛函數表地址:0012FED4 虛函數表的第一個函數地址:0044F148 Base::f同理可得到其他虛函數: (Fun)*((int*)*(int*)(&b)+0); // Base::f() (Fun)*((int*)*(int*)(&b)+1); // Base::g() (Fun)*((int*)*(int*)(&b)+2); // Base::h() */(1)一般繼承:假如有派生類繼承基類,但是沒有覆蓋其虛函數
class Derive :public Base { public:virtual void f1(){cout<<"Derive::f1"<<endl;}virtual void g1(){cout<<"Derive::g1"<<endl;}virtual void h1(){cout<<"Derive::h1"<<endl;} };Derive d; ((Fun)*((int*)*(int*)(&d)+0))(); // Base::f() ((Fun)*((int*)*(int*)(&d)+1))(); // Base::g() ((Fun)*((int*)*(int*)(&d)+2))(); // Base::h() ((Fun)*((int*)*(int*)(&d)+3))(); // Derive::f1() ((Fun)*((int*)*(int*)(&d)+4))(); // Derive::g1() ((Fun)*((int*)*(int*)(&d)+5))(); // Derive::h1()// 父類指針指向子類對象 Base* b = &d; ((Fun)*((int*)*(int*)b+0))(); // Base::f() ((Fun)*((int*)*(int*)b+1))(); // Base::g() ((Fun)*((int*)*(int*)b+2))(); // Base::h() ((Fun)*((int*)*(int*)b+3))(); // Derive::f1() ((Fun)*((int*)*(int*)b+4))(); // Derive::g1() ((Fun)*((int*)*(int*)b+5))(); // Derive::h1()/* 1、虛函數按照其聲明順序放在 2、父類的虛函數在子類的虛函數前面 */(2)一般繼承:假如派生類覆蓋了父類的虛函數
class Derive :public Base { public:virtual void f(){cout<<"Derive::f"<<endl;}virtual void g1(){cout<<"Derive::g1"<<endl;}virtual void h1(){cout<<"Derive::h1"<<endl;} }Derive b; Base* d = &b; (Fun)*((int*)*(int*)d+0); // Derive::f() (Fun)*((int*)*(int*)d+1); // Base::g() (Fun)*((int*)*(int*)d+2); // Base::h() (Fun)*((int*)*(int*)d+3); // Derive::g1() (Fun)*((int*)*(int*)d+4); // Derive::h1()// 由上可知,被覆蓋的取代原來父類虛函數的位置,沒覆蓋的按聲明順序(3)多重繼承:無虛函數覆蓋
class Base1{ public:virtual void f(){cout<<"Base1::f"<<endl;}virtual void g(){cout<<"Base1::g"<<endl;}virtual void h(){cout<<"Base1::h"<<endl;} };class Base2{ public:virtual void f(){cout<<"Base2::f"<<endl;}virtual void g(){cout<<"Base2::g"<<endl;}virtual void h(){cout<<"Base2::h"<<endl;} };class Base3{ public:virtual void f(){cout<<"Base3::f"<<endl;}virtual void g(){cout<<"Base3::g"<<endl;}virtual void h(){cout<<"Base3::h"<<endl;} };class Derive1: public Base1,public Base2,public Base3 { public:virtual void f1(){cout<<"Derive1::f1"<<endl;}virtual void g1(){cout<<"Derive1::g1"<<endl;} };// #1 直接從子類對象查找虛函數對應的地址 Derive1 d; ((Fun)*((int*)*((int*)(&d)+0)+0))(); // Base1::f ((Fun)*((int*)*((int*)(&d)+0)+1))(); // Base1::g ((Fun)*((int*)*((int*)(&d)+0)+2))(); // Base1::h ((Fun)*((int*)*((int*)(&d)+0)+3))(); // Derive1::f1 ((Fun)*((int*)*((int*)(&d)+0)+4))(); // Derive1::g1((Fun)((int*)*((int*)(&d)+1)+0))(); // Base2::f ((Fun)*((int*)*((int*)(&d)+1)+1))(); // Base2::g ((Fun)*((int*)*((int*)(&d)+1)+2))(); // Base2::h((Fun)*((int*)*((int*)(&d)+2)+0))(); // Base3::f ((Fun)*((int*)*((int*)(&d)+2)+1))(); // Base3::f ((Fun)*((int*)*((int*)(&d)+2)+2))(); // Base3::f// #2 從父類指針指向子類對象查找虛函數對應的地址 Base1* b1 = &d; ((Fun)*((int*)*((int*)b1+0)+0))(); // Base1::f ((Fun)*((int*)*((int*)b1+0)+1))(); // Base1::g ((Fun)*((int*)*((int*)b1+0)+2))(); // Base1::h ((Fun)*((int*)*((int*)b1+0)+3))(); // Derive1::f1 ((Fun)*((int*)*((int*)b1+0)+4))(); // Derive1::g1Base2* b2 = &d; ((Fun)*((int*)*((int*)b2+0)+0))(); // Base2::f ((Fun)*((int*)*((int*)b2+0)+1))(); // Base2::g ((Fun)*((int*)*((int*)b2+0)+2))(); // Base2::hBase3* b3 = &d; ((Fun)*((int*)*((int*)b3+0)+0))(); // Base3::f ((Fun)*((int*)*((int*)b3+0)+1))(); // Base3::g ((Fun)*((int*)*((int*)b3+0)+2))(); // Base3::h/* #1和#2都說明了子類的虛函數是緊接著第一個繼承的父類, 而且多重繼承的虛函數表是不一樣的, (1)對于子類對象來說,虛函數表像一個二維數組一樣, 數組的第一行放置第一個繼承父類的虛函數和子類本身的虛函數 數組的第二行放置第二個繼承父類的虛函數 數組的第三行放置第個繼承父類的虛函數 (2)對于父類來說, Base1的虛函數表有其自身的虛函數以及子類的虛函數 Base2和Base3的虛函數表都只有其自身的虛函數 */(4)多重繼承:有虛函數覆蓋
class Derive2: public Base1,public Base2,public Base3 { public:virtual void f(){cout<<"Derive2::f"<<endl;}virtual void g1(){cout<<"Derive2::g1"<<endl;} };// #1 直接從子類對象查找虛函數對應的地址 Derive2 d; ((Fun)*((int*)*((int*)(&d)+0)+0))(); // Derive2::f// #2 從父類指針指向子類對象查找虛函數對應的地址 Base1* b1 = &d; ((Fun)*((int*)*((int*)b1+0)+0))(); // Derive2::fBase2* b2 = &d; ((Fun)*((int*)*((int*)b2+0)+0))(); // Derive2::fBase3* b3 = &d; ((Fun)*((int*)*((int*)b3+0)+0))(); // Derive2::f// 這時候每個父類的f()虛函數都會被子類的f()所覆蓋,其他虛函數不變????????另外,需要注意的是,當父類指針指向子類對象時,不能訪問子類特有的虛函數,只能訪問子類覆蓋父類的虛函數。
總結
以上是生活随笔為你收集整理的虚函数与纯虚函数以及虚函数表之间的关系的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 超级产品:国内美妆行业正在发生什么变化?
- 下一篇: 为了买基金,写个基金理投资财分析系统,哈