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

  1.通常,每个类至初始化自己的直接基类,在应用于虚基类的时候,这个初始化策略会失败。如果使用这个规则,则可能多次初始化虚基类。为了解决这个问题,从具有虚基类的类继承的类初始化进行特殊处理。在虚派生中,由最底层的派生类构造函数初始化虚基类。即使是最底层派生类的非直接虚基类,也在最底层的虚派生类中调用其构造函数。

  2.无论虚基类出现在继承层次中的任何地方,总是在构造非虚基类之前构造虚基类。代码如下:

class A
{
public:
A()
{
cout<<"A constructor!"<<endl;
}
};

class B
{
public:
B()
{
cout<<"B constructor!"<<endl;
}
};

class C
{
public:
C()
{
cout<<"C constructor!"<<endl;
}
};

class D : public A, public B, virtual public C
{
public:
D()
{
cout<<"D constructor!"<<endl;
}
};

int _tmain(int argc, _TCHAR* argv[])
{
D d;
return 0;
}

运行结果:

可以看出,即使是C类在派生列表的最后,类C也是首先构造,因为C为虚基类。但是如果虚基类还有一个基类而且该基类不是虚基类,则必须先构造基类,代码如下:

class A
{
public:
  A()
  {
    cout<<"A constructor!"<<endl;
  }
};

class B
{
public:
  B()
  {
    cout<<"B constructor!"<<endl;
  }
};

class C
{
public:
  C()
  {
    cout<<"C constructor!"<<endl;
  }
};

class E : public C
{
public:
  E()
  {
    cout<<"E constructor!"<<endl;
  }
};

class D : public A, public B, virtual public E
{
public:
  D()
  {
    cout<<"D constructor!"<<endl;
  }
};

int _tmain(int argc, _TCHAR* argv[])
{
  D d;
  return 0;
}

运行结果:

虽然E是虚基类而C不是,但是还是先构造了C,因为C是E的基类,在构造一个类之前,必须先构造基类,即使是虚继承也不例外。

一、C++中的对象模型

1、 概念

语言中直接支持面向对象程序设计的部分;

对于各种支持的底层实现机制。(没看懂……)

2、 类中的成员分类

a) 成员函数

  i. static function

  ii. non static function

  iii. virtual function

b)  数据成员

  i. static member data

  ii. non static member data

3、 C++对象模型

a) 类对象内存布局中的包括

  i. non static member data

  ii. vptr(虚函数表指针)

  iii. vbptr(虚基类表指针)

b) 不包括

  i. static member data(存储在静态存储区)

  ii. 成员函数(存储在代码区)

c) virtual table

简称vtbl。存放着指针,这些指针指向该类每一个虚函数。虚表中的函数地址将按声明时的顺序排列。vtbl在类声明后就形成了,vptr是编译器生成的。

d) vptr的位置一般放在一个类对象的最前端。

e) 虚基类表

vbptr指向的表,用于存放虚继承中,虚基类存储相对于虚基类表指针的偏移量。

二、继承类型

1、普通继承(不包含虚函数)

a、单继承

class Base
{
public:
Base (int a = 1):base(a){}
void fun0(){cout << base << endl;}
int base;
};
class Derive:public Base
{
public:
Derive (int a = 2):derive(a){}
void fun1(){cout << base1 << endl;}
int derive;
};

b、多继承

class Base1
{
public:
Base1 (int a = 2):base1(a){}
void fun1(){cout << base1 << endl;}
int base1;
};
class Base2
{
public:
Base2 (int a = 3):base2(a){}
void fun2(){cout << base2 << endl;}
int base2;
};
class Derive: public Base1, public Base2
{
public:
Derive (int value = 4):derive (value){}
void fun3(){cout << derive << endl;}
int derive;
};

c、菱形继承

按 Ctrl+C 复制代码
按 Ctrl+C 复制代码

注:菱形继承存在二义性问题,编译都不通过,只能通过指定特定基类的方式进行访问基类变量。

Derive d;

d.base =3; // 不正确

d.Base1::base = 3; // 正确

2、普通继承(包含虚函数)

  a、单继承(包含虚函数)

class Base
{
public:
Base (int a = 1):base(a){}
virtual void fun0(){cout << base << endl;}
int base;
};
class Derive:public Base
{
public:
Derive (int a = 2):derive(a){}
virtual void fun0(){};
virtual void fun1(){cout << derive << endl;}
int derive;
};

注:派生类中新增的虚函数追加到虚函数表后面。

b、多继承(包含虚函数)

class Base1
{
public:
Base1 (int a = 2):base1(a){}
virtual void fun1(){cout << base1 << endl;}
int base1;
};
class Base2
{
public:
Base2 (int a = 3):base2(a){}
virtual void fun2(){cout << base2 << endl;}
int base2;
};
class Derive: public Base1, public Base2
{
public:
Derive (int value = 4):derive (value){}
virtual void fun3(){cout << derive << endl;}
int derive;
};

注:派生类中新增的虚函数,追加到第一个基类的虚函数表的后面。

c、菱形继承(包含虚函数)

class Base
{
public:
Base (int a = 1):base(a){}
virtual void fun0(){cout << base << endl;}
int base;
};
class Base1:public Base
{
public:
Base1 (int a = 2):base1(a){}
virtual void fun1(){cout << base1 << endl;}
int base1;
};
class Base2:public Base
{
public:
Base2 (int a = 3):base2(a){}
virtual void fun2(){cout << base2 << endl;}
int base2;
};
class Derive: public Base1, public Base2
{
public:
Derive (int value = 4):derive (value){}
virtual void fun3(){cout << derive << endl;}
int derive;
};

注:分析时,由上到下依次分析。存在二义性问题和内存冗余问题。

 3、虚继承(不包含虚函数)

新增虚基类指针,指向虚基类表,虚基类表中首项存储虚基类指针的偏移量,接下来依次存储虚基类的偏移量(偏移量是相对于虚基类表指针的存储地址)。

   a、单虚继承(不包含虚函数)

class Base
{
public:
Base (int a = 1):base(a){}
void fun0(){cout << base << endl;}
int base;
};
class Base1:virtual public Base
{
public:
Base1 (int a = 2):base1(a){}
void fun1(){cout << base1 << endl;}
int base1;
};

   b、多虚继承(不包含虚函数)

class Base1
{
public:
Base1 (int a = 2):base1(a){}
void fun1(){cout << base1 << endl;}
int base1;
};
class Base2
{
public:
Base2 (int a = 3):base2(a){}
void fun2(){cout << base2 << endl;}
int base2;
};
class Derive:virtual public Base1, virtual public Base2
{
public:
Derive (int value = 4):derive (value){}
void fun3(){cout << derive << endl;}
int derive;
};

  c、菱形虚继承(不包含虚函数)

   第一种形式:

class Base
{
public:
Base (int a = 1):base(a){}
void fun0(){cout << base << endl;}
int base;
};
class Base1:virtual Base
{
public:
Base1 (int a = 2):base1(a){}
void fun1(){cout << base1 << endl;}
int base1;
};
class Base2:virtual Base
{
public:
Base2 (int a = 3):base2(a){}
void fun2(){cout << base2 << endl;}
int base2;
};
class Derive:virtual public Base1, virtual public Base2
{
public:
Derive (int value = 4):derive (value){}
void fun3(){cout << derive << endl;}
int derive;
};

注:分析派生类的内存分布时,也是由上到下分析。虚继承将基类置于内存末尾,但是置于末尾的顺序也有一定的次序。首先Base先放到末尾,然后Base1放到末尾,最后Base2放到末尾。

第二种形式:

class Base
{
public:
Base (int a = 1):base(a){}
void fun0(){cout << base << endl;}
int base;
};
class Base1:virtual public Base
{
public:
Base1 (int a = 2):base1(a){}
void fun1(){cout << base1 << endl;}
int base1;
}; class Base2:virtual public Base
{
public:
Base2 (int a = 3):base2(a){}
void fun2(){cout << base2 << endl;}
int base2;
};
class Derive: public Base1, public Base2
{
public:
Derive (int value = 4):derive (value){}
void fun3(){cout << derive << endl;}
int derive;
};

注:分析的原则,从上到下,依次分析。

4、 虚继承(包含虚函数)

a、单虚继承(包含虚函数)

class Base
{
public:
Base (int a = 1):base(a){}
virtual void fun0(){cout << base << endl;}
int base;
};
class Base1:virtual Base
{
public:
Base1 (int a = 2):base1(a){}
virtual void fun1(){cout << base1 << endl;}
int base1;
};

  与普通的包含虚函数的单继承相比,派生类拥有自己的虚函数表以及虚函数表指针,而不是与基类共用一个虚函数表。注意虚函数表指针和虚基类表指针的存储顺序。

  b、多虚继承(包含虚函数)

class Base1
{
public:
Base1 (int a = 2):base1(a){}
virtual void fun1(){cout << base1 << endl;}
int base1;
}; class Base2
{
public:
Base2 (int a = 3):base2(a){}
virtual void fun2(){cout << base2 << endl;}
int base2;
};
class Derive:virtual public Base1, virtual public Base2
{
public:
Derive (int value = 4):derive (value){}
virtual void fun3(){cout << derive << endl;}
int derive;
};

c、菱形虚继承(包含虚函数)

第一种形式:

class Base
{
public:
Base (int a = 1):base(a){}
virtual void fun0(){cout << base << endl;}
int base;
};
class Base1:virtual public Base
{
public:
Base1 (int a = 2):base1(a){}
virtual void fun1(){cout << base1 << endl;}
int base1;
}; class Base2:virtual public Base
{
public:
Base2 (int a = 3):base2(a){}
virtual void fun2(){cout << base2 << endl;}
int base2;
};
class Derive: public Base1, public Base2
{
public:
Derive (int value = 4):derive (value){}
virtual void fun3(){cout << derive << endl;}
int derive;
};

第二种形式:

class Base
{
public:
Base (int a = 1):base(a){}
virtual void fun0(){cout << base << endl;}
int base;
};
class Base1:virtual public Base
{
public:
Base1 (int a = 2):base1(a){}
virtual void fun1(){cout << base1 << endl;}
int base1;
}; class Base2:virtual public Base
{
public:
Base2 (int a = 3):base2(a){}
virtual void fun2(){cout << base2 << endl;}
int base2;
};
class Derive: virtual public Base1,virtual public Base2
{
public:
Derive (int value = 4):derive (value){}
virtual void fun3(){cout << derive << endl;}
int derive;
};

自行脑补C++类对象的内存结构……

注:上述虚函数中,如果派生类重写了基类的虚函数,则对应虚函数表中的虚函数应该修改成重新后的虚函数,即Base::fun()->Derive::fun()。

c++继承汇总(单继承、多继承、虚继承、菱形继承)的更多相关文章

  1. Unity 游戏框架搭建 (十三) 无需继承的单例的模板

    之前的文章中介绍的Unity 游戏框架搭建 (二) 单例的模板和Unity 游戏框架搭建 (三) MonoBehaviour单例的模板有一些问题. 存在的问题: 只要继承了单例的模板就无法再继承其他的 ...

  2. 【整理】C++虚函数及其继承、虚继承类大小

    参考文章: http://blog.chinaunix.net/uid-25132162-id-1564955.html http://blog.csdn.net/haoel/article/deta ...

  3. C++中的类继承(4)继承种类之单继承&多继承&菱形继承

    单继承是一般的单一继承,一个子类只 有一个直接父类时称这个继承关系为单继承.这种关系比较简单是一对一的关系: 多继承是指 一个子类有两个或以上直接父类时称这个继承关系为多继承.这种继承方式使一个子类可 ...

  4. C++中的类继承之单继承&多继承&菱形继承

     C++中的类继承之单继承&多继承&菱形继承 单继承是一般的单一继承,一个子类只 有一个直接父类时称这个继承关系为单继承.这种关系比较简单是一对一的关系: 多继承是指 一个子类有两个或 ...

  5. inheritance,菱形继承, 虚继承,virtual

    //菱形继承   |||||||   虚继承 #include <iostream> using namespace std; class R {     int r; public:   ...

  6. C++类有继承时,析构函数必须为虚函数

    C++类有继承时,析构函数必须为虚函数.如果不是虚函数,则使用时可能存在内在泄漏的问题. 假设我们有这样一种继承关系: 如果我们以这种方式创建对象: SubClass* pObj = new SubC ...

  7. C++中对C的扩展学习新增内容———面向对象(继承)函数扩展性及虚函数机制

    1.c语言中的多态,动态绑定和静态绑定 void do_speak(void(*speak)()) { speak(); } void pig_speak() { cout << &quo ...

  8. day18-Python运维开发基础(单继承 / 多继承 / 菱形继承、类的多态)

    1. 单继承 / 多继承 / 菱形继承 # ### 继承 : 一个类除了自身所拥有的属性方法之外,还获取了另外一个类的成员属性和方法 """ 一个类可以继承另外一个类,那 ...

  9. day23:单继承&多继承&菱形继承&__init__魔术方法

    1.单继承 1.1 关于继承的一些基本概念 1.2 子类可以调用父类的公有成员 1.3 子类无法调用父类的私有成员 1.4 子类可以改写父类的方法 2.多继承 2.1 多继承的基本语法 2.2 sup ...

随机推荐

  1. Material-Animations

    https://github.com/eltld/Material-Animations

  2. Matlab依据样本随机数求概率曲线

    相关Matlab函数:hist, bar, cdfplot, ksdensity (1) hist函数 n = hist(Y, x)  假设x是一个向量,返回x的长度个以x为中心的,Y的分布情况. 比 ...

  3. 使用Swift作为Glance后端存储

    原文链接 http://thornelabs.net/2014/08/03/use-openstack-swift-as-a-backend-store-for-glance.html By defa ...

  4. iOS之UI--使用SWRevealViewController 实现侧边菜单功能详解实例

     iOS之UI--使用SWRevealViewController 实现侧边菜单功能详解实例 使用SWRevealViewController实现侧边菜单功能详解 下面通过两种方法详解SWReveal ...

  5. 使用Apache Ant合并多个jar

    Apache Ant下载地址 下载解压后进入bin目录,并在此目录打开cmd 在cmd中运行ant,运行结果为: Buildfile: build.xml does not exist! Build ...

  6. B.大钉骑马走江湖

    江湖是什么,对于在象棋界厮杀的大钉来说,江湖就是一个矩阵,他的目标,就是在江湖之中骑着马,从他的位置出发,走到终点. 当然,大钉的马也遵从中国象棋中的“马走日”的规则,而且在矩阵中,也会有一些障碍物, ...

  7. js 中call,apply,bind的区别

    call.apply.bind方法的共同点与区别: apply.call.bind 三者都是用来改变函数的this对象的指向: apply.call.bind 三者都可以利用后续参数传参: bind ...

  8. HDU4763 Theme Section —— KMP next数组

    题目链接:https://vjudge.net/problem/HDU-4763 Theme Section Time Limit: 2000/1000 MS (Java/Others)    Mem ...

  9. Maze 解题报告

    Maze Description   You are given a special Maze described as an n*m matrix, please find the shortest ...

  10. Oracle:impdb导入

    最近有现场给我一份用expdp导出dmp文件,我用imp导入时,报错.因为导出dmp的数据库是11g,导入的数据库也是11g, 但客户端安装的是10g,不能用imp导入:所以只能试着用impdp导入: ...