c++ 虚函数实现原理简单剖析

来源:互联网 发布:c语言管理系统源代码 编辑:程序博客网 时间:2024/05/29 04:53

  虚函数在C++里的作用是在是非常非常的大,很多讲述C++的文章都会讲到它,要用好C++,就一定要学好虚函数。本文对虚函数的一些实现机制,以及C++对象布局做一下探索。
  虚函数的作用是实现动态联编,也就是在程序的运行阶段动态地选择合适的成员函数,在定义了虚函数后,可以在基类的派生类中对虚函数重新定义,在派生类中重新定义的函数应与虚函数具有相同的形参个数和形参类型。以实现统一的接口,不同定义过程。如果在派生类中没有对虚函数重新定义,则它继承其基类的虚函数。那么,这个动态联编是怎么实现的呢?
下面看一个简单例子:
class AA{
public:
  virtual void fun(){printf("In AA");}
  virtual void fun1(){printf("In AA1");}
};
class BB :public AA{
public:
    int i,j;

    void fun(){printf("In BB");return 0;}
    void ff(){};
};
class CC :public AA{
public:
    void fun1(){printf("In CC1");return 0;}
    void fun(){printf("In CC");return 0;}
};

首先要明确的是,在有虚函数的类中,有一个隐藏的成员指针变量vfptr,虚函数的神秘之处就在于这个东西。这是一个指针,它是指向虚函数表的指针,好啦,又出现了一个新词,虚函数表,它就是存放虚函数地址的一张表,(不用关心它的数据结构是咋样的)。可以通过这个虚函数表找到虚函数的入口地址。ok,名词解释完毕.(我有点废).
总的说来就是,在有虚函数的类中,一定会有一个虚函数表指针vfptr,这个vfptr指针会放在类的起始处,虚函数表里会按基类声明虚函数的顺序在vfptr里存放虚函数的地址,虚函数表里存放的是虚函数的地址,是具体子类的实现函数的地址。调用虚函数的时候,是从vfptr所指的函数表里获取到函数地址,然后才调用具体的代码。
当把一个继承类的指针赋值给基类指针时,基类的指针就指向了继承类的内存,此时的vfptr就是继承类的vfptr啦。因此,通过基类指针调用虚函数的时候就找到的是继承类的虚函数地址。调用的式继承类的虚函数。如果继承类没有重载基类的某个虚函数,如class BB,则继承类就是直接继承的基类的那个虚函数。
ok,下面来看一下类内存布局,使用Microsoft Visual Studio的cl命令可以简单看到类的内存布局。
即:使用命令 cl  demo.cpp  /d1reportSingleClassLayoutCC (CC为你想查看的类名)可查看类的内存布局。

////////////class AA memory layout////////////////////
class AA size(4):
        +---
0      | {vfptr}
        +---

AA::$vftable@:
        | &A_meta
        |  0
0      | &AA::fun
1      | &AA::fun1

AA::fun this adjustor: 0
AA::fun1 this adjustor: 0
////////////class BB memory layout////////////////////
class BB        size(12):
        +---
        | +--- (base class
0      | | {vfptr}
        | +---
4      | i
8      | j
        +---

BB::$vftable@:
        | &BB_meta
        |  0
0      | &BB::fun
1      | &AA::fun1

BB::fun this adjustor: 0
/////////////////////////class c memory layout////////////////////

class CC        size(4):
        +---
        | +--- (base class
0      | | {vfptr}
        | +---
        +---

CC::$vftable@:
        | &CC_meta
        |  0
0      | &CC::fun
1      | &CC::fun1

CC::fun1 this adjustor: 0
CC::fun this adjustor: 0


综上可知,类的虚函数表的结构和其基类的虚函数表结构一致。(顺序一致
如果继承类没有重载某个基类的虚函数,
则在继承类的虚函数表中依然会保存其基类的那个虚函数的地址。它是直接继承基类的虚函数。非虚函数不出现在类的内存布局里。
注意每个类后面的size(*);这个东西。这个是说的是类占用多少内存,即使用sizeof(类变量)得到的结果就是这个size。我们来看看,class AA的size是4,咦,明明该是1(如果没有虚函数的类中没有成员变量sizeof结果就是1,不要问我,MicrosoftVisual Studio这么说的)怎么是4咧,细心的童鞋可能已经发现,刚刚谈到了class A还有一个隐藏的成员指针变量vfptr。因此,class B的size是12就不足为奇了。ok就这样吧,鄙人不才,有不对之处请各位童鞋多多指正。谢谢

参考: 1. http://c.chinaitlab.com/cc/basic/200908/790967.html
          2.以前看过的N多书

原创粉丝点击