C++跟我一起透徹理解虛函數表,函數表
//首先讓我們來瞭解類對象的構造順序。#include <iostream>using namespace std;class A{public: A(){ cout << "A" << endl; } virtual void PrintfA() = 0;};class B {public: B(){ cout << "B" << endl; }};class C :virtual public A , public B{ //關於構造對象順序: //第一步先構造虛基類,例如此處的虛繼承。 //第二步構造一般繼承類,此處從左往右順序執行。 //第三部構造成員變數,記住這裡的成員變數的構造是順序執行的。 //第四構造自身的建構函式。public: C() { cout << "C" << endl; } void PrintfA() { cout << "hello word!!" << endl; }private:};int main(){ C c; return 0;}
#include <iostream>using namespace std;typedef void (*Pfun)();class Base{public: virtual void Printf() { cout << "Base::Printf()" << endl; } void PrintfBae() { cout << "Base::PrintfBase" << endl; }};class Son1:public Base{public: virtual void Printf() { cout << "Son1::Printf()" << endl; } void PrintfSon1() { cout << "Son1::PrintfSon1()" << endl; }};class Son2 : public Son1{public: void Printf() { cout << "Son2::Printf()" << endl; } virtual void hello() { cout << "hello" << endl; } void PrintfSon2() { cout << "Son2::Printf()" << endl; }};int main(){ Son2 s2; Base &b = s2; //b.Printf(); //此處可以得出這三個類維護了一個共同的虛表,但是我們還無法知道他們的 //具體位置以及各個類裡面非虛函數的儲存關係,所以我們接下來驗證。 //Pfun fun = (Pfun)*((int *)(&(int*)&b)+1); Pfun fun = (Pfun)*((int *)*(int *)(int *)(&b) + 0); //這種做法基本沒有人解釋,所以我在後面用圖解釋了。 fun(); fun = (Pfun)*((int *)*(int *)(int *)(&b) + 1); fun(); //-------------------------------------------------------- fun = (Pfun)*((int *)*(int *)(int *)(&s2) + 0); fun(); fun = (Pfun)*((int *)*(int *)(int *)(&b) + 1); fun(); //列印結果與上面一樣,可以得出這份虛表是所有繼承該擁有虛表基類的成員類所共用的。 //每個對象的產生必定有兩個資料,一個vptr指向虛表的指標,還有其自身的成員 //變數。 return 0;}//總結1:前提是基類是有虛表的,單一繼承,所有的成員類與基類共同維護同一個虛表,各自有//一個vptr指向這個虛表,子類如果會對虛表進行替換修改,具體會根據子類是否重載了基類的//虛函數來確定,我們成為覆蓋。多繼承下面討論,其實本質是一樣的,關鍵問題是,多繼承中//子類中到底有幾份虛vptr指標。
#include<iostream>using namespace std;class A{public: virtual void Printf() { cout << "A::Printf()" << endl; } virtual void PrintfA() { cout << "A::PrintfA()" << endl; }};class B{public: virtual void PrintfB() { cout << "B::Printf()" << endl; } virtual void Printf() { cout << "B::Printf()" << endl; }};class C : public A, public B{public: void Printf() { cout << "C::Printf()" << endl; }};int main(){ typedef void(*PFun)(); C c; PFun fun = (PFun)*((int *)*(int *)((int *)(&c)+0) + 0);//第一個虛表指標vptr。 fun();//此處覆蓋了,調用的是C中的Printf(); fun = (PFun)* ((int *)*(int *)((int *)(&c) + 1) + 0);//第二個虛表指標vptr。 fun(); //此處還有一個亮點,就是為什麼(int *)(int *)兩次,這下你應該明白了吧,因為可能 //有多個虛指標,編譯器做的很好了,如果不這樣做編譯會報錯。 cout << sizeof(c) << endl;//終極測試結果8。 return 0;}//總結2:多繼承中每一個基類都維護一張自己的虛表,而子類中每繼承一個虛基類就會有存在一個//指向該基類虛表的指標,以上代碼測試結果是C對象中有兩個vptr指標,一個指向A基類的虛表,一//個指向B基類的虛表。
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。