您的位置:首页 > 编程语言 > C语言/C++

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

2009-10-28 14:00 726 查看
虚函数在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,不要问我,Microsoft
Visual Studio这么说的


)怎么是4咧,细心的童鞋可能已经发现,刚刚谈到了class A还有一个隐藏的成员指针变量vfptr。因此,class B的size是12就不足为奇了。ok就这样吧,鄙人不才,有不对之处请各位童鞋多多指正。谢谢

参考: 1. http://c.chinaitlab.com/cc/basic/200908/790967.html

2.以前看过的N多书
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: