C++知识点随笔(五):虚继承

原创 2015年08月20日 20:33:30

虚继承的出现就是为了解决多继承中访问不明确的问题。
首先让我们先看一下虚继承的代码:

#include<iostream>
using namespace std;

class AA
{
public:
    int m_a;
    AA()
    {
        m_a = 100;
    }
};

class BB : virtual public AA
{
public:
    int m_b;
    BB()
    {
        m_a = 200;
    }
};

class CC : virtual public AA
{
public:
    int m_c;
    CC()
    {
        m_a = 300;
    }
};

class DD : public BB, public CC
{
public:
    int m_d;
    DD()
    {
        m_a = 400;
    }
};

int main()
{
    DD dd;
    cout << sizeof(dd) << endl;
    cout << &dd << endl;
    cout << &dd.m_a << endl;
    cout << &dd.m_b << endl;
    cout << &dd.m_c << endl;
    cout << &dd.m_d << endl;

    cout << dd.AA::m_a << endl;
    cout << dd.BB::m_a << endl;
    cout << dd.CC::m_a << endl;
    cout << dd.m_a << endl;

    system("pause");
    return 0;
}

输出结果:
这里写图片描述
dd对象在内存中的结构如下:
这里写图片描述
我们看到虚继承中,内存的分配是与多继承不同的,我们不再是赋值两份AA对象,而是将虚基类放在了最后面,然后原来的BB和CC所继承的AA的地址存放的是指向AA对象的指针,这样做的另一个好处是还可以节约内存空间,因为如果AA对象的大小是100个字节,那我们也只需要一个4字节的指针指向它就可以了,而不用赋值一份儿占用空间。

注意:虚基类只有一份,并且所有继承虚基类的子类都要虚继承,否则如果有一个不是虚继承的那也会再复制一份AA,内存中同样存在了两份AA,那么访问不明确的问题就还是存在。

虚基类为什么要放在最后?
因为我们在使用这个虚基类的时候首先要知道是哪一个对象(就是指向虚基类的指针)调用的它,我们把虚基类放在最后就是要保证:前面的所有的对象使用虚基类的时候都是通过指向虚基类的指针找到的。如果我们不放在后面而是放在前面,那么找到虚基类就有了两种方式:直接使用对象的地址 or 指向虚基类的指针,这样我们在使用虚基类的时候还要加一个判断,来判断到底是谁调用的呢?这样显然更麻烦了。所以我们要把虚基类放在后面,让所有子类都是通过指向虚基类的指针来找到它。

含有虚函数的虚继承

我们首先看一道题:下列程序的结果是什么?

#include <iostream>
#include <memory.h>
#include <assert.h>
using namespace std;

class A
{
    char k[3];
public:
    virtual void aa(){};
};

class B : public virtual A
{
    char j[3];
public:
    virtual void bb(){};
};

class C : public virtual B
{
    char i[3];
public:
    virtual void cc(){};
};

int main(int arge,char *argv[])
{
    cout<<"sizeof(A):"<<sizeof(A)<<endl;
    cout<<"sizeof(B):"<<sizeof(B)<<endl;
    cout<<"sizeof(C):"<<sizeof(C)<<endl;

    system("pause");
    return 0;
}

输出结果:
这里写图片描述
我们来看一下c对象在内存中的结构:
这里写图片描述
从图中我们可以看出:每个类最初始的4个字节都是指向虚函数列表的指针,由于A是虚基类,没有父类,所以A只有8个字节大小;而B虚继承了A,所以B要有一个指向A类地址的指针,所以B类是12+8(A的大小)=20;同样,C类虚继承了B,所以C要有一个指向B类地址的指针,所以C类是12+20(B的大小)=32。

什么情况下会产生新的虚函数列表呢?
为了便于理解,我们首先来看一下上一个例子中,改为普通继承情况下的输出:
这里写图片描述
我们现在再看一下c对象在内存中的结构:
这里写图片描述
那么我们是通过什么方式来确定他们的结构的呢?就是分别用不同类型的指针去指向c这个对象。如下:

C c;
A* a = &c;
B* b = &c;

当我们使用普通继承的时候a、b、c所返回的地址都是相同的,即 0C 地址。所以在实现多态的时候,父类将虚函数加到了这个虚表中,而他的子类在重写虚函数的时候也是首先复制了父类的虚函数列表,然后用自己重写的虚函数的指针覆盖父类所存入的指针。这样我们在父类调用多态的时候就可以直接从虚表里找到新的虚函数的地址了。

版权声明:本文为博主原创文章,未经博主允许不得转载。

C++知识点随笔(二):继承、多态

一、继承继承是类之间的,对象之间没有什么关系,比如子类继承了父类的成员属性,并且子类的对象修改了这个成员属性,可是修改之后我们再去输出父类的成员属性发现并没有发生改变,原因是这两个对象本来就是两块空间...

C++知识点随笔(六):模板

一、函数模板C++中模板和重载的区别: 模板是除了类型全都相同的情况下可以使用;而重载是里面的功能也有不同时使用的。 下面看一个函数模板的使用实例:#include using namespace...

C++知识点随笔(三):static、const、friend、inline

一、static静态成员属性为什么要在类外初始化? 静态成员属性之所以不能在构造函数内初始化,是因为构造函数必须要在定义对象的时候才会调用,而static变量在编译的时候就创建了,所以要在类外通过类...

C++知识点随笔(七):成员函数指针、模拟虚函数列表

成员函数指针普通的全局函数指针定义:typedef void (*PFUN)();普通的函数指针可以指向返回值和参数类型都相同的全局函数,可是成员函数该怎么办呢?如果成员函数是static的,那么可以...

C++中虚函数与纯虚函数以及虚继承的相关知识

这篇文章主要利用网上的博客,经过自己的总结写成,不免出现错误情况,如有错误,请指正。 虚函数 首先是虚函数的意义。在面向对象的设计思想中,虚函数的作用是实现多态性。如何实现多态呢?下面看C++虚函数表...

进程、线程知识点随笔

优先级反转问题 原理 所谓优先级翻转问题(priority inversion)即当一个高优先级任务通过信号量机制访问共享资源时,该信号量已被一低优先级任务占有,而这个低优先级任务...

深度学习(DL)与卷积神经网络(CNN)学习笔记随笔-01-CNN基础知识点

转载自:http://blog.csdn.net/niuwei22007/article/details/47399913 《CNN基础知识点》From:Convolutional Neur...

深度学习(DL)与卷积神经网络(CNN)学习笔记随笔-01-CNN基础知识点

《CNN基础知识点》From:Convolutional Neural Networks (LeNet)原文链接可以查看更多信息:http://blog.csdn.net/niuwei22007/ar...

C++ 继承与接口 知识点 小结(一)

要求理解覆盖、重载、隐藏的概念与相互之间的区别;熟记类继承中派生类和对象及其函数的访问控制权限;掌握虚函数、虚函数表、虚函数指针的联系;理解区分虚函数和虚继承在虚方法、虚指针在空间分配上的重点与难点;...

C++ 知识点(三):面向对象编程:类,对象,继承,重载

C++ 类和对象C++ 在 C 语言的基础上增加了面向对象编程,C++ 支持面向对象程序设计。类是 C++ 的核心特性,通常被称为用户定义的类型。类用于指定对象的形式,它包含了数据表示法和用于处理数据...
  • Jurbo
  • Jurbo
  • 2016年09月06日 10:54
  • 841
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:C++知识点随笔(五):虚继承
举报原因:
原因补充:

(最多只允许输入30个字)