C++的虛函數(shù)(Virtual Function)是通過一張?zhí)摵瘮?shù)表(Virtual Table)來實現(xiàn)的。簡稱為V-Table。 在這個表中,主是要一個類的虛函數(shù)的地址表,這張表解決了繼承、覆蓋的問題,保證其容真實反應實際的函數(shù)。這樣,在有虛函數(shù)的類的實例中這個表被分配在了
這個實例的內(nèi)存中,所以,當我們用父類的指針來操作一個子類的時候,這張?zhí)摵瘮?shù)表就顯得由為重要了,它就像一個地圖一樣,指明了實際所應該調(diào)用的函數(shù)。
1. 無繼承的情況
#include <iostream>
using namespace std;
class Base {
public:
virtual void f() { cout << "Base::f()" << endl; }
virtual void g() { cout << "Base::g()" << endl; }
virtual void h() { cout << "Base::h()" << endl; }
};
int main()
{
typedef void (*Fun)();
Base *b = new Base;
cout << *(int*)(&b) << endl; //虛函數(shù)表的地址存放在對象最開始的位置
Fun funf = (Fun)(*(int*)*(int*)b);
Fun fung = (Fun)(*((int*)*(int*)b + 1));
Fun funh = (Fun)(*((int*)*(int*)b + 2));
funf();
fung();
funh();
cout << (Fun)(*((int*)*(int*)b + 3)); // 最后一個位置為0,表明虛函數(shù)表的結(jié)束
return 0;
}
|
注意:在上面這個圖中,虛函數(shù)表中最后一個節(jié)點相當于字符串的結(jié)束符,其標志了虛函數(shù)表的結(jié)束,在Codeblocks下打印為0。
2. 繼承,無虛函數(shù)覆蓋的情形 #include <iostream>
using namespace std;
class Base {
public:
virtual void f() { cout << "Base::f()" << endl; }
virtual void g() { cout << "Base::g()" << endl; }
virtual void h() { cout << "Base::h()" << endl; }
};
class Derive: public Base {
virtual void f1() { cout << "Derive::f1()" << endl; }
virtual void g1() { cout << "Derive::g1()" << endl; }
virtual void h1() { cout << "Derive::h1()" << endl; }
};
int main()
{
typedef void (*Fun)();
Base *b = new Derive;
cout << *(int*)b << endl;
Fun funf = (Fun)(*(int*)*(int*)b);
Fun fung = (Fun)(*((int*)*(int*)b + 1));
Fun funh = (Fun)(*((int*)*(int*)b + 2));
Fun funf1 = (Fun)(*((int*)*(int*)b + 3));
Fun fung1 = (Fun)(*((int*)*(int*)b + 4));
Fun funh1 = (Fun)(*((int*)*(int*)b + 5));
funf(); // Base::f()
fung(); // Base::g()
funh(); // Base::h()
funf1(); // Derive::f1()
fung1(); // Derive::g1()
funh1(); // Derive::h1()
cout << (Fun)(*((int*)*(int*)b + 6));
return 0;
}
|
從上表可以發(fā)現(xiàn):
1. 虛函數(shù)按照其聲明順序放于表中。
2. 父類的虛函數(shù)在子類的虛函數(shù)前面。
3. 繼承,虛函數(shù)覆蓋的情形
#include <iostream>
using namespace std;
class Base {
public:
virtual void f() { cout << "Base::f()" << endl; }
virtual void g() { cout << "Base::g()" << endl; }
virtual void h() { cout << "Base::h()" << endl; }
};
class Derive: public Base {
virtual void f() { cout << "Derive::f()" << endl; }
virtual void g1() { cout << "Derive::g1()" << endl; }
virtual void h1() { cout << "Derive::h1()" << endl; }
};
int main()
{
typedef void (*Fun)();
Base *b = new Derive;
cout << *(int*)b << endl;
Fun funf = (Fun)(*(int*)*(int*)b);
Fun fung = (Fun)(*((int*)*(int*)b + 1));
Fun funh = (Fun)(*((int*)*(int*)b + 2));
Fun fung1 = (Fun)(*((int*)*(int*)b + 3));
Fun funh1 = (Fun)(*((int*)*(int*)b + 4));
funf(); // Derive::f()
fung(); // Base::g()
funh(); // Base::h()
fung1(); // Derive::g1()
funh1(); // Derive::h1()
cout << (Fun)(*((int*)*(int*)b + 5));
return 0;
}
|
從上表可以看出:
1. 覆蓋的f()函數(shù)被放到了虛表中原來父類虛函數(shù)的位置。
2. 沒有被覆蓋的函數(shù)依舊。
3. 可通過獲取獲取成員函數(shù)指針來調(diào)用成員函數(shù)(即使是private類型的),帶 來一定安全性的影響。
4. 多繼承的情形
#include <iostream>
using namespace std;
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 Derive: public Base1,public Base2, public Base3 {
virtual void f() { cout << "Derive::f()" << endl; }
virtual void g1() { cout << "Derive::g1()" << endl; }
};
int main()
{
typedef void (*Fun)();
Derive d;
Base1 *b1 = &d;
Base2 *b2 = &d;
Base3 *b3 = &d;
b1->f(); //Derive::f()
b2->f(); //Derive::f()
b3->f(); //Derive::f()
b1->g(); //Base1::g()
b2->g(); //Base2::g()
b3->g(); //Base3::g()
Fun b1fun = (Fun)(*(int*)*(int*)b1);
Fun b2fun = (Fun)(*(int*)*((int*)b1+1));
Fun b3fun = (Fun)(*(int*)*((int*)b1+2));
b1fun(); // Derive::f()
b2fun(); // Derive::f()
b3fun(); // Derive::f()
return 0;
}
|
從上表可以看出:
1.
每個父類都有自己的虛表。
2.
子類的成員函數(shù)被放到了第一個父類的表中。(所謂的第一個父類是按照聲明順序來判斷的)
3.
對于多繼承無虛函數(shù)覆蓋的情況,布局與上圖類似(Derive的位置對應Base)。
|