多重继承中,一个基类可以在派生层次中出现多次,如果一个派生类有多个直接基类,而这些直接基类又有一个共同的基类,则在最终的派生类中会保留该间接共同基类数据成员的多分同名成员。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. 转移iOS App常见问题和回答

    打算转移公司的app,做下转移的记录 怎样转移app官方的一个视频:http://v.youku.com/v_show/id_XNjI0MjQxMzI0.html 以下是apple官方转移常见问题回答 ...

  2. this、call和apply、bind

    this关键字: JavaScript的this关键字,总是指向一个对象,具体指向哪个对象,是根据运行时函数指向环境动态绑定的.简单来说,this就是谁调用指向谁.具体使用中,this的指向,大致可以 ...

  3. poj 2264 Advanced Fruits(DP)

    Advanced Fruits Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 1944   Accepted: 967   ...

  4. JAVA学习之 Model2中的Servlet与.NET一般处理程序傻傻分不清楚

    时隔多日,多日合适吗,应该是时隔多月.我又想起了一般处理程序.这都是由于近期在实现的DRP系统中经经常使用到jsp+servlet达到界面与逻辑的分离.servlet负责处理从jsp传回的信息:每当这 ...

  5. Spring Boot JPA 连接数据库

    本文将介绍怎样在Spring Boot project中加入JPA作为持久化方式. 改动 pom.xml 依赖 与上一篇介绍的 jdbc 不同的是 spring-boot-starter-jdbc 改 ...

  6. BZOJ 4976: 宝石镶嵌 背包

    4976: 宝石镶嵌 Time Limit: 2 Sec  Memory Limit: 128 MB Description 魔法师小Q拥有n个宝石,每个宝石的魔力依次为w_1,w_2,...,w_n ...

  7. poj 1742 Coins(二进制拆分+bitset优化多重背包)

    \(Coins\) \(solution:\) 这道题很短,开门见山,很明显的告诉了读者这是一道多重背包.但是这道题的数据范围很不友好,它不允许我们直接将这一题当做01背包去做.于是我们得想一想优化. ...

  8. JQuery操作TABLE,及console.info问题。

    还用alert 吗?看看console.info吧,代码的测试平台:ie9, firefox12 ​1. [代码][JavaScript]代码<!DOCTYPE html><html ...

  9. 转:Oracle客户端NLS_LANG参数的设置详解

    原文:http://database.51cto.com/art/201107/279361.htm 我们知道,Oracle客户端语言支持可以通过NLS_LANG参数的设置来完成,不同的系统平台上NL ...

  10. 自写程序调用mount

    代码: int fd = open("/dev/fuse", O_RDWR); printf("fd=%d\n",fd); int res; res=mount ...