【本文鏈接】
http://www.cnblogs.com/hellogiser/p/virtual-function-table.html
【分析】
對C++ 了解的人都應該知道虛函數(Virtual Function)是通過一張虛函數表(Virtual Table)來實現的。簡稱為V-Table。在這個表中,主是要一個類的虛函數的地址表,這張表解決了繼承、覆蓋的問題,保證其內容真實反應實際的函數。這樣,在有虛函數的類的實例中這個表被分配在了這個實例的內存中,所以,當我們用父類的指針來操作一個子類的時候,這張虛函數表就顯得由為重要了,它就像一個地圖一樣,指明了實際所應該調用的函數。
這里我們着重看一下這張虛函數表。C++的編譯器應該是保證虛函數表的指針存在於對象實例中最前面的位置(這是為了保證取到虛函數表的有最高的性能——如果有多層繼承或是多重繼承的情況下)。 這意味着我們通過對象實例的地址得到這張虛函數表,然后就可以遍歷其中函數指針,並調用相應的函數。
可以將虛函數表看做一個二維數組,其中每個一維數組的首地址存儲在對象的實例之中。
來看個例子,通過實例得到虛函數表地址,進而訪問虛函數。
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
/* version: 1.0 author: hellogiser blog: http://www.cnblogs.com/hellogiser date: 2014/9/29 */ #include "stdafx.h" #include <iostream> using namespace std; class Base1 { public : Base1() { } virtual void f() { cout << "Base1::f" << endl; } virtual void g() { cout << "Base1::g" << endl; } virtual void h() { cout << "Base1::h" << endl; } }; typedef void (*Fun)( void ); void test() { Base1 obj; Fun pFun; int **pVtab = ( int **)(&obj); // base1 pFun = (Fun)pVtab[ 0 ][ 0 ]; pFun(); pFun = (Fun)pVtab[ 0 ][ 1 ]; pFun(); pFun = (Fun)pVtab[ 0 ][ 2 ]; pFun(); } int main() { test(); return 0 ; } /* Base1::f Base1::g Base1::h */ |
圖解如下
在上面這個圖中,虛函數表的最后多加了一個結點,這是虛函數表的結束結點,就像字符串的結束符“/0”一樣,其標志了虛函數表的結束。這個結束標志的值在不同的編譯器下是不同的。
下面,將分別說明“無覆蓋”和“有覆蓋”時的虛函數表的樣子。沒有覆蓋父類的虛函數是毫無意義的。之所以要講述沒有覆蓋的情況,主要目的是為了給一個對比。在比較之下,我們可以更加清楚地知道其內部的具體實現。
(1)一般繼承(無虛函數覆蓋)
下面,再讓我們來看看繼承時的虛函數表是什么樣的。假設有如下所示的一個繼承關系:
請注意,在這個繼承關系中,子類沒有重載任何父類的函數。那么,在派生類的實例中,其虛函數表如下所示:
對於實例:Derive d; 的虛函數表如下:
我們可以看到下面幾點:
1)虛函數按照其聲明順序放於表中。
2)父類的虛函數在子類的虛函數前面。
代碼驗證如下
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
/* version: 1.0 author: hellogiser blog: http://www.cnblogs.com/hellogiser date: 2014/9/29 */ #include "stdafx.h" #include <iostream> using namespace std; class Base1 { public : Base1() { } virtual void f() { cout << "Base1::f" << endl; } virtual void g() { cout << "Base1::g" << endl; } virtual void h() { cout << "Base1::h" << endl; } }; class Derived: public Base1 { virtual void f1() { cout << "Derived::f1" << endl; } virtual void g1() { cout << "Derived::g1" << endl; } virtual void h1() { cout << "Derived::h1" << endl; } }; typedef void (*Fun)( void ); void test() { Derived obj; Fun pFun; int **pVtab = ( int **)(&obj); // base1 pFun = (Fun)pVtab[ 0 ][ 0 ]; pFun(); pFun = (Fun)pVtab[ 0 ][ 1 ]; pFun(); pFun = (Fun)pVtab[ 0 ][ 2 ]; pFun(); // derived pFun = (Fun)pVtab[ 0 ][ 3 ]; pFun(); pFun = (Fun)pVtab[ 0 ][ 4 ]; pFun(); pFun = (Fun)pVtab[ 0 ][ 5 ]; pFun(); } int main() { test(); return 0 ; } /* Base1::f Base1::g Base1::h Derived::f1 Derived::g1 Derived::h1 */ |
(2)一般繼承(有虛函數覆蓋)
覆蓋父類的虛函數是很顯然的事情,不然,虛函數就變得毫無意義。下面,我們來看一下,如果子類中有虛函數重載了父類的虛函數,會是一個什么樣子?假設,我們有下面這樣的一個繼承關系。
為了看到被繼承過后的效果,在這個類的設計中,只覆蓋了父類的一個函數:f()。那么,對於派生類的實例,其虛函數表會是下面的一個樣子:
我們從表中可以看到下面幾點,
1)覆蓋的f()函數被放到了虛表中原來父類虛函數的位置。
2)沒有被覆蓋的函數依舊。
代碼驗證如下
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
/* version: 1.0 author: hellogiser blog: http://www.cnblogs.com/hellogiser date: 2014/9/29 */ #include "stdafx.h" #include <iostream> using namespace std; class Base1 { public : Base1() { } virtual void f() { cout << "Base1::f" << endl; } virtual void g() { cout << "Base1::g" << endl; } virtual void h() { cout << "Base1::h" << endl; } }; class Derived: public Base1 { virtual void f() { cout << "Derived::f" << endl; } virtual void g1() { cout << "Derived::g1" << endl; } virtual void h1() { cout << "Derived::h1" << endl; } }; typedef void (*Fun)( void ); void test() { Derived obj; Fun pFun; int **pVtab = ( int **)(&obj); pFun = (Fun)pVtab[ 0 ][ 0 ]; pFun(); pFun = (Fun)pVtab[ 0 ][ 1 ]; pFun(); pFun = (Fun)pVtab[ 0 ][ 2 ]; pFun(); pFun = (Fun)pVtab[ 0 ][ 3 ]; pFun(); pFun = (Fun)pVtab[ 0 ][ 4 ]; pFun(); } int main() { test(); return 0 ; } /* Derived::f Base1::g Base1::h Derived::g1 Derived::h1 */ |
這樣,我們就可以看到對於下面這樣的程序,
1
2 |
Base *b =
new
Derived(); b->f(); |
由b所指的內存中的虛函數表的f()的位置已經被Derive::f()函數地址所取代,於是在實際調用發生時,是Derive::f()被調用了,這就實現了多態。
代碼驗證如下
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
/* version: 1.0 author: hellogiser blog: http://www.cnblogs.com/hellogiser date: 2014/9/29 */ #include "stdafx.h" #include <iostream> using namespace std; class Base1 { public : Base1() { } virtual void f() { cout << "Base1::f" << endl; } virtual void g() { cout << "Base1::g" << endl; } virtual void h() { cout << "Base1::h" << endl; } }; class Derived: public Base1 { virtual void f() { cout << "Derived::f" << endl; } virtual void g1() { cout << "Derived::g1" << endl; } virtual void h1() { cout << "Derived::h1" << endl; } }; typedef void (*Fun)( void ); void test() { Derived obj; Base1 *p = &obj; p->f(); p->g(); p->h(); } int main() { test(); return 0 ; } /* Derived::f Base1::g Base1::h */ |
(3)多重繼承(無虛函數覆蓋)
下面,再讓我們來看看多重繼承中的情況,假設有下面這樣一個類的繼承關系。注意:子類並沒有覆蓋父類的函數。
對於子類實例中的虛函數表,是下面這個樣子:
我們可以看到:
1) 每個父類都有自己的虛表,虛表的順序與繼承順序有關。
1
|
class
Derived :
public
Base1,
public
Base2,
public
Base3
|
虛表的順序依次為Base1,Base2,Base3
1
|
class
Derived :
public
Base3,
public
Base2,
public
Base1
|
虛表的順序依次為Base3,Base2,Base1
2) 子類的成員函數被放到了第一個父類的表中。(所謂的第一個父類是按照聲明順序來判斷的)
代碼驗證如下
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
/* version: 1.0 author: hellogiser blog: http://www.cnblogs.com/hellogiser date: 2014/9/29 */ #include "stdafx.h" #include <iostream> using namespace std; class Base1 { public : Base1() { } virtual void f() { cout << "Base1::f" << endl; } virtual void g() { cout << "Base1::g" << endl; } virtual void h() { cout << "Base1::h" << endl; } }; class Base2 { public : Base2() { } virtual void f() { cout << "Base2::f" << endl; } virtual void g() { cout << "Base2::g" << endl; } virtual void h() { cout << "Base2::h" << endl; } }; class Base3 { public : Base3() { } virtual void f() { cout << "Base3::f" << endl; } virtual void g() { cout << "Base3::g" << endl; } virtual void h() { cout << "Base3::h" << endl; } }; class Derived: public Base1, public Base2, public Base3 { virtual void f1() { cout << "Derived::f1" << endl; } virtual void g1() { cout << "Derived::g1" << endl; } }; typedef void (*Fun)( void ); void test() { Derived obj; Fun pFun; int **pVtab = ( int **)(&obj); // base1 pFun = (Fun)pVtab[ 0 ][ 0 ]; pFun(); pFun = (Fun)pVtab[ 0 ][ 1 ]; pFun(); pFun = (Fun)pVtab[ 0 ][ 2 ]; pFun(); //==================================== // derived pFun = (Fun)pVtab[ 0 ][ 3 ]; pFun(); pFun = (Fun)pVtab[ 0 ][ 4 ]; pFun(); //==================================== // base2 pFun = (Fun)pVtab[ 1 ][ 0 ]; pFun(); pFun = (Fun)pVtab[ 1 ][ 1 ]; pFun(); pFun = (Fun)pVtab[ 1 ][ 2 ]; pFun(); // base3 pFun = (Fun)pVtab[ 2 ][ 0 ]; pFun(); pFun = (Fun)pVtab[ 2 ][ 1 ]; pFun(); pFun = (Fun)pVtab[ 2 ][ 2 ]; pFun(); } int main() { test(); return 0 ; } /* Base1::f Base1::g Base1::h Derived::f Derived::g1 Base2::f Base2::g Base2::h Base3::f Base3::g Base3::h */ |
更改一下繼承為Base3,Base2,Base1驗證結果如下
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
/* version: 1.0 author: hellogiser blog: http://www.cnblogs.com/hellogiser date: 2014/9/29 */ #include "stdafx.h" #include <iostream> using namespace std; class Base1 { public : Base1() { } virtual void f() { cout << "Base1::f" << endl; } virtual void g() { cout << "Base1::g" << endl; } virtual void h() { cout << "Base1::h" << endl; } }; class Base2 { public : Base2() { } virtual void f() { cout << "Base2::f" << endl; } virtual void g() { cout << "Base2::g" << endl; } virtual void h() { cout << "Base2::h" << endl; } }; class Base3 { public : Base3() { } virtual void f() { cout << "Base3::f" << endl; } virtual void g() { cout << "Base3::g" << endl; } virtual void h() { cout << "Base3::h" << endl; } }; class Derived: public Base3, public Base2, public Base1 { virtual void f1() { cout << "Derived::f1" << endl; } virtual void g1() { cout << "Derived::g1" << endl; } }; typedef void (*Fun)( void ); void test() { Derived obj; Fun pFun; int **pVtab = ( int **)(&obj); // base3 pFun = (Fun)pVtab[ 0 ][ 0 ]; pFun(); pFun = (Fun)pVtab[ 0 ][ 1 ]; pFun(); pFun = (Fun)pVtab[ 0 ][ 2 ]; pFun(); //==================================== // derived pFun = (Fun)pVtab[ 0 ][ 3 ]; pFun(); pFun = (Fun)pVtab[ 0 ][ 4 ]; pFun(); //==================================== // base2 pFun = (Fun)pVtab[ 1 ][ 0 ]; pFun(); pFun = (Fun)pVtab[ 1 ][ 1 ]; pFun(); pFun = (Fun)pVtab[ 1 ][ 2 ]; pFun(); // base1 pFun = (Fun)pVtab[ 2 ][ 0 ]; pFun(); pFun = (Fun)pVtab[ 2 ][ 1 ]; pFun(); pFun = (Fun)pVtab[ 2 ][ 2 ]; pFun(); } int main() { test(); return 0 ; } /* Base3::f Base3::g Base3::h Derived::f1 Derived::g1 Base2::f Base2::g Base2::h Base1::f Base1::g Base1::h */ |
(4)多重繼承(有虛函數覆蓋)
下圖中,我們在子類中覆蓋了父類的f()函數。
下面是對於子類實例中的虛函數表的圖:
我們可以看見,三個父類虛函數表中的f()的位置被替換成了子類的函數指針。這樣,我們就可以任一靜態類型的父類來指向子類,並調用子類的f()了。如:
1
2 3 4 5 6 7 8 9 10 11 |
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() |
代碼驗證如下
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
/* version: 1.0 author: hellogiser blog: http://www.cnblogs.com/hellogiser date: 2014/9/29 */ #include "stdafx.h" #include <iostream> using namespace std; class Base1 { public : Base1() { } virtual void f() { cout << "Base1::f" << endl; } virtual void g() { cout << "Base1::g" << endl; } virtual void h() { cout << "Base1::h" << endl; } }; class Base2 { public : Base2() { } virtual void f() { cout << "Base2::f" << endl; } virtual void g() { cout << "Base2::g" << endl; } virtual void h() { cout << "Base2::h" << endl; } }; class Base3 { public : Base3() { } virtual void f() { cout << "Base3::f" << endl; } virtual void g() { cout << "Base3::g" << endl; } virtual void h() { cout << "Base3::h" << endl; } }; class Derived: public Base1, public Base2, public Base3 { virtual void f() { cout << "Derived::f" << endl; } virtual void g1() { cout << "Derived::g1" << endl; } }; typedef void (*Fun)( void ); void test() { Derived obj; Fun pFun; int **pVtab = ( int **)(&obj); // base1 pFun = (Fun)pVtab[ 0 ][ 0 ]; pFun(); pFun = (Fun)pVtab[ 0 ][ 1 ]; pFun(); pFun = (Fun)pVtab[ 0 ][ 2 ]; pFun(); pFun = (Fun)pVtab[ 0 ][ 3 ]; pFun(); // base2 pFun = (Fun)pVtab[ 1 ][ 0 ]; pFun(); pFun = (Fun)pVtab[ 1 ][ 1 ]; pFun(); pFun = (Fun)pVtab[ 1 ][ 2 ]; pFun(); // base3 pFun = (Fun)pVtab[ 2 ][ 0 ]; pFun(); pFun = (Fun)pVtab[ 2 ][ 1 ]; pFun(); pFun = (Fun)pVtab[ 2 ][ 2 ]; pFun(); } int main() { test(); return 0 ; } /* Derived::f Base1::g Base1::h Derived::g1 Derived::f Base2::g Base2::h Derived::f Base3::g Base3::h */ |
【參考】