什么是继承
继承是面向对象复用的重要手段。通过继承定义一个类,继承是类型之间的关系建模,共享公有的东西,实现各自本质不同的东西。
继承的方式分为:
公有继承、保护继承、私有继承
总结:
1.基类的私有成员在派生类中是不能被访问的,如果一些基类成员在类外不想被基类对象直接访问,但需要在派生类中能访问,就定义为保护的。可以看出保护成员限定符是因为继承才出现的。
2.public继承是一个接口继承,保持Is-a原则,每个父类可用的成员对子类也可用,因为每个子类对象也是一个父类对象。也就是说,我就是你。我包含着你。
3.protected/privated继承是一个实现继承,基类的部分成员并未完全成为子类借口的一部分,
是has-a的关系原则,所以非特殊情况不会使用这两种继承关系,在绝大多数场景下使用的都是公有继承。
4.不管是哪种继承方式,在派生类内部都可以访问积累的共有成员和保护成员,但是基类的私有成员在子类中不可见,就是不能访问,但是也继承下来了。
5.使用关键字class时默认的继承方式是private,使用关键字struct时默认public,不过最好显示的写出继承方式。
6.在实际运用中一般都是public继承,很少有其他两种
继承的格式
class A
{
public:
int _a;
}
class B:public A
{
public:
int _b;
}
这种方式就是B公有继承了A
继承与转换—赋值兼容规则–public继承
1.子类对象可以赋值给父类对象(切片/切割)
2.父类对象不能赋值给子类对象
3.父类的指针/引用可以指向子类对象
4.子类的指针/引用不能指向父类对象(但是可以通过强制类型转换实现,不过有危险性)
继承体系中的作用域
1.在集成体系中基类和派生类都有独立的作用域
2.子类和父类中有同名成员,子类成员将会屏蔽父类对成员的直接访问。可以通过加上类名的方法进行调用(隐藏/重定义)
注意:在集成体系中最好不要定义同名的成员
派生类的默认成员函数
在继承体系中,派生类如果没有显示定义这六个成员函数,编译系统将会默认合成
单继承和多继承
1.单继承就是一个子类只有一个直接的父类
2.多继承就是一个子类有两个或者两个以上的直接的父类
菱形继承
class A
{
public:
int _a;
};
class B:public A
{
public:
int _b;
};
class C:public A
{
public:
int _c;
};
class D:public B,public C
{
public:
int _d;
};
int main()
{
D d;
d._a = 10;//这里调用成员的时候就会出现错误,对_a的访问不明确
d._b = 2;
d._c = 3;
d._d = 4;
}
要想解决这个问题也很简单,只需要在调用的时候加上类的域名就好
d.B::_a = 10;
d.C::_a = 20;
运行结果如下图
菱形继承的对象模型
由此可以看出菱形继承存在二义性问题,和数据冗余问题,存在了两份A类的成员
解决方案:
- 在调用对象的时候,指明类域就可以解决二义性问题
- 用虚继承既可以解决数据冗余的问题。
虚继承的格式,B类和C类虚拟继承A
格式:
class B:virtual public A
class C:virtual public A
进行改变后,上面的代码再次运行就不会出现错误,因为_a变为公共的资源,具体可以打开监视窗口,发现只要改变_a的值,B和C类中的_a会同时改变
虚继承的对象模型如下图
在内存中具体的图示(代码就是上面的那个)
总结:
1.虚继承解决了在菱形继承中子类对象包含多分父类对象的数据冗余和浪费空间的问题
2.虚继承体系看起来很复杂,在实际应用中我们通常不会定义如此复杂的集成体系。一般不到万不得已不要使用菱形虚拟继承,因为它解决数据冗余问题的同时也带来了性能上的损耗
这个是我之前写的继承,更加详细一点
[继承] (http://blog.csdn.net/chan0311/article/details/69791225)