虚函数在类中的体现
对象的多态性需要通过虚表和虚表指针来完成,虚表指针被定义在对象首地址的前4字节处,因此虚函数必须作为成员函数使用。
由于非成员函数没有 this 指针,因此无法获得虚表指针,进而无法获取虚表,也就无法访问虚函数。
子类对虚函数进行了重写,所以如果是子类的指针,是访问不到父类的成员函数的。而父类的指针可以访问到子类的成员函数。
值得注意的是,虽然虚表是共享的,但是虚表指针并不是,类的每一个对象有一个属于它自己的虚表指针。
编译环境:VS2012
- 类中只有普通成员函数:
class CClassA
{
public:
void fun_a(){}
};
- 类中含有虚函数:
class CClassA
{
public:
virtual void fun_a(){}
};
由此可见,有虚函数的多了4个字节,这4个字节就是分给虚函数指针的.
虚函数表的存储机制
一般继承(无虚函数覆盖)
C++代码:
#include <iostream>
using namespace std;
class CClassA
{
public:
virtual void fun_a(){}
virtual void fun_b(){}
virtual void fun_c(){}
};
class CClassB : public CClassA
{
public:
virtual void fun_e(){}
virtual void fun_f(){}
virtual void fun_g(){}
};
int main()
{
CClassA objA;
CClassB objB;
return 0;
}
反汇编代码:
- CClassB 的虚函数表
CONST SEGMENT
??_7CClassB@@6B@ DD FLAT:??_R4CClassB@@6B@ ; CClassB::`vftable'
DD FLAT:?fun_a@CClassA@@UAEXXZ
DD FLAT:?fun_b@CClassA@@UAEXXZ
DD FLAT:?fun_c@CClassA@@UAEXXZ
DD FLAT:?fun_e@CClassB@@UAEXXZ
DD FLAT:?fun_f@CClassB@@UAEXXZ
DD FLAT:?fun_g@CClassB@@UAEXXZ
CONST ENDS
- CClassA 的虚函数表
CONST SEGMENT
??_7CClassA@@6B@ DD FLAT:??_R4CClassA@@6B@ ; CClassA::`vftable'
DD FLAT:?fun_a@CClassA@@UAEXXZ
DD FLAT:?fun_b@CClassA@@UAEXXZ
DD FLAT:?fun_c@CClassA@@UAEXXZ
CONST ENDS
可以看到,虚函数表存放在常量段里(模块的数据段通常存放该模块的全局数据和静态数据,这样我们可以把虚表看作是模块的全局数据或者静态数据),并且 CClassB 继承 CClassA 后,虚函数表中的函数排列顺序为先基类再子类。
也可以在内存中证明排列顺序,如图:
objB 的前三个是 objA 的三个函数
可得objB 的虚函数结构为:
一般继承(有虚函数覆盖)
C++代码:
class CClassA
{
public:
virtual void fun_a(){}
virtual void fun_b(){}
virtual void fun_c(){}
};
class CClassB : public CClassA
{
public:
virtual void fun_a(){} //覆盖基类的fun_a
virtual void fun_f(){}
virtual void fun_g(){}
};
汇编中的虚函数表:
- CClassB: (这里 fun_a 已经不再是 CClassA 的了)
CONST SEGMENT
??_7CClassB@@6B@ DD FLAT:??_R4CClassB@@6B@ ; CClassB::`vftable'
DD FLAT:?fun_a@CClassB@@UAEXXZ
DD FLAT:?fun_b@CClassA@@UAEXXZ
DD FLAT:?fun_c@CClassA@@UAEXXZ
DD FLAT:?fun_f@CClassB@@UAEXXZ
DD FLAT:?fun_g@CClassB@@UAEXXZ
CONST ENDS
- CClassA:
CONST SEGMENT
??_7CClassA@@6B@ DD FLAT:??_R4CClassA@@6B@ ; CClassA::`vftable'
DD FLAT:?fun_a@CClassA@@UAEXXZ
DD FLAT:?fun_b@CClassA@@UAEXXZ
DD FLAT:?fun_c@CClassA@@UAEXXZ
CONST ENDS
在内存中的情况:
第一个函数已经不同,说明基类中的被覆盖
objB 的虚函数结构为:
多重继承(无虚函数覆盖)
C++代码:
class CClassA
{
public:
virtual void fun_a(){}
virtual void fun_b(){}
};
class CClassB
{
public:
virtual void fun_c(){}
virtual void fun_d(){}
};
class CClassC : public CClassA, public CClassB
{
public:
/*virtual void fun_a(){}
virtual void fun_c(){}*/
virtual void fun_e(){}
virtual void fun_f(){}
};
int main()
{
CClassA objA;
CClassB objB;
CClassC objC;
return 0;
}
反汇编各个类的虚函数表:
- CLassC (有两个虚函数表):
可以看到 C 本身 的虚函数被接在第一个继承类 CClassA 的后面
CONST SEGMENT
??_7CClassC@@6BCClassB@@@ DD FLAT:??_R4CClassC@@6BCClassB@@@ ; CClassC::`vftable'
DD FLAT:?fun_c@CClassB@@UAEXXZ
DD FLAT:?fun_d@CClassB@@UAEXXZ
CONST ENDS
CONST SEGMENT
??_7CClassC@@6BCClassA@@@ DD FLAT:??_R4CClassC@@6BCClassA@@@ ; CClassC::`vftable'
DD FLAT:?fun_a@CClassA@@UAEXXZ
DD FLAT:?fun_b@CClassA@@UAEXXZ
DD FLAT:?fun_e@CClassC@@UAEXXZ
DD FLAT:?fun_f@CClassC@@UAEXXZ
CONST ENDS
- CClassB :
CONST SEGMENT
??_7CClassB@@6B@ DD FLAT:??_R4CClassB@@6B@ ; CClassB::`vftable'
DD FLAT:?fun_c@CClassB@@UAEXXZ
DD FLAT:?fun_d@CClassB@@UAEXXZ
CONST ENDS
- CClassA :
CONST SEGMENT
??_7CClassA@@6B@ DD FLAT:??_R4CClassA@@6B@ ; CClassA::`vftable'
DD FLAT:?fun_a@CClassA@@UAEXXZ
DD FLAT:?fun_b@CClassA@@UAEXXZ
CONST ENDS
在内存中查看:
objC 的总体情况(两个虚函数指针 vfptr):
- objC 的内存:
第一个虚函数表指针 0036CD58H:
其中4个地址的内容分别是:(这里也可以看出 C 的虚函数跟在 A 的后面)
第二个虚函数表指针 0036CD74H:
其中2个地址的内容分别是:(这里就是第二个继承类的虚函数)
SO , CClassC 的虚表结构:
多重继承(有虚函数覆盖)
C++代码:
class CClassA
{
public:
virtual void fun_a(){}
virtual void fun_b(){}
};
class CClassB
{
public:
virtual void fun_c(){}
virtual void fun_d(){}
};
class CClassC : public CClassA, public CClassB
{
public:
virtual void fun_a(){}
virtual void fun_c(){}
virtual void fun_e(){}
virtual void fun_f(){}
};
int main()
{
CClassA objA;
CClassB objB;
CClassC objC;
return 0;
}
反汇编虚函数表:
- CClassC:
CONST SEGMENT
??_7CClassC@@6BCClassB@@@ DD FLAT:??_R4CClassC@@6BCClassB@@@ ; CClassC::`vftable'
DD FLAT:?fun_c@CClassC@@UAEXXZ
DD FLAT:?fun_d@CClassB@@UAEXXZ
CONST ENDS
CONST SEGMENT
??_7CClassC@@6BCClassA@@@ DD FLAT:??_R4CClassC@@6BCClassA@@@ ; CClassC::`vftable'
DD FLAT:?fun_a@CClassC@@UAEXXZ
DD FLAT:?fun_b@CClassA@@UAEXXZ
DD FLAT:?fun_e@CClassC@@UAEXXZ
DD FLAT:?fun_f@CClassC@@UAEXXZ
CONST ENDS
- CClassB:
CONST SEGMENT
??_7CClassB@@6B@ DD FLAT:??_R4CClassB@@6B@ ; CClassB::`vftable'
DD FLAT:?fun_c@CClassB@@UAEXXZ
DD FLAT:?fun_d@CClassB@@UAEXXZ
CONST ENDS
- CClassA:
CONST SEGMENT
??_7CClassA@@6B@ DD FLAT:??_R4CClassA@@6B@ ; CClassA::`vftable'
DD FLAT:?fun_a@CClassA@@UAEXXZ
DD FLAT:?fun_b@CClassA@@UAEXXZ
CONST ENDS
在内存中查看:
objC 的内存:
第一个虚表:
对应的函数:
第二个虚表内容:
对应的函数:
SO , CClassC 的虚表结构:
值得注意的是,如果基类中有数据成员被继承,那么 CClassC 的对象的模型应该是这样的: