类之间的纵向关系——继承
继承
定义:被继承的类叫做基类(父类),继承的类叫派生类(子类),在派生类类名后面加
: 继承方式 基类
class CFather{};
class CSon:public CFather{};
父类(基类)与子类(派生类)之间的关系:
把一些功能相似的类,类中公共的成员单独抽离出来,放到一个类中,这个类就是父类
子类如何使用父类对象 :
通过继承关系,子类可以使用父类的成员。如果子类和父类有同名的成员,默认使用子类的成员,如果想要使用父类的成员,需要在成员名前加上类名::用于显式的指定区分,
son.m_a; //子类成员
son.CSon::m_a; //子类成员
son.CFather::m_a; //父类成员
子类继承父类,相当于将父类的成员包含到自己的类里,所以定义子类对象所占用的空间大小除了子类自身的成员还包括父类的成员。成员在内存空间分布为:先父类成员后子类成员,而每个类中的成员分布与在类中声明的顺序一致。
三种继承方式:
继承方式与访问修饰符一样,都是三种
继承方式:描述了父类的成员在子类中所能使用的范围,即访问控制。继承方式和访问修饰符共同决定了父类成员的访问权限。
继承格式:
class 子类名:继承方式 父类名
class CSonson :public CSon{};
父类 | 子类 |
private: | 不可访问 |
protected: | private: |
public: | private: |
父类 | 子类 |
private: | 不可访问 |
protected: | protected: |
public: | protected: |
父类 | 子类 |
private: | 不可访问 |
protected: | protected: |
public: | public: |
继承下构造析构执行的顺序:
定义子类对象时执行顺序:父构造->子构造->孙构造...| ...孙析构->子析构->父析构。
构造顺序说明:
在子类创建对象的时候,执行子类的构造函数(注意这里并不是直接先执行父类的构造函数),但要先执行子类的构造的初始化列表,在初始化列表中会默认调用父类的无参构造初始化父类成员,如果父类只有带参数的构造,那么需要在子类的初始化参数列表显示的指定父类的初始化。
析构顺序说明:
子类对象的生命周期结束后,因为是子类所以自动调用子类析构,当析构执行完了,才会回收对象分配的空间,当然这个空间包含创建的父类的成员,那么回收父类成员前,自动调用父类的析构。如果是new出来的子类对象,同理。
继承的优点:
多个子类在增加公共方法时,只需要在父类添加一份即可,提高了代码的复用性,扩展性。
隐藏
定义
隐藏:在继承的条件下,父类和子类中有同名的成员,那他们之间的关系称为隐藏。
如果子类中没有函数,则他会调用父类的函数,但当调用父类和子类的同名成员方法时,他会调用子类中的函数,将传输的参数转换成子类中的函数所需类型,如果转换不了则会报错。父类和子类中,同名的但是参数列表不同的函数,他们之间的关系并不是函数重载的关系,作用域不同,必须使用 类名:: 去区分到底该调用哪个函数。
class CFather {void fun();
};
class CSon :public CFather {void fun(int a); //隐藏关系,自动将父类的成员屏蔽了
};
注:父类指针不强转,也可以指向子类对象;而子类的指针必须通过强转才能指向父
亲对象。
父类指针指向子类对象
CFather * p = new CSon;
优点:统一多种子类类型,提高代码的复用性和扩展性
弊端:只能使用父类的成员,不能使用子类的成员