【摘要】

本文从5段代码实例出发,通过类中类的普通继承,类的虚继承,类的多重继承,多个虚函数类的普通继承、虚继承与多重继承,几个交叉概念,详细的阐释了继承、虚函数与虚继承的基本概念,深入剖析了继承于虚继承的区别于联系。

【Exp.001-虚继承】

#include <stdio.h>

class A {
public:
int a;
};//sizeof(A)=4 class B : virtual public A {
public:
  int b;
};//sizeof(B)=4(A副本)+4(虚表指针)+4(自己变量)=12 class C : virtual public B {
};//sizeof(c)= 12(B副本)+4(虚表指针) = 16,如果这里改为直接继承,那么sizeof(c)=12 int main() {
printf("%d\n", sizeof(C));
return ;
}

解析:这里需要理解虚继承基类对派生类的空间大小的影响,理解虚指针在虚继承中为子类带来了哪些空间的改变。

【Exp.002-多重继承】

#include <stdio.h>

class A {
public:
int a;
};//sizeof(A) = 4 class B : virtual public A {
};// sizeof(B) =4+4=8 class C : virtual public A {
};//sizeof(C) =4+4=8 class D : public B, public C{
};
//sizeof(D)=8+8-4=12 这里需要注意要减去4,因为B和C同时继承A,只需要保存一个A的副本就好了,sizeof(D)=4(A的副本)+4(B的虚表)+4(C的虚表)=12 int main() {
printf("%d\n", sizeof(D));
return ;
}

解析:这里需要关注 class D 的数据空间大小,理解多重虚继承对派生类虚指针以及派生类空间的影响。

【Exp.003-普通继承(含有:空类、虚函数)】

class A
{
}; class B
{
char ch;
virtual void func0() { }
}; class C
{
char ch1;
char ch2;
virtual void func() { }
virtual void func1() { }
}; class D: public A, public C
{
int d;
virtual void func() { }
virtual void func1() { }
}; class E: public B, public C
{
int e;
virtual void func0() { }
virtual void func1() { }
}; int main(void)
{
cout<<"A="<<sizeof(A)<<endl;//result=1 空类所占空间的大小为 1
cout<<"B="<<sizeof(B)<<endl;//result=8 1+4 对齐 8
cout<<"C="<<sizeof(C)<<endl;//result=8 1+1+4 对齐 8
cout<<"D="<<sizeof(D)<<endl;//result=12 C的副本+D本身=8+4=12
cout<<"E="<<sizeof(E)<<endl;//result=20 B的副本+C的副本+E本身=8+8+4=20
return ;
}

这里需要区分一下:

①没有继承的时候,存在虚函数则需要加上虚指针,如果有多个也只需要加上一个,因为只有一个虚指针;

②对于普通继承,类D和类E中自己的虚函数,大小为0,因为,它没有虚表;

③对于虚继承中,派生类中存在一个或多个虚函数的时候,它本身就有一个虚表,指向自己的虚表,所以要加4。

【Exp.004-虚继承(多重继承和虚函数)】

class CommonBase
{
int co;
};// size = 4 class Base1: virtual public CommonBase
{
public:
virtual void print1() { }
virtual void print2() { }
private:
int b1;
};//4副本+4虚指针+4自身+4(虚继承+虚函数构成指针多一个)=16 class Base2: virtual public CommonBase
{
public:
virtual void dump1() { }
virtual void dump2() { }
private:
int b2;
};//同理16 class Derived: public Base1, public Base2
{
public:
void print2() { }
void dump2() { }
private:
int d;
};//16+16-4+4=32

解析:如果不是虚继承的类,即便有虚函数也不会因此增加存储空间,如果是虚继承的类,没有虚函数就添加一个虚指针空间,有虚函数不论多少个,就添加两个虚指针空间。

【Exp.005-虚继承与虚函数】

class A
{
public:
virtual void aa() { }
virtual void aa2() { }
private:
char ch[];
}; // 1+4 = 补齐 = 8 class B: virtual public A
{
public:
virtual void bb() { }
virtual void bb2() { }
}; // 8(副本)+4(虚继承)+4(虚指针) = 16 int main(void)
{
cout<<"A's size is "<<sizeof(A)<<endl;// 4+4=8
cout<<"B's size is "<<sizeof(B)<<endl;// A的副本+4+4=16
return ;
}

解析:如果不是虚继承的类,即便有虚函数也不会因此增加存储空间,如果是虚继承的类,没有虚函数就添加一个虚指针空间,有虚函数不论多少个,就添加两个虚指针空间。

【小结】

重要的事情讲三遍!!!

如果不是虚继承的类,即便有虚函数也不会因此增加存储空间,如果是虚继承的类,没有虚函数就添加一个虚指针空间,有虚函数不论多少个,就添加两个虚指针空间!!!

原文地址:https://blog.csdn.net/u013630349/article/details/47057929

C++ 深入理解 虚继承、多重继承和直接继承的更多相关文章

  1. C++_day8_ 多重继承、钻石继承和虚继承

    1.继承的复习 1.1 类型转换 编译器认为访问范围缩小是安全的. 1.2 子类的构造与析构 子类中对基类构造函数初始化只能写在初始化表里,不能写在函数体中. 阻断继承. 1.3 子类的拷贝构造与拷贝 ...

  2. C++多重继承与虚拟继承

    本文只是粗浅讨论一下C++中的多重继承和虚拟继承. 多重继承中的构造函数和析构函数调用次序 我们先来看一下简单的例子: #include <iostream> using namespac ...

  3. 图文例解C++类的多重继承与虚拟继承

    文章导读:C++允许为一个派生类指定多个基类,这样的继承结构被称做多重继承. 在过去的学习中,我们始终接触的单个类的继承,但是在现实生活中,一些新事物往往会拥有两个或者两个以上事物的属性,为了解决这个 ...

  4. c++继承汇总(单继承、多继承、虚继承、菱形继承)

    多重继承中,一个基类可以在派生层次中出现多次,如果一个派生类有多个直接基类,而这些直接基类又有一个共同的基类,则在最终的派生类中会保留该间接共同基类数据成员的多分同名成员.C++提供虚基类的方法使得在 ...

  5. 《挑战30天C++入门极限》图文例解C++类的多重继承与虚拟继承

        图文例解C++类的多重继承与虚拟继承 在过去的学习中,我们始终接触的单个类的继承,但是在现实生活中,一些新事物往往会拥有两个或者两个以上事物的属性,为了解决这个问题,C++引入了多重继承的概念 ...

  6. C++逆向分析----多重继承和菱形继承

    多重继承 多重继承是指C++类同时继承两个类或两个以上的类. class Test { public: int num1; Test() { num1 = 1; } virtual void Proc ...

  7. java提高篇(二)-----理解java的三大特性之继承

    在<Think in java>中有这样一句话:复用代码是Java众多引人注目的功能之一.但要想成为极具革命性的语言,仅仅能够复制代码并对加以改变是不够的,它还必须能够做更多的事情.在这句 ...

  8. javascript中继承(二)-----借用构造函数继承的个人理解

    本人目录如下: 零.寒暄&回顾 一,借用构造函数 二.事件代理 三,call和apply的用法 四.总结 零.寒暄&回顾 上次博客跟大家分享了自己对原型链继承的理解,想看的同学欢迎猛击 ...

  9. 对Java不能多继承,只能单继承,却可以实现多个接口的理解

    1.java与C++的不同点在于多继承. Java:不能多继承,只能单继承,但可以实现多个接口 C++:可以实现多继承.例如: class A extends B implements C,D,E { ...

随机推荐

  1. 2018-2019-20175334实验一《Java开发环境的熟悉》实验报告

    2018-2019-20175334实验一<Java开发环境的熟悉>实验报告 一.实验内容及步骤 实验一Java开发环境的熟悉-1 建立"自己学号exp1"的目录 在& ...

  2. 20164310Exp2后门原理与实践

    一.基础问题回答 1.例举你能想到的一个后门进入到你系统中的可能方式 答:在莫名其妙的网站下载某些莫名奇妙的播放器. 2.例举你知道的后门如何启动起来(win及linux)的方式? 答:对于windo ...

  3. List<Map<String, Object>>集合中获取某个key并转换为List<Integer>集合

    package com.codyy.sso.controller.yuanqu; import java.util.ArrayList; import java.util.HashMap; impor ...

  4. 关于__cmp__的使用

    __cmp__是python的类中所使用的特殊函数,一般用于对类对象列表的排序. 举个例子,假设需要对Student类的对象列表按照其成绩grade属性进行排序,那么可以这么设计: class Stu ...

  5. pip install pytest on Mac (EI Capitan 10.11.6)

    升级了Mac 系统后发现用pip安装pytest出现下面链接中的问题,解决方法是在install时候加上--user选项: 1. 切到home directory: cd - 2. install p ...

  6. ubuntu16.04上安装ros-kinetic

    1.设置安装源 sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" ...

  7. Spark-RPC理解

    基本架构 Akka Actor式RPC架构 Spark采用的是AkkaActor架构实现RPC,但是实际使用过程为了兼容不同节点之间的文件下载,采用Netty来实现Actor功能. Spark RPC ...

  8. Python全栈开发记录_第七篇(模块_time_datetime_random_os_sys_hashlib_logging_configparser_re)

    这一篇主要是学习python里面的模块,篇幅可能会比较长 模块的概念:在Python中,一个.py文件就称之为一个模块(Module). 模块一共三种: python标准库 第三方模块 应用程序自定义 ...

  9. 使用K-means进行聚类,用calinski_harabaz_score评价聚类效果

    代码如下: """ 下面的方法是用kmeans方法进行聚类,用calinski_harabaz_score方法评价聚类效果的好坏 大概是类间距除以类内距,因此这个值越大越 ...

  10. 搭建IntelliJ IDEA授权服务器

    地址:https://blog.csdn.net/maozexijr/article/details/79072287    https://www.jianshu.com/p/754d8f907f2 ...