黑白程式

黑白程式

导航

虚函数表(转)

考点:“DLL HELL”的了解
出现频率:★★
答案:
“DLL HELL”主要是指DLL(动态链接库)版本冲突的问题。一般情况下DLL新版本会覆盖旧版本,那么原来使用旧版本的dll的应用程序就会不能继续正常工作了。
 
扩展知识:虚函数表
大家知道虚函数(Virtual Function)是通过一张虚函数表(Virtual Table)来实现的。在这个表中,主要是一个类的虚函数的地址表,这张表解决了继承、覆盖的问题,其内容真实反应实际的函数。这样,在有虚函数的类的实例中这个表被分配在了这个实例的内存中,所以,当用父类的指针来操作一个子类的时候,这张虚函数表就显得尤为重要了,它就像一个地图一样,指明了实际所应该调用的函数。
在C++的标准规格说明书中说到,编译器必须保证虚函数表的指针存在于对象实例中最前面的位置(这是为了保证正确取到虚函数的偏移量)。这意味着通过对象实例的地址得到这张虚函数表,然后就可以遍历其中函数指针,并调用相应的函数。请看下面程序例子:
1       #include <iostream>
2       using namespace std;
3      
4       class Base
5       {
6       public:
7                virtual void fun1() {cout << "Base::fun1" << endl;}
8                virtual void fun2() {cout << "Base::fun2" << endl;}
9                virtual void fun3() {cout << "Base::fun3" << endl;}
10     private:
11              int num1;
12              int num2;
13     };
14    
15     typedef void (*Fun)(void);
16    
17     int main()
18     {
19              Base b;
20              Fun pFun;
21             
22              pFun = (Fun)*((int*)*(int*)(&b)+0);      //取得Base::fun1()地址
23              pFun();                             //执行Base::fun1()
24              pFun = (Fun)*((int*)*(int*)(&b)+1);      //取得Base::fun1()地址
25              pFun();                             //执行Base::fun2()
26              pFun = (Fun)*((int*)*(int*)(&b)+2);      //取得Base::fun1()地址
27              pFun();                             //执行Base::fun3()
28    
29              return 0;
30     }
上面程序的执行结果如下:
1       Base::fun1
2       Base::fun2
3       Base::fun3
可以看到通过函数指针pFun的调用,分别执行了对象b的三个虚函数。通过这个示例发现,可以通过强行把&b转成int *,取得虚函数表的地址,然后再次取址就可以得到第一个虚函数的地址了,也就是Base::fun1()。如果要调用Base::fun2()和Base::fun3(),只需要把&b先加上数组元素的偏移,后面的步骤类似就可以了。
程序中的Base对象b内存结构图,如图9.3所示。

&bp

vfptr
num1
num2
 
 

Base::fun1()      Base::fun2()     Base::fun3()     NULL

图9.3  Base虚函数表图
一个类会有多少张虚函数表呢?
对于一个单继承的类,如果它有虚拟函数,则只有一张虚函数表。对于多重继承的类,它可能有多张虚函数表。
考虑下面代码中的各个类的定义:
1       #include <iostream>
2       using namespace std;
3      
4       class Base1
5       {
6       public:
7                Base1(int num) : num_1(num) {}
8                virtual void foo1() {cout << "Base1::foo1 " << num_1 << endl;}
9                virtual void foo2() {cout << "Base1::foo2 " << num_1 << endl;}
10              virtual void foo3() {cout << "Base1::foo3 " << num_1 << endl;}
11     private:
12              int num_1;
13     };
14    
15     class Base2
16     {
17     public:
18              Base2(int num) : num_2(num) {}
19              virtual void foo1() {cout << "Base2::foo1 " <<num_2 << endl;}
20              virtual void foo2() {cout << "Base2::foo2 " <<num_2 << endl;}
21              virtual void foo3() {cout << "Base2::foo3 " <<num_2 << endl;}
22     private:
23              int num_2;
24     };
25    
26     class Base3
27     {
28     public:
29              Base3(int num) : num_3(num) {}
30              virtual void foo1() {cout << "Base3::foo1 " << num_3 << endl;}
31              virtual void foo2() {cout << "Base3::foo2 " << num_3 << endl;}
32              virtual void foo3() {cout << "Base3::foo3 " << num_3 << endl;}
33     private:
34              int num_3;
35     };
36    
37     class Derived1 : public Base1
38     {
39     public:
40              Derived1(int num) : Base1(num) {}
41              virtual void faa1() {cout << "Derived1::faa1" << endl;}    //无覆盖
42              virtual void faa2() {cout << "Derived1::faa2" << endl;}
43     };
44    
45     class Derived2 : public Base1
46     {
47     public:
48              Derived2(int num) : Base1(num) {}
49              virtual void foo2() {cout << "Derived2::foo2" << endl;}    //只覆盖了Base1::foo2
50              virtual void fbb2() {cout << "Derived2::fbb2" << endl;}
51              virtual void fbb3() {cout << "Derived2::fbb3" << endl;}
52     };
53    
54     class Derived3 : public Base1, public Base2, public Base3   //多重继承,无覆盖
55     {
56     public:
57              Derived3(int num_1, int num_2, int num_3) :
58                       Base1(num_1), Base2(num_2), Base3(num_3) {}
59              virtual void fcc1() {cout << "Derived3::fcc1" << endl;}
60              virtual void fcc2() {cout << "Derived3::fcc2" << endl;}
61     };
62    
63     class Derived4 : public Base1, public Base2, public Base3    //多重继承,有覆盖
64     {
65     public:
66              Derived4(int num_1, int num_2, int num_3) :
67                       Base1(num_1), Base2(num_2), Base3(num_3) {}
68              virtual void foo1() {cout << "Derived4::foo1" << endl;}    //覆盖了Base1::foo1,
69                                                          //Base2::foo1, Base3::foo1
70              virtual void fdd() {cout << "Derived4::frr" << endl;}
71     };
这个例子说明了四种继承情况下的虚函数表。
(1)一般继承(无虚函数覆盖):Derived1类继承自Base1类,Derived1的虚函数表如图9.4所示。

vfptr

Base1::foo1()

Base1::foo2()

Base1::foo3()

NULL

图9.4  Derived1虚函数表图
Derived1类内没有任何覆盖基类Base1的函数,因此两个虚拟函数faa1()和faa2()被依次添加到了虚函数表的末尾。
(2)一般继承(有虚函数覆盖):Derived2类继承自Base1类,并对Base1类中的虚函数foo2()进行了覆盖。Derived2的虚函数表如图9.5所示。

vfptr

Base1::faa3()

Derived2::fbb2()

Derived2::fbb3()

NULL

Derived2::foo2()

Base1::foo1()

图9.5  Derived2虚函数表图
Derived2覆盖了基类Base1的faa1(),因此其虚函数表中Derived2::foo2()替换了Base1::foo2()一项,fbb2()和fbb3()被依次添加到了虚函数表的末尾。
(3)多重继承(无虚函数覆盖):Derived3类继承自Base1类、Base2类、Base3类,Derived3的虚函数表如图9.6所示。

Vfptr
(Base1)

Base1::foo1()

Base1::foo2()

Base1::foo3()

Derived3::fcc1()

Derived3::fcc2()
 

NULL

Vfptr
(Base2)

Vfptr
(Base3)

Base2::foo1()

Base2::foo2()

Base2::foo3()

NULL

Base3::foo3()

NULL

Base3::foo1()

Base3::foo2()

图9.6  Derived3虚函数表图
每个父类都有自己的虚表,Derived3也就有了三个虚表,并且成员函数fcc1()和fcc2()被放到了第一个父类(Base1)的表中,这里所谓的第一个父类是按照声明顺序来判断的。这样做就是为了解决不同的父类类型的指针指向同一个子类实例,而能够调用到实际的函数。例如:
1       Base2 *pBase2 = new Derived3();
2       pBase2->foo2();   //调用Base2::foo2()
把Base2类型的指针指向Derive3实例,那么调用将是对应Base2虚表里的那些函数。
(4)多重继承(有虚函数覆盖):Derived4类继承自Base1类、Base2类、Base3类,并对Base1类的foo1()、Base2类的foo1()、Base3类的foo1()都进行了覆盖。Derived4的虚函数表如图9.7所示。
 

Vfptr
(Base1)

Derived4::foo1()

Base1::foo2()

Base1::foo3()

Derived4::fdd()

NULL

Vfptr
(Base2)

Vfptr
(Base3)

Derived4::foo1()

Base2::foo2()

Base2::foo3()

NULL

Base3::foo3()

NULL

Derived4::foo1()

Base3::foo2()

图9.7  Derived4虚函数表图
可以看见,Base1::foo1()、Base2::foo1()和Base3::foo1()都被替换成了Derived::foo1()。这样,我们就可以把任一个静态类型的父类来指向子类,并调用子类的f()了。如:
1       Base1*pBase1 = new Derived4();
2       pBase1->foo1();             //调用从Base1继承的虚表中的Derived4::foo1()
下面是我们所讨论的四种继承情况下的测试代码:
1       int main()
2       {
3                Base1 *pBase1 = NULL;
4                Base2 *pBase2 = NULL;
5                Base3 *pBase3 = NULL;
6      
7                cout << "-----  一般继承自Base1,无覆盖  ----------" << endl;
8                Derived1 d1(1);        //Derived1一般继承自Base1,无覆盖
9                pBase1 = &d1;   
10              pBase1->foo1(); //执行Base1::foo1(); 
11    
12              cout << "-----  一般继承自Base1,覆盖foo2()  ---------" << endl;
13              Derived2 d2(2);        //Derived2一般继承自Base1,覆盖了Base1::foo2()
14              pBase1 = &d2;
15              pBase1->foo2(); //执行Derived2::foo2();
16    
17              cout << "-----  多重继承,无覆盖  ----------" << endl;
18              Derived3 d3(1, 2, 3);    //Derived3多重继承自Base1,Base2,Base3,没有覆盖
19              pBase1 = &d3;
20              pBase2 = &d3;
21              pBase3 = &d3;
22              pBase1->foo1();  //执行Base1::foo1();
23              pBase2->foo1();  //执行Base2::foo1();
24              pBase3->foo1();  //执行Base3::foo1();
25             
26              cout << "------  多重继承,覆盖foo1()  ---------" << endl;
27              Derived4 d4(1, 2, 3);    //Derived4多重继承自Base1,Base2,Base3,覆盖foo1()
28              pBase1 = &d4;
29              pBase2 = &d4;
30              pBase3 = &d4;
31              pBase1->foo1();        //执行Derived4::foo1();
32              pBase2->foo1();        //执行Derived4::foo1();
33              pBase3->foo1();        //执行Derived4::foo1();
34              return 0;
35     }
测试结果如下:
1           一般继承自Base1,无覆盖  ----------
2       Base1::foo1 1
3       一般继承自Base1,覆盖foo2()
4       Derived2::foo2
5       -----  多重继承,无覆盖  ----------
6       Base1::foo1 1
7       Base2::foo1 2
8       Base3::foo3 3
9       ------  多重继承,覆盖foo1()  ---------
10     Derived4::foo1
11     Derived4::foo1
12     Derived4::foo1

posted on 2009-05-31 21:23  黑白程式  阅读(156)  评论(0编辑  收藏  举报