C++跟我一起透徹理解虛函數表
阿新 • • 發佈:2017-07-20
技術 覆蓋 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++跟我一起透徹理解虛函數表