5.2基类和派生类 在C++中,当一个类被其他类继承时,被继承的类称为基类( base class)。继承其他类 特性的类称为派生类( derived class从本质上看,基类是具有一个类集合中的公共特性 派生类在继承基类特性的同时可以加入自己独有的特性 基类与派生类之间反映出下述三种不同的现象: (1)派生类是基类的具体化。即模拟概念层次,表示“is-a”的关系 (2)派生类是基类的延迟定义。 可以定义一个抽象基类,定义一些操作,使它们服从一定的协议,但许多可能并未实现, 然后定义非抽象的派类,实现抽象基类中定义的行为。这时派生类不是基类的具体化,而是 抽象类的实现。在JAVA中,有专门的纯虚类,称为接口,其作用就是为不同的类提供一个 统一的接口,同时间接实现多继承(JAVA不支持多继承) (3)派生类是基类的结合。 当一个派生类有多于一个的基类时,它们组合在一起形成具有所有基类行为的类型。这 时要注意,不要用继承表达聚合关系。 5.2.1基类与派生类的说明 先看一个例子。 例5.1]派生类的说明EX5_1CPP。 继承基类的派生类定义的一般形式: class derived class name access specifier base class name }; 其中 access specifier可以是3个关键字之一: public、 private(默认值)或 protected 派生类也称为子类、导出类。它具有下述特点: (1)可在基类所提供有基础上包含新成员 (2)可在自己类中隐藏基类的任何成员 (3)为新类重新定义基类中的函数 [例5.2]子类的特点EX52.CPP。 5.2.2派生类的继承权与访问域 派生类的继承权如果不能有效在加以限制,就不能按照实际情况表达求解问题的复杂 性。因此访问权限是一个很重要的问题 1)对于基类的私有成员,派生类及派生类的使用者无权访问 (2)对于基类的公有成员,则按派生类的定义,分为三种情况 ①私有派生,继承基类的公有成员作为自己的私有成员,这些成员只能被派生类的成员 函数访问。 Access specifier是 private或省略 [例5.3]私有派生EX5_3.CPP
5.2 基类和派生类 在 C++中,当一个类被其他类继承时,被继承的类称为基类(base class)。继承其他类 特性的类称为派生类(derived class)。从本质上看,基类是具有一个类集合中的公共特性, 派生类在继承基类特性的同时可以加入自己独有的特性。 基类与派生类之间反映出下述三种不同的现象: (1)派生类是基类的具体化。即模拟概念层次,表示“is-a”的关系。 (2)派生类是基类的延迟定义。 可以定义一个抽象基类,定义一些操作,使它们服从一定的协议,但许多可能并未实现, 然后定义非抽象的派类,实现抽象基类中定义的行为。这时派生类不是基类的具体化,而是 抽象类的实现。在 JAVA 中,有专门的纯虚类,称为接口,其作用就是为不同的类提供一个 统一的接口,同时间接实现多继承(JAVA 不支持多继承)。 (3)派生类是基类的结合。 当一个派生类有多于一个的基类时,它们组合在一起形成具有所有基类行为的类型。这 时要注意,不要用继承表达聚合关系。 5.2.1 基类与派生类的说明 先看一个例子。 [例 5.1] 派生类的说明 EX5_1.CPP。 继承基类的派生类定义的一般形式: class derived_class_name:access_specifier base_class_name { …… }; 其中 access_specifier 可以是 3 个关键字之一:public、private(默认值)或 protected。 派生类也称为子类、导出类。它具有下述特点: (1)可在基类所提供有基础上包含新成员; (2)可在自己类中隐藏基类的任何成员; (3)为新类重新定义基类中的函数; [例 5.2] 子类的特点 EX5_2.CPP。 5.2.2 派生类的继承权与访问域 派生类的继承权如果不能有效在加以限制,就不能按照实际情况表达求解问题的复杂 性。因此访问权限是一个很重要的问题。 (1)对于基类的私有成员,派生类及派生类的使用者无权访问。 (2)对于基类的公有成员,则按派生类的定义,分为三种情况: ①私有派生,继承基类的公有成员作为自己的私有成员,这些成员只能被派生类的成员 函数访问。Access_specifier 是 private 或省略。 [例 5.3] 私有派生 EX5_3.CPP
②公有派生是基类中所有的公有成员,在派生类中也都是公有的。它不必一一说明,而 在派生类定义时,在基类前加一个 public关键字。如: class public b ③保护派生,基类的公有成员和保护成员在派生类中是保护成员,仅能在派生类的成员 中被使用,而不允许派生类的对象使用。 Access specifier是 protected 表5.1列出了成员访问控制的各种情况。 继承性质 基类性质 派生类性质 Public rotected Protected Private 不可访问 I Protected [ Public Protected Protected Protected 不可访问 private Public Protected (3)派生类对基类成员直接访问问题 派生类不能访问基类的私有成员,若要访问必须使用基类的接口,即通过基成员函数。 如何直接访问类成员,有两种方法可选: ①在类定义体中增加保护段( protected),将基类私有成员提供派生类访问的部分放置 在保护段 ②将需要访问基类私有成员的派生类成员函数声明为基类的友元 例5.4]派生类对基类成员的直接访问EX54.CPP。(?) (4)访问域的调整规则 使用作用域符(::)可以调整访问域,但要注意其限制条件 ①访问声明只能对变量或函数名,不能说明类型和参数:重载函数只需一个声明即可 ②不能对私有段成员作访问声明,必须保护封装性。 ③只能在相应的段(保护或公有段)作访问声明,不能改变所属段。即基类成员被调整 后,在派生类中的访问权限既不能扩大也不能缩小。基类中的公有成员只能被调整为公有成 员,保护成员只能被调整为保护成员,私有成员不可调整。 例5.5]访问域的调整EX55.CPP class base i protected int b. private
②公有派生是基类中所有的公有成员,在派生类中也都是公有的。它不必一一说明,而 在派生类定义时,在基类前加一个 public 关键字。如: class:public b { …… }; ③保护派生,基类的公有成员和保护成员在派生类中是保护成员,仅能在派生类的成员 中被使用,而不允许派生类的对象使用。Access_specifier 是 protected。 表 5.1 列出了成员访问控制的各种情况。 继承性质 基类性质 派生类性质 Public Public Public Protected Protected Private 不可访问 Protected Public Protected Protected Protected Private 不可访问 private Public Private Protected Private Private 不可访问 (3)派生类对基类成员直接访问问题。 派生类不能访问基类的私有成员,若要访问必须使用基类的接口,即通过基成员函数。 如何直接访问类成员,有两种方法可选: ①在类定义体中增加保护段(protected),将基类私有成员提供派生类访问的部分放置 在保护段。 ②将需要访问基类私有成员的派生类成员函数声明为基类的友元。 [例 5.4] 派生类对基类成员的直接访问 EX5_4.CPP。(?) (4)访问域的调整规则 使用作用域符(::)可以调整访问域,但要注意其限制条件: ①访问声明只能对变量或函数名,不能说明类型和参数;重载函数只需一个声明即可。 ②不能对私有段成员作访问声明,必须保护封装性。 ③只能在相应的段(保护或公有段)作访问声明,不能改变所属段。即基类成员被调整 后,在派生类中的访问权限既不能扩大也不能缩小。基类中的公有成员只能被调整为公有成 员,保护成员只能被调整为保护成员,私有成员不可调整。 [例 5.5] 访问域的调整 EX5_5.CPP。 class base { public: int a; protected: int b; private: int c; };
class derived: base base: a: //correct base: c: /error base: b: //correct 若基类和派生类具有同名成员,则基类的该成员被编译器隐藏起来。若在派生类中调用 继承来的被隐蔽的成员,作为访问声明需要明确指定基类类范围:而非同名成员,派生类在 使用继承成员时可视为是在该派生类范围,且不必对这些成员重新定义 5.2.3派生类的构造函数和析构函数 基类往往有构造函数和析构函数,但创建和结束派生类对象时,如何调用它们,这是下 面需要讨论的问题。 (1)执行原则:当基类和派生类都具有构造函数和析构函数时,将按类派生的顺序执 行构造函数,而按相反的顺序执行析构函数 (2)派生类构造函数:当派生类本身需要构造函数,或者是在定义派生类对象时,其 相应的基类对象需要调用带参数的构造函数,就必须定义派生类的构造函数 定义格式如下 derived constructor(arg list ): base(arg list) //body of derived constructor 这里建立了一个变量传递链,首先将基类和派生类所需的所有变量都传递给构造函数。 然后,用派生类构造函数的扩展说明形式,将某些变量传递给基类 例5.6]从派生类向基类传递变量EX5_6.CPP (3)有关说明 若基类使用缺省构造函数或不带参数的构造函数,则在派生类中定义构造函数时可略去 base(arg_list)”,此时若派生类还需要初始化,则可不定义构造函数 派生类是否要定义析构函数与所属基类无关。若派生类在退出其定义域前需要作释放内 存等处理,就需要定义析构函数 例5.刁定义学校人事管理的类及其调用情况显示实例EX5_5.CPP
class derived:base{ public: base::a;//correct base::c;//error base::b;//error protected: base::b;//correct base::a;//error base::c;//error }; 若基类和派生类具有同名成员,则基类的该成员被编译器隐藏起来。若在派生类中调用 继承来的被隐蔽的成员,作为访问声明需要明确指定基类类范围;而非同名成员,派生类在 使用继承成员时可视为是在该派生类范围,且不必对这些成员重新定义。 5.2.3 派生类的构造函数和析构函数 基类往往有构造函数和析构函数,但创建和结束派生类对象时,如何调用它们,这是下 面需要讨论的问题。 (1)执行原则:当基类和派生类都具有构造函数和析构函数时,将按类派生的顺序执 行构造函数,而按相反的顺序执行析构函数。 (2)派生类构造函数:当派生类本身需要构造函数,或者是在定义派生类对象时,其 相应的基类对象需要调用带参数的构造函数,就必须定义派生类的构造函数。 定义格式如下: derived_constructor(arg_list):base(arg_list) { //body of derived constructor }; 这里建立了一个变量传递链,首先将基类和派生类所需的所有变量都传递给构造函数。 然后,用派生类构造函数的扩展说明形式,将某些变量传递给基类。 [例 5.6] 从派生类向基类传递变量 EX5_6.CPP。 (3)有关说明 若基类使用缺省构造函数或不带参数的构造函数,则在派生类中定义构造函数时可略去 “:base(arg_list)”,此时若派生类还需要初始化,则可不定义构造函数。 派生类是否要定义析构函数与所属基类无关。若派生类在退出其定义域前需要作释放内 存等处理,就需要定义析构函数。 [例 5.7] 定义学校人事管理的类及其调用情况显示实例 EX5_5.CPP