面试常见题之虚函数及底层的实现

26 篇文章 0 订阅

一.菱形对象模型(成员函数+虚函数的覆盖)

class AA
{
public:
    virtual void fun1()
    {
        cout << "AA::fun1()" << endl;
    }
public:
    int _aa;
};
class BB :public AA
{
public:
    virtual    void fun1()
    {
        cout << "BB::fun1()" << endl;
    }
public:
    int _bb;
};
class CC :public AA
{
public:
    virtual    void fun1()
    {
        cout << "CC::fun1()" << endl;
    }
public:
    int _cc;
};
class DD :public BB, public CC
{
public:
    virtual    void fun1()
    {
        cout << "DD::fun1()" << endl;
    }
public:
    int _dd;
};
int main()
{
    DD d;
    d.BB::_aa = 1;
    d.CC::_aa = 2;
    d._bb = 3;
    d._cc = 4;
    d._dd = 5;
cout << sizeof(d) << endl;
    system("pause");
    return 0;
}
前面也提到过,菱形继承会存在数据的冗余和二义性。那如果加上虚函数结果会怎样呢?
我们知道有虚表指针存放虚函数的地址;我们再从内存的角度观察一下:


观察结果:


我们画出菱形的对象模型如下:


现在我们再来分析内存为什么是28个字节,BB对象中有一个虚表指针_vfptr存放重写派生类DD::fun1()虚函数的地址,BB的对象中还有从派生类AA继承的整形_aa和自己类中的_bb;CC的对象中有一个虚表指针_vfptr 重写了派生类DD::fun1()的虚函数的地址,以及自己从基类集成的_aa;DD的对象中只有自己类中的_dd。这样我们就很容易搞明白28个字节的原因了。

二.菱形虚继承(虚函数的重写)

#include<iostream>
using namespace std;
class AA
{
public:
    virtual void fun1()
    {
        cout << "AA::fun1()" << endl;
    }
public:
    int _aa;
};
class BB :virtual public AA
{
public:
    virtual    void fun1()
    {
        cout << "BB::fun1()" << endl;
    }
    virtual    void fun2()
    {
        cout << "BB::fun2()" << endl;
    }
public:
    int _bb;
};
class CC :virtual public AA
{
public:
    virtual    void fun1()
    {
        cout << "CC::fun1()" << endl;
    }
    virtual    void fun2()
    {
        cout << "CC::fun2()" << endl;
    }
public:
    int _cc;
};
class DD :public BB, public CC
{
public:
    virtual    void fun1()
    {
        cout << "DD::fun1()" << endl;
    }
    virtual    void fun2()
    {
        cout << "DD::fun2()"<< endl;
    }
public:
    int _dd;
};
int main()
{
    DD d;
    d.BB::_aa = 1;
    d.CC::_aa = 2;

    d._bb = 3;
    d._cc = 4;
    d._dd = 5;
    cout << sizeof(d) << endl;
    system("pause");
    return 0;
}

但是结果是36,我们再从监视窗口来看,

我们看到有两个虚表指针,有两层意思,第一层派生类的虚函数BB::fun2()重写了基类的虚函数AA::fun2().,第二层派生类DD::fun2()有重写了虚函数BB::fun2()。
我们再画出菱形对象的模型如下:

来分析内存的大小为什么是36,对象BB我们可以理解有三部分组成,一个虚表指针存放虚函数DD::fun2()的地址,一个虚基址指针用来存放BB对象的偏移量,整型_bb对象CC也有三部分组成,一个虚表指针存放虚函数DD::fun2()的地址,一个 虚基表指针存放CC对象的偏移量,以及整形_cc。对象DD只有整形_dd,对象AA有两部分虚表指针和整形_aa.

三.总结

虚函数的作用:虚函数的重写是实现多态的条件,通过虚表指针调用相应的函数实现。
虚继承的作用:
1.解决菱形继承的二义性和数据冗余的问题,
2.底层实现原理与编译器相关,一般通过虚基类 指针实现,即各对象中只保存一份父类的对象,多继承时通过虚基类指针引用该公共对象,从而避 免菱形继承中的二义性问题。
对象模型前面也有介绍http://blog.csdn.net/f2016913/article/details/55292948
以及菱形继承http://blog.csdn.net/f2016913/article/details/55225939




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值