【摘要】

本文从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. Python【每日一问】07

    问:请解释使用 *args 和 **kwargs 的含义 答: *args:可变参数,表示将实参中按照位置传值,多余的值都给 args,多余的实参被打包成 tuple(元组),然后传递给函数调用 # ...

  2. DP基础练习(4.21)

    数塔 Description 在讲述DP算法的时候,一个经典的例子就是数塔问题,它是这样描述的: 有如下所示的数塔,要求从顶层走到底层,若每一步只能走到相邻的结点,则经过的结点的数字之和最大是多少? ...

  3. 基于ssd的手势识别模型(object detection api方式)

    [Tensorflow]Object Detection API-训练自己的手势识别模型 1. 安装tensorflow以及下载object detection api 1.安装tensorflow: ...

  4. 知识点:tuple 元素真的不可变吗

    tuple 元素真的不可变吗 有一种有序列表叫元组:tuple. tuple和list非常类似,但是tuple一旦初始化就不能修改,比如同样是列出单位或同学的名字: >>> clas ...

  5. 8、Zookeeper分布式锁

    基础知识:http://www.cnblogs.com/LiZhiW/p/4931577.html 1 可重入读写锁示例代码如下(lock.acquire加几个,就必须使用几个lock.release ...

  6. centos7下安装.net core运行时

    Add the dotnet product feed Before installing .NET, you'll need to register the Microsoft key, regis ...

  7. MVC JsonResult 结果返回

    使用MVC之后, 默认的ActionResult 有很多子类譬如 JsonResult之类, 可以很方便. 基本用法如下:         public ActionResult GetVacatio ...

  8. UnityShaderVariant的一些探究心得

    最近遇到了一个问题,角色在Unity编辑器里运行渲染结果都是好的,打包到IOS上却发现,角色身上渲染的很黑.花了些时间查了查,又试了试,把这方面算是初步弄清楚了. 先说出现问题的原因,由于我们把sha ...

  9. make apr-util 报错 all recurisive

    安装expat库: yum install expat-devel 再次make 即可.

  10. 位运算骚操作 Part 3

    ▶ 异或运算 "^" 具有的部分性质: ● 交换律,结合律 ● a ^ b == (!a & b) | (a & !b),a ^ 1 == !a,a ^ 0 == ...