C++虚函数与虚函数表

一、多态是C++三大特性之一,也是面向对象设计中一个很是重要的概念。所谓多态性就是当不一样的html

对象接收到相同的消息时所产生的不一样的响应。ios

C++中虚函数的存在其实就是为了解决面向对象编程设计当中的多态问题,即经过基类的指针(或者是引用)编程

指向实例化的派生类对象,从而经过基类的指针(或者是引用)调用派生类的成员函数,从而实现晚绑定(函数

不在编译时根据函数名和参数来肯定调用哪个函数,而是在运行时根据具体的环境来动态的肯定)。spa

正是因为虚函数这种机制的存在,把基类的析构函数申明为虚析构函数就可以解决经过释放基类指针所形成的设计

内存泄露的问题。指针

 

二、没有继承关系时的虚函数表code

 1 #include <iostream>
 2 
 3 class CBassClass {
 4 public:
 5     virtual void FunC1(void) { std::cout << "FunC1(void)" << std::endl; }
 6     virtual void FunC2(void) { std::cout << "FunC2(void)" << std::endl; }
 7     virtual void FunC3(void) { std::cout << "FunC3(void)" << std::endl; }
 8 };
 9 
10 typedef void (*pfnFun) (void);
11 
12 int main(void)
13 {    
14     CBassClass *pBase = new CBassClass;
15 
16     std::cout << "虚函数表地址:0x" << (int *)pBase << std::endl;          // 虚函数表地址存在对象所占内存空间的前4个字节
17     std::cout << "虚函数表中的第一个函数地址:0x" << *((int *)(*(int *)pBase) + 0) << std::endl;
18     std::cout << "虚函数表中的第二个函数地址:0x" << *((int *)(*(int *)pBase) + 1) << std::endl;
19     std::cout << "虚函数表中的第三个函数地址:0x" << *((int *)(*(int *)pBase) + 2) << std::endl;
20     std::cout << "虚函数表中的结束标志:0x" << *((int *)(*(int *)pBase) + 3) << std::endl;
21 
22     pfnFun fn = NULL;
23     for (int i = 0; i < 3; i++) {
24         fn = (pfnFun)*((int *)(*(int *)pBase) + i);
25         (*fn)();
26     }
27 
28     delete pBase;
29     return 0;
30 }

以上代码的输出结果:htm

   

从结果能够看出来,虚函数的入口地址在虚函数表当中是按照顺序依次存放的,最后会以0做为结束标志。对象

 

三、存在继承关系的虚函数表(不存在覆盖关系时)

 1 #include <iostream>
 2 
 3 class CBassClass {
 4 public:
 5     virtual void FunC1(void) { std::cout << "CBassClass FunC1(void)" << std::endl; }
 6     virtual void FunC2(void) { std::cout << "CBassClass FunC2(void)" << std::endl; }
 7     virtual void FunC3(void) { std::cout << "CBassClass FunC3(void)" << std::endl; }
 8 };
 9 
10 class DClass :public CBassClass {
11 public:
12     virtual void FunC4(void) { std::cout << "DClass FunC4(void)" << std::endl; }
13     virtual void FunC5(void) { std::cout << "DClass FunC5(void)" << std::endl; }
14 };
15 
16 typedef void (*pfnFun) (void);
17 
18 int main(void)
19 {    
20     CBassClass *pBase = new DClass;    // DClass *pBase = new DClass; 结果同样
21 
22     std::cout << "虚函数表地址:0x" << (int *)pBase << std::endl;    // 虚函数表地址存在对象所占内存空间的前4个字节
23     std::cout << "虚函数表中的第一个函数地址:0x" << *((int *)(*(int *)pBase) + 0) << std::endl;
24     std::cout << "虚函数表中的第二个函数地址:0x" << *((int *)(*(int *)pBase) + 1) << std::endl;
25     std::cout << "虚函数表中的第三个函数地址:0x" << *((int *)(*(int *)pBase) + 2) << std::endl;
26     std::cout << "虚函数表中的第四个函数地址:0x" << *((int *)(*(int *)pBase) + 3) << std::endl;
27     std::cout << "虚函数表中的第五个函数地址:0x" << *((int *)(*(int *)pBase) + 4) << std::endl;
28     std::cout << "虚函数表中的结束标志:0x" << *((int *)(*(int *)pBase) + 5) << std::endl;
29 
30     pfnFun fn = NULL;
31     for (int i = 0; i < 5; i++) {
32         fn = (pfnFun)*((int *)(*(int *)pBase) + i);
33         (*fn)();
34     }
35 
36     delete pBase;
37 
38     return 0;
39 }

以上代码运行的结果:

 1 虚函数表地址:0x0052B1C0
 2 虚函数表中的第一个函数地址:0x16520113
 3 虚函数表中的第二个函数地址:0x16520378
 4 虚函数表中的第三个函数地址:0x16520383
 5 虚函数表中的第四个函数地址:0x16520423
 6 虚函数表中的第五个函数地址:0x16520418
 7 虚函数表中的结束标志:0x0
 8 CBassClass FunC1(void)
 9 CBassClass FunC2(void)
10 CBassClass FunC3(void)
11 DClass FunC4(void)
12 DClass FunC5(void)

从上面的结果能够看出来,虚函数表中的函数地址是按顺序存放的,派生类继承了基类的虚函数表,而后

又在虚函数表中将本身的虚函数的函数地址存放在虚函数表中,先存放基类的虚函数后存放派生类的虚函数。

 

四、存在继承关系的虚函数表(存在覆盖关系时)

 1 #include <iostream>
 2 
 3 class CBassClass {
 4 public:
 5     virtual void FunC1(void) { std::cout << "CBassClass FunC1(void)" << std::endl; }
 6     virtual void FunC2(void) { std::cout << "CBassClass FunC2(void)" << std::endl; }
 7     virtual void FunC3(void) { std::cout << "CBassClass FunC3(void)" << std::endl; }
 8 };
 9 
10 class DClass :public CBassClass {
11 public:
12     virtual void FunC2(void) { std::cout << "DClass FunC2(void)" << std::endl; }
13     virtual void FunC3(void) { std::cout << "DClass FunC3(void)" << std::endl; }
14     virtual void FunC4(void) { std::cout << "DClass FunC4(void)" << std::endl; }
15     virtual void FunC5(void) { std::cout << "DClass FunC5(void)" << std::endl; }
16 };
17 
18 typedef void (*pfnFun) (void);
19 
20 
21 int main(void)
22 {    
23     CBassClass *pBase = new DClass;
24 
25     std::cout << "虚函数表地址:0x" << (int *)pBase << std::endl;    // 虚函数表地址存在对象所占内存空间的前4个字节
26     std::cout << "虚函数表中的第一个函数地址:0x" << *((int *)(*(int *)pBase) + 0) << std::endl;
27     std::cout << "虚函数表中的第二个函数地址:0x" << *((int *)(*(int *)pBase) + 1) << std::endl;
28     std::cout << "虚函数表中的第三个函数地址:0x" << *((int *)(*(int *)pBase) + 2) << std::endl;
29     std::cout << "虚函数表中的第四个函数地址:0x" << *((int *)(*(int *)pBase) + 3) << std::endl;
30     std::cout << "虚函数表中的第五个函数地址:0x" << *((int *)(*(int *)pBase) + 4) << std::endl;
31     std::cout << "虚函数表中的结束标志:0x" << *((int *)(*(int *)pBase) + 5) << std::endl;
32 
33     pfnFun fn = NULL;
34     for (int i = 0; i < 5; i++) {
35         fn = (pfnFun)*((int *)(*(int *)pBase) + i);
36         (*fn)();
37     }
38 
39     delete pBase;
40 
41     return 0;
42 }

以上代码运行的结果是:

 1 虚函数表地址:0x006CB1C0
 2 虚函数表中的第一个函数地址:0x267185
 3 虚函数表中的第一个函数地址:0x267510
 4 虚函数表中的第一个函数地址:0x267505
 5 虚函数表中的第一个函数地址:0x267495
 6 虚函数表中的第一个函数地址:0x267490
 7 CBassClass FunC1(void)
 8 DClass FunC2(void)
 9 DClass FunC3(void)
10 DClass FunC4(void)
11 DClass FunC5(void)

从结果能够看出来,派生类中重写了基类中的两个虚函数,派生类继承了基类的虚函数表以后,将虚函数表中

相应的两个函数指针替换成了派生类本身的虚函数。其余的却是没什么变化。

 

五、多继承状况

示例代码就不拷贝出来了,总之就是多重继承的状况,会为每个基类建一个虚函数表。派生类的虚函数放到

第一个虚函数表的后面。

具体的请看这篇博客:  http://www.cnblogs.com/chinazhangjie/archive/2012/07/11/2586535.html 

相关文章
相关标签/搜索