继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能。这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。
C++的继承方式:
C++类的3种继承方式,分别是public继承,protected继承,private继承。我们最常用的还是public继承。class默认的是private继承,它的member如果没写权限也是默认private。struct则相反,默认的是public继承。下面是主要的事项以及区别:
1、私有继承:
(1)基类的公有成员,派生类可以继承为自己的私有成员,在类内可以访问,类外不可以访问;
(2)基类的保护成员,派生类可以继承为自己的私有成员,在类内可以访问,类外不可以访问;
(3)基类的私有成员,派生类同样不可以访问。
2、公有继承:
(1)基类的公有成员,派生类可以继承为自己的公有成员,在类内可以访问,类外也可以访问;
(2)基类的保护成员,派生类可以继承为自己的保护成员,在类内可以访问,类外不可以访问;
(3)基类的 私有成员,派生类不可以访问。
3、保护继承:
(1)基类的公有成员,派生类可以继承为自己的保护成员,在类内可以访问,类外不可以访问;
(2)基类的保护成员,派生类可以继承为自己的保护成员,在类内可以访问,类外不可以访问;
(3)基类的私有成员,派生类同样不可以访问。
具体情况如图所示:
关于继承的几点总结:
1. 基类的 private 成员在派生类中是不能被访问的,如果基类成员不想在类外直接被访问,但需要
在派生类中能访问,就定义为 protected 。可以看出保护成员限定符是因继承才出现的。
2. public继承是一个接口继承,保持is-a原则,每个父类可用的成员对子类也可用,因为每个子类
对象也都是一个父类对象。
3. protected/private继承是一个实现继承,基类的部分成员并非完全成为子类接口的一部分,
是 has-a 的关系原则,所以非特殊情况下不会使用这两种继承关系,在绝大多数的场景下使用的
都是公有继承。私有继承以为这is-implemented-in-terms-of(是根据……实现的)。通常比
组合(composition)更低级,但当一个派生类需要访问基类保护成员或需要重定义基类的虚函
数时它就是合理的。
4. 不管是哪种继承方式,在派生类内部都可以访问基类的公有成员和保护成员,基类的私有成员存
在但是在子类中不可见(不能访问)。
5. 使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public,不过最
好显示的写出继承方式。
6. 在实际运用中一般使用都是public继承,极少场景下才会使用protetced/private继承。
继承分类:
1、单继承:一个子类只有有个父亲
以公有继承为例代码实现:
#include<iostream>
using namespace std;
class Base
{
public:
int a = 1;
};
class Derive :public Base
{
public:
int b = 2;
};
int main()
{
Derive d;
cout << sizeof(d) << endl;
system("pause");
return 0;
}
运行结果:
2、多继承:一个子类有两个或以上直接父类
代码实现:
#include<iostream>
using namespace std;
class Base1
{
public:
int c = 3;
};
class Base2
{
public:
int d = 4;
};
class Derive :public Base1, public Base2
{
public:
int e = 5;
};
int main()
{
Derive d2;
cout << sizeof(d2) << endl;
system("pause");
return 0;
}
运行结果:
3、菱形继承:多个派生类继承了同一个公共基类,而这些派生类又被同一个再次派生的类继承。
代码实现:
#include<iostream>
using namespace std;
class Base
{
protected:
int _base;
public:
void fun()
{
cout << "Base::fun" << endl;
}
};
class C1 :virtual public Base
{
protected:
int _c1;
};
class C2 :virtual public Base
{
protected:
int _c2;
};
class Derive :public C1, public C2
{
private:
int _d;
};
int main()
{
Derive d;
d.fun();
getchar();
return 0;
}
运行结果:
菱形继承中,Derived的对象模型里面保存了两份Base,当我们调用从Base里面继承的fun时,就会出现调用不明确的问题,与此同时造成了数据冗余和二义性的问题,所以,在此C++给了我们一个新的解决方案“虚继承”。在上面的程序中,虚继承就是给C1和C2在继承Base时加上关键字virtural。
所以虚继承:
1. 虚继承解决了在菱形继承体系里面子类对象包含多份父类对象的数据冗余&浪费空间的问题。
2. 虚继承体系看起来好复杂,在实际应用我们通常不会定义如此复杂的继承体系。一般不到万不得已都不要定义菱形结构的虚继承体系结构,因为使用虚继承解决数据冗余问题也带来了性能上的损耗。
以上便是对C++继承方面关于分类和方式,以及该注意的地方的一些小总结。还有继承与静态成员,友元与继承,继承与转换,作用域等在后面再做分析。有不足的地方欢迎大家批评指正。