虚函数的内存布局
一个拥有虚函数的类内部会有一个成员变量vptr,一个四字节大小的指针,指向虚函数表,虚函数表中记录了该类的各个虚函数的入口地址,如果该类重载了继承的虚函数,那么就存放自己的虚函数地址,否则就是父类的虚函数地址。
class A
{
public:
virtual void f(){};
virtual ~A(){};
};
class B:public A
{
void f(){int i=0;};
};
A* pA=new B();
pA->f();
对于f的调用操作编译器有如下动作:
void B::f()函数解释为void f(B* this);
pA->f()解释为 (*pA->vptr[0])(this);//0是f函数在虚拟函数表格中的索引
所以我们可以看出,虽然指针pA静态类型为A类的指针,但是对于f的调用是依赖于B对象内部的vptr指向的虚拟函数表,而此时函数表内的f函数已经是B类的重载版本,因此这就构成了运行时多态,这个c++的基本特征。
虚继承的内存布局,摘自《c++对话系列》。
class parent { /* whatever */ };
class child1 : public virtual parent { /* whatever */ };
class child2 : public virtual parent { /* whatever */ };
class multi : public child1, public child2 { /* whatever */ };
parent::vptr
|
parent data
|
child1::vptr
|
child1 data
|
child2::vptr
|
child2 data
|
multi::vptr
|
multi data
|
在这种复杂的情况下,最底层派生对象内部拥有三个vptr,指向三个虚函数表。
注意,经过我的实验,这种内存布局各种编译器表现不一样,比如vc就是先把child1放在最前面,然后是child2,最后是parent,并且至少vc中,我们可以通过调用static_cast获得各个类型的vptr值,这种典型的应用是在com的QueryInterface函数的实现里面。
经过上面的分析,我知道上面的虚继承会带来多个虚函数表以及多个vptr,这是内存上的额外的开销,当然避免了多个顶级父类的内存副本和模棱两可的继承,也有它的好处。
我们可以看看com里面常常出现的多继承带来的对象内存布局
class CPenguin : public IBird, public ISnappyDresser {...};
IBird和ISnappyDresser接口都继承自IUnknown接口,内存布局如下图:
纯虚函数和虚函数的区别
参考 <<Effective C++>> 3th Edition,这里作个简单的概括
纯虚函数分为函数定义和没有函数定义----
没有函数定义的纯虚函数目的是为了让子类继承接口,强制子类实现该函数;
有函数定义的纯虚函数目的是为了让子类继承接口,而父类的实现函数必须在子类重载函数中手动调用
非纯虚函数目的是让子类自动的继承接口和函数实现
虚函数与访问权限
虚函数的重载机制和访问权限是相互独立的,互相没有干扰,但是可以结合使用。
一个private权限的虚函数可以被子类重载,但是子类不能访问父类的虚函数,但是父类可以通过运行时多态的方式来调用子类重载后的虚函数。
一个protected权限的虚函数可以被子类重载,子类也可以访问父类的虚函数
一个public权限的虚函数可以被重载,子类也可以访问
虚函数与设计模式
虚函数和template method模式的结合
这也被称为NVI(non virtual interface)手法。类只暴露非虚函数f,同时提供一个保护或者私有的d_f虚函数,在f函数的视线中调用do_f。派生类重载do_f从而提供自己的独特功能。
Template Method模式的好处就是提供统一的调用框架,在f函数里面,而交给派生类自己订制的行为。比如f函数里面,可以在调用do_f之前与之后添加一些代码,执行内存分配和清除动作。
虚拟函数应该和数据成员一样对待----让他们成为私有的,除非设计需求表明应该有较少的限制。提升它们到更高存取级别比把它们降到更私有的级别更容易些。
分享到:
相关推荐
1. 从存储空间角度,虚函数对应一个指向vtable虚函数表的指针,这大家都知道,可是这个指向vtable的指针其实是存储在对象的内存空间的。问题出来了,如果构造函数是虚的,就需要通过 vtable来调用,可是对象还没有...
学习 C++ 的同志不知道有没有和我一样遇到过这样的困惑:C++中的虚函数到底怎么实现的?在各种继承关系中,虚函数表的结构到底是什么样的?曾经我是很想当然,可是后来在使用ATL的过程中,我发现并不是我想的那样。...
构造函数不能声明为虚函数,析构函数可以声明为虚函数。
虚函数表中虚函数的分布情况;其中包括发生继承的情况下虚函数表中虚函数的分布情况;
高质量的C++多态讲解,详细讲解虚函数,虚函数表,虚函数继承,虚函数继承下的内存分配等
虚函数表 对C++ 了解的人都应该知道虚函数(Virtual Function)是通过一张虚函数表(Virtual Table)来实现的。简称为V-Table。 在这个表中,主是要一个类的虚函数的地址表,这张表解决了继承、覆盖的问题,保证其容...
C++虚函数表的测试代码,用于学习C++虚函数的调用关系。
2、熟练掌握虚函数的作用及其使用方法。 3、掌握静态关联和动态关联的概念和用法。 4、理解纯虚函数和抽象类的概念和用法。 (二)实验内容 1、定义一个类A,在A中有两个私有的整型变量a和b,定义构造函数对a和b进行...
c++虚函数.C++中的虚函数的作用主要是实现了多态的机制。
个类如果有虚函数,不管是几个虚函数,都会为这个类声明一个虚函数表,这个虚表是一个含有虚函数的类的,不是说是类对象的。一个含有虚函数的类,不管有多少个数据成员,每个数据成员都有一个虚指针,在内存中,存放...
C++中的虚函数的作用主要是实现了多态的机制。关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际 子类的成员函数。
V6.0验证源码 ,分析虚函数的实现过程 ,强制转换的实质 ,多种指针用法,如果完全看懂了相信你的C++,指针会有一个更深刻的认识。希望对大家有用
C++虚函数及虚函数表解析,内容详细,分析清晰,推荐给大家。
c++里,指针和引用是很重要的概念,这个程序不仅对指针和引用做了说明、使用,而且对子类不能继承父类虚函数也做了说明。
多态是C++语言中的一项重要的机制,虚函数就是为实现多态而设计的。多态就是用父类型的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指针有“多种形态”。而虚函数表在...
C++实验六 多态性和虚函数的应用 课程 实验报告 作业参考的良品!
自己设计的图形面积计算程序,利用虚函数计算基类及派生类的图形面积,包括三角形、正方形、长方形、梯形
简单例子展示虚函数展现的多态特性,更改一处注释就能对比基类是否是虚函数带来的变化
用C++简单编码实现虚函数,展现虚函数的用法,以及虚析函数的用法 和 判断类的大小(在类中有虚函数的时候,无虚函数的时候)
彻底搞清楚继承是个什么东西 彻底搞清楚虚函数和虚函数表是个什么东西