c++ 虛函數表解析


【本文鏈接】

http://www.cnblogs.com/hellogiser/p/virtual-function-table.html

【分析】

  對C++ 了解的人都應該知道虛函數(Virtual Function)是通過一張虛函數表(Virtual Table)來實現的。簡稱為V-Table。在這個表中,主是要一個類的虛函數的地址表,這張表解決了繼承、覆蓋的問題,保證其內容真實反應實際的函數。這樣,在有虛函數的類的實例中這個表被分配在了這個實例的內存中,所以,當我們用父類的指針來操作一個子類的時候,這張虛函數表就顯得由為重要了,它就像一個地圖一樣,指明了實際所應該調用的函數。

  這里我們着重看一下這張虛函數表。C++的編譯器應該是保證虛函數表的指針存在於對象實例中最前面的位置(這是為了保證取到虛函數表的有最高的性能——如果有多層繼承或是多重繼承的情況下)。 這意味着我們通過對象實例的地址得到這張虛函數表,然后就可以遍歷其中函數指針,並調用相應的函數。

  可以將虛函數表看做一個二維數組,其中每個一維數組的首地址存儲在對象的實例之中。

   來看個例子,通過實例得到虛函數表地址,進而訪問虛函數。

 C++ Code 
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)父類的虛函數在子類的虛函數前面。

代碼驗證如下

 C++ Code 
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)沒有被覆蓋的函數依舊。

代碼驗證如下

 C++ Code 
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
*/

這樣,我們就可以看到對於下面這樣的程序,

 C++ Code 
1
2
 
Base *b =  new  Derived();
b->f();

由b所指的內存中的虛函數表的f()的位置已經被Derive::f()函數地址所取代,於是在實際調用發生時,是Derive::f()被調用了,這就實現了多態。

代碼驗證如下

 C++ Code 
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) 每個父類都有自己的虛表,虛表的順序與繼承順序有關。

 C++ Code 
1
 
class  Derived :  public  Base1,  public  Base2,  public  Base3

虛表的順序依次為Base1,Base2,Base3

 C++ Code 
1
 
class  Derived :  public  Base3,  public  Base2,  public  Base1

虛表的順序依次為Base3,Base2,Base1

2) 子類的成員函數被放到了第一個父類的表中。(所謂的第一個父類是按照聲明順序來判斷的)

代碼驗證如下

 C++ Code 
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驗證結果如下

 C++ Code 
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()了。如:

 C++ Code 
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()

代碼驗證如下

 C++ Code 
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
*/

【參考】

http://blog.csdn.net/haoel/article/details/1948051


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM