1. 程式人生 > >C++跟我一起透徹理解虛函數表

C++跟我一起透徹理解虛函數表

技術 覆蓋 text 編譯 pretty ring 對象 virt roc

//首先讓我們來了解類對象的構造順序。
#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基類的虛表。

技術分享

C++跟我一起透徹理解虛函數表