第六章 继承与派生 主要内容: ●继承与派生的基本概念:继承与类聚集的关系、基类与派生类的概念、单继承与多继承、 基类与派生类的关系以及派生类的定义及其构成。 ●对基类成员的访问控制。包括在public、private和protected三种继承方式下,基类的实 例、直接派生类、直接派生类实例和间接派生类中对基类中public、private和protected 成员的访问控制。 ● 派生类的构造函数和析构函数。包括:派生类构造函数的定义格式、对基类成员和子对 象成员的初始化、派生类构造函数的执行顺序,多继承时对基类构造函数的调用顺序、 派生类析构函数的定义格式及其执行顺序。 ·二义性问题:二义性的概念、产生二义性的原因、解决二义性问题的成员名限定法和支 配原则 ● 虚基类。引入虚基类的原因、虚基类的定义方法、虚基类与非虚基类的区别、虚基类构 造函数的定义及其调用 ● 子类型关系:子类型关系的概念、实现以及赋值兼容原则。 选择 1①提供了类对外界的接口,②是类的内部实现,而③不允许外界访问,但 允许派生类的成员访问,这样既有一定的隐藏能力,又提供了开放的接口 A公有成员 B私有成员 C私有成员函数 D保护成员 答案:①A②B ③D 注释:类成员访问权限的定义。派生类中包含了基类中的所有成员,但基类的私有成员在派 生类中是隐藏的,不能被访问。派生类的缺省继承方式是private。 2下列关于继承的描述中,错误的是。 A析构函数不能被继承 B派生类是基类的组合 C派生类的成员除了它自己的成员外,还包含了它的基类的成员 D派生类中继承的基类成员的访问权限到派生类保持不变 答案D 注释:派生类不能继承基类的构造函数和析构函数,也不能访问基类的私有成员,而对基类 的公有成员和保护成员的访问权限则随着派生类对基类的继承方式而改变。 3派生类可以分为①和②。由②得到的派生类,其基类的所有公有成员都只 能成为它的私有成员,这些私有成员只能被派生类的成员函数访问,而③无权访问:由 ①得到的派生类,其基类中所有公有成员在派生类中也都是公有的。 A公有派生B派生类的实例C私有派生 D派生类的构造函数 答案:①A②C③B 注释:派生类的继承方式由三种:公有继承public、私有继承private和保护继承protected。 直接派生类从基类公有继承时,基类的公有成员和保护成员在派生类中仍为公有成员和保护 成员,因此派生类的实例可以访问基类的公有成员,除此之外,对基类中的其他成员不能访 问:直接派生类从基类私有继承时,基类的公有成员和保护成员在派生类中都改变为私有成 员,这些成员只能在派生类类体内访问:直接从基类保护继承时,基类的公有成员在派生类 中改变为保护成员,基类的保护成员在派生类中仍为保护成员。 1
第六章 继承与派生 主要内容: z 继承与派生的基本概念:继承与类聚集的关系、基类与派生类的概念、单继承与多继承、 基类与派生类的关系以及派生类的定义及其构成。 z 对基类成员的访问控制。包括在 public、private 和 protected 三种继承方式下,基类的实 例、直接派生类、直接派生类实例和间接派生类中对基类中 public、private 和 protected 成员的访问控制。 z 派生类的构造函数和析构函数。包括:派生类构造函数的定义格式、对基类成员和子对 象成员的初始化、派生类构造函数的执行顺序,多继承时对基类构造函数的调用顺序、 派生类析构函数的定义格式及其执行顺序。 z 二义性问题:二义性的概念、产生二义性的原因、解决二义性问题的成员名限定法和支 配原则 z 虚基类。引入虚基类的原因、虚基类的定义方法、虚基类与非虚基类的区别、虚基类构 造函数的定义及其调用 z 子类型关系:子类型关系的概念、实现以及赋值兼容原则。 选择 1 ① 提供了类对外界的接口, ② 是类的内部实现,而 ③ 不允许外界访问,但 允许派生类的成员访问,这样既有一定的隐藏能力,又提供了开放的接口 A 公有成员 B 私有成员 C 私有成员函数 D 保护成员 答案:①A ②B ③D 注释:类成员访问权限的定义。派生类中包含了基类中的所有成员,但基类的私有成员在派 生类中是隐藏的,不能被访问。派生类的缺省继承方式是 private。 2 下列关于继承的描述中,错误的是 。 A 析构函数不能被继承 B 派生类是基类的组合 C 派生类的成员除了它自己的成员外,还包含了它的基类的成员 D 派生类中继承的基类成员的访问权限到派生类保持不变 答案 D 注释:派生类不能继承基类的构造函数和析构函数,也不能访问基类的私有成员,而对基类 的公有成员和保护成员的访问权限则随着派生类对基类的继承方式而改变。 3 派生类可以分为 ① 和 ② 。由 ② 得到的派生类,其基类的所有公有成员都只 能成为它的私有成员,这些私有成员只能被派生类的成员函数访问,而 ③ 无权访问;由 ① 得到的派生类,其基类中所有公有成员在派生类中也都是公有的。 A 公有派生 B 派生类的实例 C 私有派生 D 派生类的构造函数 答案:①A ②C ③B 注释:派生类的继承方式由三种:公有继承public、私有继承private和保护继承protected。 直接派生类从基类公有继承时,基类的公有成员和保护成员在派生类中仍为公有成员和保护 成员,因此派生类的实例可以访问基类的公有成员,除此之外,对基类中的其他成员不能访 问;直接派生类从基类私有继承时,基类的公有成员和保护成员在派生类中都改变为私有成 员,这些成员只能在派生类类体内访问;直接从基类保护继承时,基类的公有成员在派生类 中改变为保护成员,基类的保护成员在派生类中仍为保护成员。 1
4派生类的构造函数的成员初始化列表中,不能包含一。 A基类的构造函数B派生类子对象的初始化 C基类中子对象的初始化D派生类中一般数据成员的初始化 答案:C 注释:派生类的构造函数只负责对其基类成员(调用基类的构造函数)、新定义的子对象成 员以及一般数据成员进行初始化。 5多继承的构造顺序可以分为如下4步: ①所有非虚基类的构造函数按照它们被继承的顺序构造: ②所有虚基类的构造函数按照它们被继承的顺序构造: ③所有子对象的构造函数体按照它们声明的顺序构造: ④派生类自己的构造函数体 A④③①②B②④③① C②①③④ D③④②① 答案:C 6关于多继承二义性的描述中,错误的是 。 A一个派生类的两个基类中都有某个同名成员,在派生类中对这个成员的访问可能出现二义 性 B解决二义性的最常用的方法是对成员名的限定法 C基类和派生类中同时出现的同名函数,也存在二义性问题 D一个派生类是从两个基类派生出来的,而这两个基类又有一个共同的基类,对该基类成员 进行访问时,可能出现二义性 答案:C 注释:基类和派生类中同时出现同名函数,符合支配原则。 7带有虚基类的多层派生类构造函数的成员初始化列表中都要列出虚基类的构造函数,这样 将对虚基类的的子对象初始化 A与虚基类下面的派生个数有关B多次 C两次 D一次 答案:D 注释:从虚基类直接或间接派生的派生类的构造函数的成员初始化列表中都要列出对虚基类 构造函数的调用,但只有用于建立对象的最派生类的构造函数调用虚基类的构造函数,而派 生类的所有基类中列出的对虚基类的构造函数的调用在执行中被忽略,从而保证对虚基类子 对象只初始化一次。 8关于子类型的描述中,错误的是 A子类型关系是可逆的 B公有派生类的对象可以初始化基类的引用 C只有在公有继承下,派生类是基类的子类型 D子类型关系是可传递的 答案:A 注释:公有继承时,派生类是基类的子类型,反之则不成立,即子类型关系不可逆,但可传 递。 具有子类型关系的基类和派生类的对象之间满足如下赋值兼容原则 ●派生类的对象可以赋值给基类的对象,即用派生类对象中从基类继承来的成员,逐个赋 值给基类对象的成员 ●派生类的成员可以初始化基类的引用 ●派生类的对象的地址可以赋值给指向基类的指针 填空 2
4 派生类的构造函数的成员初始化列表中,不能包含 。 A 基类的构造函数 B 派生类子对象的初始化 C 基类中子对象的初始化 D 派生类中一般数据成员的初始化 答案:C 注释:派生类的构造函数只负责对其基类成员(调用基类的构造函数)、新定义的子对象成 员以及一般数据成员进行初始化。 5 多继承的构造顺序可以分为如下 4 步: ① 所有非虚基类的构造函数按照它们被继承的顺序构造; ② 所有虚基类的构造函数按照它们被继承的顺序构造; ③ 所有子对象的构造函数体按照它们声明的顺序构造; ④ 派生类自己的构造函数体 A ④③①② B ②④③① C ②①③④ D ③④②① 答案:C 6 关于多继承二义性的描述中,错误的是 。 A 一个派生类的两个基类中都有某个同名成员,在派生类中对这个成员的访问可能出现二义 性 B 解决二义性的最常用的方法是对成员名的限定法 C 基类和派生类中同时出现的同名函数,也存在二义性问题 D 一个派生类是从两个基类派生出来的,而这两个基类又有一个共同的基类,对该基类成员 进行访问时,可能出现二义性 答案: C 注释:基类和派生类中同时出现同名函数,符合支配原则。 7 带有虚基类的多层派生类构造函数的成员初始化列表中都要列出虚基类的构造函数,这样 将对虚基类的的子对象初始化 。 A 与虚基类下面的派生个数有关 B 多次 C 两次 D 一次 答案:D 注释:从虚基类直接或间接派生的派生类的构造函数的成员初始化列表中都要列出对虚基类 构造函数的调用,但只有用于建立对象的最派生类的构造函数调用虚基类的构造函数,而派 生类的所有基类中列出的对虚基类的构造函数的调用在执行中被忽略,从而保证对虚基类子 对象只初始化一次。 8 关于子类型的描述中,错误的是 A 子类型关系是可逆的 B 公有派生类的对象可以初始化基类的引用 C 只有在公有继承下,派生类是基类的子类型 D 子类型关系是可传递的 答案:A 注释:公有继承时,派生类是基类的子类型,反之则不成立,即子类型关系不可逆,但可传 递。 具有子类型关系的基类和派生类的对象之间满足如下赋值兼容原则 z 派生类的对象可以赋值给基类的对象,即用派生类对象中从基类继承来的成员,逐个赋 值给基类对象的成员 z 派生类的成员可以初始化基类的引用 z 派生类的对象的地址可以赋值给指向基类的指针 填空 2
1类继承中缺省的继承方式是私有继承private。. 2当用protected继承从基类派生一个类时,基类的public.成员成为派生类的保护protected 成员,protected成员成为派生类的保护protected)成员。 3多继承中时,多个基类中拥有一个同名的成员,则不能用调整访问权限确定对该成员的访 问。 注释:二义性检查在访问控制权限或类型检查前进行,访问控制权限不同或类型不同不能解 决二义性问题。 4派生类的对象可以作为基类的对象处理。 阅读程序写出运行结果 include class Data { public: Data (int x) Data:x=x; cout<<"Data cons."<<endl: } private: int x; class Base { public: Base(int x):dl(x){cout<<"Base cons."<<endl; ~Base(){cout<<"Base des."<<endl; private: Data d1: 方 class Derived:public Base { public: Derived (int x):Base(x).d2(x){cout<<"Derived cons."<<endl: ~Derived(){cout<<"Derived des."<<endl; private: Data d2: void main() { Derived obj(5); } 答案: Data cons. 3
1 类继承中缺省的继承方式是私有继承private。 2 当用protected继承从基类派生一个类时,基类的public成员成为派生类的保护protected 成员,protected成员成为派生类的保护protected成员。 3 多继承中时,多个基类中拥有一个同名的成员,则不能用调整访问权限确定对该成员的访 问。 注释:二义性检查在访问控制权限或类型检查前进行,访问控制权限不同或类型不同不能解 决二义性问题。 4 派生类的对象可以作为基类的对象处理。 阅读程序写出运行结果 # include class Data { public: Data (int x) { Data::x=x; cout<<”Data cons.”<<endl; } private: int x; }; class Base { public: Base(int x):d1(x) {cout<<”Base cons.”<<endl; ~Base() {cout<<”Base des.”<<endl; } private: Data d1; }; class Derived: public Base { public: Derived (int x) :Base(x),d2(x) {cout<<”Derived cons.” <<endl;} ~Derived() { cout<<”Derived des.”<<endl;} private: Data d2; }; void main() { Derived obj(5); } 答案: Data cons. 3
Base cons. Data cons. Derived cons. Derived des. Data des. Base des. Data des. 2 include class Base public: Base(int i) x=ii cout<<"Constructor of Base."<<endl; ~Base(){cout<<"Destructor of Base."<<endl;) void show(){cout<<"x="<<x<<endl;} private: int x; }: class Derived:public Base { public: Derived (int i):Base(i).d(i)fcout<<"Constructor of Derived."<<endl: private: Base d; void main() Derived obj(5); Obj.show(); } 答案: Constructor of Base. Constructor of Base. Constructor of Derived X=5 Destructor of Base. Destructor of Base. 注释:派生类析构函数的定义与基类无关,与没有继承关系的新类中析构函数的定义完全相 同。它只负责对新增成员的清理工作,系统会自己调用基类及子对象的析构函数进行相应的 4
Base cons. Data cons. Derived cons. Derived des. Data des. Base des. Data des. 2 # include class Base { public: Base(int i) { x=ii cout<<”Constructor of Base .”<<endl; } ~Base() {cout<<”Destructor of Base.”<<endl;} void show() {cout<<”x=”<<x<<endl;} private: int x; }; class Derived: public Base { public: Derived (int i) :Base(i),d(i) {cout<<”Constructor of Derived .” <<endl;} private: Base d; }; void main() { Derived obj(5); Obj.show(); } 答案: Constructor of Base. Constructor of Base. Constructor of Derived . x=5 Destructor of Base. Destructor of Base. 注释:派生类析构函数的定义与基类无关,与没有继承关系的新类中析构函数的定义完全相 同。它只负责对新增成员的清理工作,系统会自己调用基类及子对象的析构函数进行相应的 4
清理工作。 程序设计 定义一个点类Point,.并由此派生出一个圆类 参考答案: include class Point { int x,y; public: Point(int x,int y)(Point::x=x;Point::y=y;) int getx(){return x:) int gety()freturn y;) } class Circle:public Point { int r; public: Circle int x,int y,int r):Point(x,y) { Circle::r=r; } int getr(){return r;) } void main() Circle c(4,5,3); Point *p=&c; coutgetx0Kgety0K<")”<end; } 程序执行结果: 圆半径:3,圆心:(4,5) 5
清理工作。 程序设计 定义一个点类 Point,并由此派生出一个圆类 参考答案: # include class Point { int x,y; public: Point(int x, int y) {Point::x=x; Point::y=y;} int getx() {return x:} int gety() {return y;} }; class Circle:public Point { int r; public: Circle ( int x, int y, int r):Point(x,y) { Circle::r=r; } int getr() {return r;} }; void main() { Circle c(4,5,3); Point *p=&c; coutgetx()gety()<<”)”<<endl; } 程序执行结果: 圆半径:3,圆心:(4,5) 5