继承
继承是什么?
继承机制是面向对象程序设计实现代码复用的重要手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能。
采用继承手段生成的类称为派生类
继承呈现了面对想对象程序设计的层次结构,体现了由简单到复杂的认知过程。
继承是类设计层次的复用。
为什么需要继承?
假设现在不存在继承机制,现在我们实现了一个person
类,这个类中含有人的一切属性及行为。
我们现在想就人生阶段来实现对person
的详细划分,处于不同人生阶段的人有不同的事情需要做,在实现时,即不同阶段的人有不同的方法及一些属性。
如果没有继承,我们想要这么干,似乎只能通过重新新建一些类,将人拥有的一切属性及行为封装进去,再往里面封装一些专属的方法及属性。这么干的话,会产生大量重复的代码,使得程序体积庞大,不好
而继承虽然也要新建类,但只需要再新类后面添加继承的语法,就可以将人的一切属性及行为继承入新类,我们只需要再添加专属方法属性即可,大大提高了代码复用率。
现假设person
类如下:
只要是个人,他应该基本具有以下几个基本属性及行为。
class Person {
public:
void Eat() {}//进食
void walk() {}//行走
void speak() {}//讲话
private:
string eye;//眼睛颜色
string father;//父亲名字
string mother;//母亲名字
};
如果没有继承,我们想要实现一个青年Youth
类,我们只能这样实现:
class Youth {
public:
void marry() {}//结婚
void work() {}//工作
public:
void Eat() {}
void walk() {}
void speak() {}
private:
int foot;
int hand;
int eye;
string father;
string mother;
};
就多了两个方法,却要把人的属性及方法全部重写一遍,这很烦。
然后有了继承,我们只需要这么写就够了,极大提高了代码复用率。
继承的定义
在下图中,Person
是父类,也称作基类,Youth
是子类,也称作派生类
继承关系及访问限定符
继承方式 | 访问限定符 |
---|---|
public继承 | public访问 |
protected继承 | protected访问 |
private继承 | private访问 |
继承基类成员访问方式的变化
基类成员类别/派生类成员类别/继承方式 | public继承 | protected继承 | private继承 |
---|---|---|---|
public成员 | 派生类的public成员 | 派生类的protected成员 | 派生类的private成员 |
protected成员 | 派生类的protected成员 | 派生类的protected成员 | 派生类的private成员 |
private成员 | 派生类中不可见 | 派生类中不可见 | 派生类中不可见 |
这个表我挑一个举例解释:
比如基类的protected
成员,被派生类以(public/protected
)方式继承,就会成为派生类的protected
成员,若是被以private
方式继承,则会成为派生类的private
成员
【Note】:
1.基类的private成员无论被以什么方式继承,在派生类中都是不可见的。此处的不可见指的是基类的私有成员仍被继承到派生类中,不过语法上限制派生类对象访问这些基类的私有成员,无论在类内还是类外。
2.protected修饰基类的成员,可以使该成员在类外不能被直接访问吗,但允许派生类访问。(由此可见,protected
修饰限定符是因继承出现的)
3.若继承时继承方式处为class
,则默认继承方式为private
;
若继承时继承方式处为struct
,则默认继承方式为public
4.使用protected/private
方式继承下来的成员只能在派生类类内使用,实际中扩展维护性不强
基类和派生类对象赋值转换
1.派生类对象可以赋值给基类的对象/基类的指针/基类的引用,专业说法叫做切片,意指将派生类中的基类部分切割出来用来赋值
2.基类对象不能赋值给派生类对象! 为什么?因为基类中不可能有派生类专有的成员!总不能无中生有叭
3.基类的指针可以通过强制类型转换赋值给派生类,但是必须是基类的指针是指向派生类对象时才是安全的
继承中的作用域
1.在继承体系中,基类与派生类都有独立的作用域
2.若是派生类存在与基类同名的成员,将形成隐藏,派生类成员将屏蔽基类对同名成员的直接访问。(只要函数名相同,就会构成隐藏)
派生类的默认成员函数
1.派生类的构造函数必须调用基类的构造函数,去初始化基类的成员。若是基类没有默认构造函数,则需要在派生类的构造函数的初始化列表中显式调用基类的构造函数
2.派生类的拷贝构造函数必须调用基类的拷贝构造函数完成派生类的拷贝初始化
3.派生类的operator=
必须调用基类的operator=
完成基类的赋值
4.派生类构造顺序:先构造基类,再构造派生类;
派生类的析构顺序:先析构派生类,再析构基类
友元关系不能继承
即基类友元不能访问派生类的私有与保护成员
静态成员只能存在一个
若基类中存在静态成员,则整个继承体系中只会存在一个这样的成员
菱形继承及菱形虚拟继承
单继承:一个子类只有一个直接父类,此时的继承关系称为单继承
多继承:一个子类有两个及以上父类,此时的继承关系称为多继承
菱形继承:多继承的特殊情况,如下
可以看出,A的成员被B与D继承,B与D又被C继承,这样会导致A的成员被C继承两份,一份来自B,一份来自D,造成了数据冗余及二义性。
为了避免这个问题,B与D可以采用虚拟继承,则可以避免这个问题。
虚拟继承只需要再继承方式之前加一个virtual
修饰即可,如下:
class A {
//
};
class B :virtual public A {
//
};
class D :virtual public A {
//
};
class C :public B,public C {
//
};
但,虚拟继承千万不要去其他情况下使用嗷!