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 ...
随机推荐
- 通视频URL截取第一帧图片
为了方便直接给UIImage加个类别,以后什么时候使用可以直接调用. #import <UIKit/UIKit.h> @interface UIImage (Video) /** 通过视频 ...
- 嵌入式开发之davinci---8148/8127/8168 中dsp c674的浮点和定点兼容
c674: 是c67(浮点)+c64(定点) 兼容的 http://processors.wiki.ti.com/index.php/-mv_option_to_use_with_the_C674x ...
- C#令人迷惑的DateTime:世界标准时间还是本地时间?
先来看一段代码: 复制内容到剪贴板程序代码 DateTime time = DateTime.Parse("2013-07-05 00:00:00");Console.WriteL ...
- 文件宝iOS/iPhone/iPad客户端简介
App Store地址:https://itunes.apple.com/cn/app/id1023365565?mt=8 文件宝-装机必备的文件管家,专业的rar-zip 解压工具,局域网看片神器, ...
- Phoenix put the sql back in NoSql
Overview | Apache Phoenix http://phoenix.apache.org/index.html Apache Phoenix enables OLTP and opera ...
- Studio 3T for MongoDB连接51.212复制集
Studio 3T for MongoDB连接51.212复制集 [ #DirectConection Authentication Mode - Basic(MONGODB-CR or SCEAM- ...
- safi 中placeholder不垂直居中
用css hack将line-height 设置为1 例子: input{height: 32px; line-height: 32px; [;line-height: 1px;]};
- JVM学习资料收集
JVM实用参数(一)JVM类型以及编译器模式 http://ifeve.com/useful-jvm-flags-part-1-jvm-types-and-compiler-modes-2/ JVM实 ...
- sql索引原理以及优化
http://itindex.net/detail/52237-%E7%B4%A2%E5%BC%95-%E5%8E%9F%E7%90%86 http://itindex.net/detail/5171 ...
- 无线网络中的MAC协议(1)
前文我们对传统的有线网络的MAC协议进行了分析,接下来我们在对无线网络的MAC也进行一个详细的介绍.那么无线网络中的MAC工作方式是如何的呢?无线局域网(WLAN)中MAC所对应的标准为IEEE 80 ...