第5章 继承与派生
- 格式:doc
- 大小:629.00 KB
- 文档页数:16
1.什么是类的继承与派生?继承性是面向对象程序设计的第二个重要特性,通过继承实现了数据抽象基础上的代码重用。
继承是对许多问题中分层特性的一种自然描述,因而也是类的具体化和被重新利用的一种手段,它所表达的就是一种对象类之间的相交关系。
它使得某类对象可以继承另外一类对象的特征和能力。
继承所具有的作用有两个方面:一方面可以减少代码冗余;另一方面可以通过协调性来减少相互之间的接口和界面。
通过继承方式定义的子类也称为派生类。
2.类的三种继承方式之间的区别是什么?类的继承方式有public(公有)继承、protected(保护)继承和private(私有)继承三种。
对于不同的继承方式,会导致基类成员原来的访问属性在派生类中有所变化。
表5.1列出了不同继承方式下基类成员访问属性的变化情况。
表5.1 不同继承方式下基类成员的访问属性说明:该表第1列给出3种继承方式,第1行给出基类成员的3种访问属性。
其余单元格内容为基类成员在派生类中的访问属性。
从表中可以看出:(1) 基类的私有成员在派生类中均是不可访问的,它只能由基类的成员访问。
(2) 在公有继承方式下,基类中的公有成员和保护成员在派生类中的访问属性不变。
(3) 在保护继承方式下,基类中的公有成员和保护成员在派生类中均为保护的。
(4) 在私有继承方式下,基类中的公有成员和保护成员在派生类中均为私有的。
需要注意的是:保护成员与私有成员唯一的不同是当发生派生后,处在基类protected区的成员可被派生类直接访问,而私有成员在派生类中是不可访问的。
在同一类中私有成员和保护成员的用法完全一样。
3.派生类能否直接访问基类的私有成员?若否,应如何实现?派生类不能直接访问基类的私有成员。
具体实现方式:(1) 在类定义体中增加保护段为了便于派生类的访问,可以将基类私有成员中需提供给派生类访问的部分定义为保护段成员。
保护段成员可以被它的派生类访问,但是对于外界是隐藏起来的。
这样,既方便了派生类的访问,又禁止外界对它的派生类访问。
第 5 章继承与派生5.1 继承与派生的看法图图图图25.2 派生类的声明方式5.3 派生类的构成数据构造 (C++版 )图图5.4 派生类成员的接见属性5.4.1 公用继承C++ 面向对象程序设计3 5.4.2 私有继承图5.4.3 保护成员和保护继承图4数据构造 (C++版 ) 5.4.4 多级派生时的接见属性图5.5 派生类的构造函数和析构函数5.5.1 简单的派生类的构造函数图图5.5.2 有子对象的派生类的构造函数C++ 面向对象程序设计5图5.5.3 多层派生时的构造函数图5.5.4 派生类构造函数的特别形式5.5.5 派生类的析构函数5.6 多重继承5.6.1 声明多重继承的方法5.6.2 多重继承派生类的构造函数5.6.3 多重继承引起的二义性问题6数据构造 (C++版 )图图图图图C++ 面向对象程序设计7图图5. 205.6.4 虚基类1.虚基类的作用图8数据构造 (C++版 )图图图2.虚基类的初始化C++ 面向对象程序设计9图3.虚基类的简单应用举例5.7 基类与派生类的变换图图10数据构造 (C++版 )5.8 继承与组合5.9 继承在软件开发中的重要意义习题1.将例 5.1 的程序片段补充和改写成一个圆满、正确的程序,用公用继承方式。
在程序中应包括输入数据的函数,在程序运行时输入num,name,sex,age,addr的值,程序应输出以上 5 个数据的值。
2.将例 5.2 的程序片段补充和改写成一个圆满、正确的程序,用私有继承方式。
在程序中应包括输入数据的函数,在程序运行时输入num,name,sex,age,addr的值,程序应输出以上 5 个数据的值。
3.将例 5.3 的程序更正、补充,写成一个圆满、正确的程序,用保护继承方式。
在程序中应包括输入数据的函数。
4.更正例5.3 的程序,改为用公用继承方式。
上机调试程序,使之能正确运行并获取正确的结果。
对这两种继承方式作比较剖析,考虑在什么情况下二者不能够互相代替。
第5章继承与派生5.1继承与派生的概念
图5.1
图5.2
图5.3
图5.4
2
C++面向对象程序设计
图5.5
5.2派生类的声明方式
5.3派生类的构成
图5.6
5.4派生类成员的访问属性
5.4.1公用继承
C++面向对象程序设计 3 5.4.2私有继承
图5.7
5.4.3保护成员和保护继承
图5.8
4
C++面向对象程序设计5.4.4多级派生时的访问属性
图5.9
5.5派生类的构造函数和析构函数5.5.1简单的派生类的构造函数
图5.10
图5.11
5.5.2有子对象的派生类的构造函数
C++面向对象程序设计 5
图5.12
5.5.3多层派生时的构造函数
图5.13
5.5.4派生类构造函数的特殊形式
5.5.5派生类的析构函数
5.6多重继承
5.6.1声明多重继承的方法
5.6.2多重继承派生类的构造函数
5.6.3多重继承引起的二义性问题
6
C++面向对象程序设计
图5.14
图5.15
图5.16
图5.17
图5.18
C++面向对象程序设计7
图5.19
图5. 20
5.6.4虚基类
1. 虚基类的作用
图5.21
8
C++面向对象程序设计
图5.22
图5.23
图5.24
2. 虚基类的初始化
C++面向对象程序设计9
图5.25
3. 虚基类的简单应用举例
5.7基类与派生类的转换
图5.26
图5.27
10
C++面向对象程序设计
5.8继承与组合
5.9继承在软件开发中的重要意义
习题
1. 将例5.1的程序片段补充和改写成一个完整、正确的程序,用公用继承方式。
在程序中应包括输入数据的函数,在程序运行时输入num,name,sex,age,addr的值,程序应输出以上5个数据的值。
2. 将例5.2的程序片段补充和改写成一个完整、正确的程序,用私有继承方式。
在程序中应包括输入数据的函数,在程序运行时输入num,name,sex,age,addr的值,程序应输出以上5个数据的值。
3. 将例5.3的程序修改、补充,写成一个完整、正确的程序,用保护继承方式。
在程序中应包括输入数据的函数。
4. 修改例
5.3的程序,改为用公用继承方式。
上机调试程序,使之能正确运行并得到正确的结果。
对这两种继承方式作比较分析,考虑在什么情况下二者不能互相代替。
5. 有以下程序结构,请分析访问属性。
class A//A为基类
{public:
void f1( );
int i;
protected:
void f2( );
int j;
private:
int k;
};
class B: public A //B为A的公用派生类
{public:
void f3( );
protected:
int m;
private:
int n;
};
class C: public B //C为B的公用派生类
{public:
void f4( );
private:
int p;
};
int main( )
{A a1; //a1是基类A的对象
B b1; //b1是派生类B的对象
C c1; //c1是派生类C的对象
return 0;
}问:
(1) 在main函数中能否用b1.i,b1.j和b1.k引用派生类B对象b1中基类A的成员?
(2) 派生类B中的成员函数能否调用基类A中的成员函数f1和f2?
(3) 派生类B中的成员函数能否引用基类A中的数据成员i,j,k?
(4) 能否在main函数中用c1.i,c1.j,c1.k,c1.m,c1.n,c1.p引用基类A的成员i,j,k,派生类B的成员m,n,以及派生类C的成员p?
(5) 能否在main函数中用c1.f1(),c1.f2(),c1.f3()和c1.f4()调用f1,f2,f3,f4成员函数?
(6) 派生类C的成员函数f4能否调用基类A中的成员函数f1,f2和派生类中的成员函数f3?
6. 有以下程序结构,请分析所有成员在各类的范围内的访问权限。
class A
{public:
void f1( );
protected:
void f2( );
private:
int i;
};
class B: public A
{public:
void f3( );
int k;
private:
int m;
};
class C: protected B
{public:
void f4( );
protected:
int m;
private:
int n;
};
class D: private C
{public:
void f5( );
protected:
int p;
private:
int q;
};
int main()
{A a1;
B b1;
C c1;
D d1;
7. 有以下程序,请完成下面工作:
①阅读程序,写出运行时输出的结果。
②然后上机运行,验证结果是否正确。
③分析程序执行过程,尤其是调用构造函数的过程。
#include <iostream> using namespace std;
class A
{public:
A( ){a=0;b=0;}
A(int i){a=i;b=0;}
A(int i,int j){a=i;b=j;}
void display( ){cout<<″a=″<<a<<″b=″<<b;} private:
int a;
int b;
};
class B : public A
{public:
B( ){c=0;}
B(int i):A(i){c=0;}
B(int i,int j):A(i,j){c=0;}
B(int i,int j,int k):A(i,j){c=k;}
void display1( )
{display();
cout<<″c=″<<c<<endl;
}
private:
int c;
};
int main( )
{ B b1;
B b2(1);
B b3(1,3);
B b4(1,3,5);
b1.display1( );
b2.display1( );
b3.display1( );
b4.display1( );
return 0;
8. 有以下程序,请完成下面工作:
①阅读程序,写出运行时输出的结果。
②然后上机运行,验证结果是否正确。
③分析程序执行过程,尤其是调用构造函数和析构函数的过程。
#include <iostream> using namespace std;
class A
{public:
A( ){cout<<″constructing A ″<<endl;}
~A( ){cout<<″destructing A ″<<endl;}
};
class B : public A
{public:
B( ){cout<<″constructing B ″<<endl;}
~B( ){cout<<″destructing B ″<<endl;}
};
class C : public B
{public:
C( ){cout<<″constructing C ″<<endl;}
~C( ){cout<<″destructing C ″<<endl;}
};
int main( )
{C c1;
return 0;
9. 分别定义Teacher(教师)类和Cadre(干部)类,采用多重继承方式由这两个类派生出新类Teacher_Cadre(教师兼干部)。
要求:
①在两个基类中都包含姓名、年龄、性别、地址、电话等数据成员。
②在Teacher类中还包含数据成员title(职称),在Cadre类中还包含数据成员post(职务),在Teacher_Cadre类中还包含数据成员wages(工资)。
③对两个基类中的姓名、年龄、性别、地址、电话等数据成员用相同的名字,在引用这些数据成员时,指定作用域。
④在类体中声明成员函数,在类外定义成员函数。
⑤在派生类Teacher_Cadre的成员函数show中调用Teacher类中的display函数,输出姓名、年龄、性别、职称、地址、电话,然后再用cout语句输出职务与工资。
10. 将本章5.8节中的程序片段加以补充完善,成为一个完整的程序。
在程序中使用继承和组合。
在定义Professor类对象prof1时给出所有数据的初值,然后修改prof1的生日数据,最后输出prof1的全部最新数据。