c++继承汇总(单继承、多继承、虚继承、菱形继承)
多重继承中,一个基类可以在派生层次中出现多次,如果一个派生类有多个直接基类,而这些直接基类又有一个共同的基类,则在最终的派生类中会保留该间接共同基类数据成员的多分同名成员。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、菱形继承
注:菱形继承存在二义性问题,编译都不通过,只能通过指定特定基类的方式进行访问基类变量。
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++继承汇总(单继承、多继承、虚继承、菱形继承)的更多相关文章
- Unity 游戏框架搭建 (十三) 无需继承的单例的模板
之前的文章中介绍的Unity 游戏框架搭建 (二) 单例的模板和Unity 游戏框架搭建 (三) MonoBehaviour单例的模板有一些问题. 存在的问题: 只要继承了单例的模板就无法再继承其他的 ...
- 【整理】C++虚函数及其继承、虚继承类大小
参考文章: http://blog.chinaunix.net/uid-25132162-id-1564955.html http://blog.csdn.net/haoel/article/deta ...
- C++中的类继承(4)继承种类之单继承&多继承&菱形继承
单继承是一般的单一继承,一个子类只 有一个直接父类时称这个继承关系为单继承.这种关系比较简单是一对一的关系: 多继承是指 一个子类有两个或以上直接父类时称这个继承关系为多继承.这种继承方式使一个子类可 ...
- C++中的类继承之单继承&多继承&菱形继承
C++中的类继承之单继承&多继承&菱形继承 单继承是一般的单一继承,一个子类只 有一个直接父类时称这个继承关系为单继承.这种关系比较简单是一对一的关系: 多继承是指 一个子类有两个或 ...
- inheritance,菱形继承, 虚继承,virtual
//菱形继承 ||||||| 虚继承 #include <iostream> using namespace std; class R { int r; public: ...
- C++类有继承时,析构函数必须为虚函数
C++类有继承时,析构函数必须为虚函数.如果不是虚函数,则使用时可能存在内在泄漏的问题. 假设我们有这样一种继承关系: 如果我们以这种方式创建对象: SubClass* pObj = new SubC ...
- C++中对C的扩展学习新增内容———面向对象(继承)函数扩展性及虚函数机制
1.c语言中的多态,动态绑定和静态绑定 void do_speak(void(*speak)()) { speak(); } void pig_speak() { cout << &quo ...
- day18-Python运维开发基础(单继承 / 多继承 / 菱形继承、类的多态)
1. 单继承 / 多继承 / 菱形继承 # ### 继承 : 一个类除了自身所拥有的属性方法之外,还获取了另外一个类的成员属性和方法 """ 一个类可以继承另外一个类,那 ...
- day23:单继承&多继承&菱形继承&__init__魔术方法
1.单继承 1.1 关于继承的一些基本概念 1.2 子类可以调用父类的公有成员 1.3 子类无法调用父类的私有成员 1.4 子类可以改写父类的方法 2.多继承 2.1 多继承的基本语法 2.2 sup ...
随机推荐
- php 0、null、empty和false之间的关系
// 判断 0 与 ''.null.empty.false 之间的关系 $a = 0; echo "0 与 ''. empty.null.false 之间的关系:"; if($a ...
- ios开发之自己定义默认生成的导航栏 标题 颜色 返回button
一 改动导航栏颜色 导航栏在哪个页面代码放在那里面 self.navigationController.navigationBar.tintColor = [UIColor colorWithR ...
- linux 输入子系统(1) -Event types
输入系统协议用类型types和编码codecs来表示输入设备的值并用此来通知用户空间的应用程序. input协议是一个基于状态的协议,只有当相应事件编码对应的参数值发生变化时才会发送该事件.不过,状态 ...
- 新拿到的app跑的时候出现问题
连接器链接失败,是因为对应类build后的中间产物.o文件没有生成,对应架构下,用模拟器跑的,很可能是因为无法编译产出x86,或i386架构的中间产物,所以linker链接转换机器码时候找不到对应的中 ...
- gitlab merge过程
基本步骤如下: 以我的分支为例 1.创建本地分支,命令 git checkout -b liuping_develop2.创建好分支后提交到远程 ,命令 git push origin liuping ...
- delphi 7中使用idhttp抓取网页 解决假死现象(使用TIdAntiFreezeControl控件)
在delphi 7中使用idhttp抓取网页,造成窗口无反应的假死状态.通过搜索获得两种方法. 1.写在线程中,但是调用比较麻烦 2.使用delphi 提供的idantifreeze(必须安装indy ...
- 细说align 的作用及用法
.align 就是用来对齐的,究竟怎么对齐,有啥情况?下面分析一下 基本情况讲解 (一) $vim align1.s 在新建的文件编辑以下代码: 1 2 3 4 5 6 _start: b reset ...
- HDU2089 不要62 —— 数位DP
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2089 不要62 Time Limit: 1000/1000 MS (Java/Others) M ...
- uptime命令
uptime命令能够打印系统总共运行了多长时间和系统的平均负载.uptime命令可以显示的信息显示依次为:现在时间.系统已经运行了多长时间.目前有多少登陆用户.系统在过去的1分钟.5分钟和15分钟内的 ...
- 解决IE浏览器部分版本不支持background-size属性问题
background-size是CSS3新增的属性,现在有很多浏览器都支持CSS3了.但是IE浏览器有些版本还是不支持,比如IE8,IE9也有些CSS3的属性会支持,但是有些也不支持.在这里就了解一下 ...