考点:“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 |
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::foo1() |
Base1::foo2() |
Base1::foo3() |
Derived3::fcc1() |
Derived3::fcc2() |
NULL |
Vfptr |
Vfptr |
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 |
Derived4::foo1() |
Base1::foo2() |
Base1::foo3() |
Derived4::fdd() |
NULL |
Vfptr |
Vfptr |
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