类的继承与派生
继承的概念
使用基类派生新类时,除构造函数和析构函数外 ,基类的所有成员自动成为派生类的成员,包括基类的成员变量和成员函数。
派生类可以 重新定义或修改 基类中已有的成员,包括可以改变基类中成员的 访问权限 。
派生类的定义和大小
派生类的定义:
class 基类名
{
};
class 派生类名: public 基类名
{
};
存储空间的计算:派生类对象 = 基类成员 + 派生类成员 。
使用 sizeof()
函数可以计算对象占用的存储空间。
继承关系的特殊性
基类的友元类或友元函数 ,派生类 不会继承 。
基类是某类的友元类,则派生类也是某类的友元类。
基类的成员函数是某类的友元函数,则派生类继承的成员函数也是某类的友元函数。
基类的静态属性随静态成员被继承,即:静态属性被继承。
派生类通过使用 <类名>::<成员名>
的方式引用或调用静态成员。
有继承关系的类之间的访问
使用 ::
作用域标识符,可以访问基类的非私有成员。
// 在派生类的成员函数中访问
<基类名>::<成员变量名>; // 基类的成员变量
<基类名>::<成员函数名>(); // 基类的成员函数
// 派生类的对象访问
<派生类对象名>.<基类名>::<成员变量名>;
<派生类对象名>.<基类名>::<成员函数名>();
protected 访问范围说明符
基类中的保护成员可以在派生类的成员函数中被访问。
在基类中,一般都将 需要隐藏的成员 说明为保护成员而非私有成员。
多重继承
一个类从多个基类派生的一般格式:
class 派生类名: 继承方式说明符 基类名1, 继承方式说明符 基类名2
{
类体
};
如果派生类中新增了同名成员,则派生类成员将 隐藏 所有基类的同名成员。
当派生类通过多重继承的形式,继承了多个基类,且多个基类中拥有同名成员,需要使用 作用域标识符 ::
来避免二义性。
访问控制
设计继承类时,需要使用 继承方式说明符 指明派生类的继承方式。
继承方式说明符:public
、private
、protected
。
公有继承
继承方式说明符:public
。
当一个类派生自 公有 基类时,基类的 公有 成员也是派生类的 公有 成员,基类的 保护 成员也是派生类的 保护 成员,基类的 私有 成员 不能 直接被派生类访问,但是可以通过调用基类的公有和保护成员来访问。
私有继承
继承方式说明符:private
。
当一个类派生自 私有 基类时,基类的 公有 和 保护 成员将成为派生类的 私有 成员。
保护继承
继承方式说明符:protected
。
当一个类派生自 保护 基类时,基类的 公有 和 保护 成员将成为派生类的 保护 成员。
类型兼容规则
类型兼容规则是指需要基类对象的任何地方,都可以使用公有派生类的对象来代替,也称为 赋值兼容规则 。
在 公有 派生的前提下,有以下3条兼容规则:
- 派生类对象可以赋值给基类对象;
- 派生类对象可以用来初始化基类引用;
- 派生类对象的地址可以赋值给基类指针,即派生类的指针可以赋值给基类的指针。
派生类的构造函数和析构函数
派生类对象 创建 时,总是先执行基类的构造函数,再执行派生类的构造函数。
派生类对象 消亡 时,总是先执行派生类的析构函数,再执行基类的析构函数。
定义派生类构造函数的一般格式如下:
派生类名::派生类名(参数表): 基类名1(基类1的参数表), 基类名2(基类2的参数表)
{
派生类构造函数体
};
派生类构造函数的执行次序:
- 调用基类构造函数,调用顺序按照:它们 被继承时声明的顺序 ;
- 对派生类新增的成员变量 初始化,调用顺序按照:它们在 类中声明的顺序 ;
- 执行派生类的构造函数体中的内容。
类之间的关系
使用已有类编写新的类有两种方式:继承 和 组合 。
继承 关系也称 “is a” 关系或 “是” 关系。具有 传递性 。
组合 关系也称 “has a” 关系或 “有” 关系。表现为 封闭类。
多层次的派生
在 C++ 中,派生是可以多层次的。
根据是否直接继承关系,可以分为:直接基类 和 间接基类 。
一个基类可以被多次说明为某个派生类的间接基类,但只能成为一次直接基类。
当生成派生类的对象时,会从最顶层的基类开始逐层往下执行所有基类的构造函数,最后执行派生类自身的构造函数。
当派生类的对象消亡时,会执行自身的析构函数,然后自底向上执行各个基类的析构函数。
基类与派生类指针的互相转换
在 公有 派生的情况下,因为派生类对象也是基类对象,所以派生类对象可以赋值给基类对象 。
对于指针类型,可以使用基类指针指向派生类对象,也可以将派生类的指针直接赋值给基类指针。
注意:即使基类指针指向一个派生类对象,不能通过基类指针访问派生类的成员函数。