C++专题总结之理解虚拟函数、多继承、虚基类和RTTI

主题概要
C++虚拟函数、多继承、虚基类、RTTI
编辑时间
新建20170102
序号参考资料
1More effective C++
2http://blog.csdn.net/haoel/article/details/1948051

虚拟函数

记得以前面试的时候,面试官问了个问题,“多态的本质是什么”?
当时没答上来,现在想想如果能说出虚拟函数表来应该就可以了。
所谓虚函数的“虚”是指“直到运行时才能知道要调用的是哪一个函数”。那通过什么方法确定?答案是虚函数表和虚函数表指针。virtual table和virtual table pointers通常被分别地称为vtbl和vptr。
一个vtbl通常是一个函数指针数组。在程序中的每个类只要声明了虚函数或继承了虚函数,它就有自己的vtbl,并且类中vtbl的项目是指向虚函数实现体的指针。
例如这个类:

class C1 
{
public:
    C1();
    virtual ~C1();
    virtual void f1();
    virtual int f2(char c) const;
    virtual void f3(const string& s);
    void f4() const;    
};

虚函数表数组,看起来像这样:

这里写图片描述
注意非虚函数f4不在表中,而且C1的构造函数也不在。
如果有一个C2类继承自C1,重新定义了它继承的一些虚函数,并加入了它自己的一些虚函数:

class C2 : public C1 
{
public:
    C2();               // 非虚函数
    virtual ~C2();     // 重定义函数
    virtual void f1();  // 重定义函数
    virtual void f5(char *str); // 新的虚函数
};

它的virtual table项目指向与对象相适合的函数。这些项目包括指向没有被C2重定义的C1虚函数的指针,而f1被替换为子类重定义的指针:

这里写图片描述
如果有一函数调用:
ptr->f1();
根据这个虚函数表就能确定要执行哪个函数了。
实际上根据虚函数表只是完成了一半的工作,假设新建一个对象时,怎么样确定这个对象是使用哪一个虚函数表?只有用某种方法指出每个对象对应的vtbl(虚函数表)时,它们才能使用。这是virtual table pointer的工作,它来建立这种联系。
每个声明了虚函数的对象都带有它,它是一个看不见的数据成员,指向对应类的virtual table。
我们可以认为包含有虚函数的对象的布局是这样的:

这里写图片描述
假如我们有一个程序,包含几个C1和C2对象。对象、vptr和刚才我们讲述的vtbl之间的关系,在程序里我们可以这样去想象:

这里写图片描述

考虑这段这段程序代码:

void makeACall(C1 *pC1)
{
    pC1->f1();
}

通过指针pC1调用虚拟函数f1。仅仅看这段代码,你不会知道它调用的是那一个f1函数——C1::f1或C2::f1,因为pC1可以指向C1对象也可以指向C2对象。尽管如此编译器仍然得为在makeACall的f1函数的调用生成代码,它必须确保无论pC1指向什么对象,函数的调用必须正确。编译器生成的代码会做如下这些事情:
1). 通过对象的vptr找到类的vtbl。
2). 找到对应vtbl内的指向被调用函数的指针。
3). 调用第二步找到的的指针所指向的函数。

多继承与虚基类

多继承经常导致对虚基类的需求。没有虚基类,如果一个派生类有一个以上从基类的继承路径,基类的数据成员被复制到每一个继承类对象里,继承类与基类间的每条路径都有一个拷贝。而把基类定义为虚基类则可以消除这种复制。

虚基类是以什么方式消除这种复制的?

虚基类的实现经常使用指向虚基类的指针做为避免复制的手段。
例如考虑下面这幅图,我经常称它为“恐怖的多继承菱形”:

这里写图片描述
这里A是一个虚基类,因为B和C虚拟继承了它。使用一些编译器(特别是比较老的编译器),D对象会产生这样布局:
这里写图片描述
可以看到,通过虚继承的B类和C类中,分别有一个指向基类数据成员的指针,这样就避免了重复复制的问题。

运行时类型识别

RTTI能让我们在运行时找到对象和类的有关信息,所以肯定有某个地方存储了这些信息让我们查询。这些信息被存储在类型为type_info的对象里,你能通过使用typeid操作符访问一个类的type_info对象。

语言规范上这样描述:我们保证可以获得一个对象动态类型信息,如果该类型有至少一个虚函数。

一点思考:运用C++也不短时间了,这个知识感觉还是盲点,是类似JAVA/C#里面反射的东西吗?

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值