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多书
虚函数的作用是实现动态联编,也就是在程序的运行阶段动态地选择合适的成员函数,在定义了虚函数后,可以在基类的派生类中对虚函数重新定义,在派生类中重
新定义的函数应与虚函数具有相同的形参个数和形参类型。以实现统一的接口,不同定义过程。如果在派生类中没有对虚函数重新定义,则它继承其基类的虚函数。
那么,这个动态联编是怎么实现的呢?
下面看一个简单例子:
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多书
相关文章推荐
- 【C++拾遗】 C++虚函数实现原理
- C++虚函数的底层实现原理
- 欧几里得算法(辗转求余法)的C++实现及简单原理
- 剖析C++多态:用C实现简单多态
- 简单实现一个进度条并剖析原理
- 从 Arm 汇编看 Android C++虚函数实现原理
- C++多态,虚函数作用及底层实现原理
- c++类型兼容规则与虚函数实现多态的实现原理和区别
- C++ 虚函数实现原理
- c++智能指针的原理与简单实现
- 虚函数的实现原理--c++虚函数表解析
- RetargetAction实现原理简单代码剖析
- C++虚函数的原理及实现
- 【深入剖析C++系列】虚函数的实现机制
- C++智能指针原理分析与简单实现
- c++ 虚函数实现多态的原理
- (转载)【C++拾遗】 C++虚函数实现原理
- 【C++拾遗】 C++虚函数实现原理
- c++虚函数原理及实现(转载)
- 简述C++虚函数作用及底层实现原理