1、第 7 章 继承 第 1 页 共 23 页 (西昌学院信息技术系 韩德)继承是面向对象程序设计实现软件重用的重要方法。程序员可以在已有类的基础上定义 新的数据成员和成员函数。原有类称为基类,新的类称为派生类,这种程序设计方法称为继 承。一个操作的特殊实现,用继承方法增加新的类,不会影响原有的类层次结构。派生类成 员由基类成员和自身定义的成员组成。单继承的派生类只有一个基类。多继承的派生类有多个基类。类成员的访问特性和类的继承性质决定类成员的作用域和可见性。类的公有成员称为接 口,可以在类外访问。派生类不能访问基类的私有(private)成员,但可以访问基类的公有 (public) 和保护 (p
2、rotected) 成员。 对基类成员的访问性质还受继承方式影响。 公有 (public) 继承方式,基类的 public和 protected成员在派生类中性质不变;保护(protected)继承,基 类的 public 和 protected 成员都成为派生类的 protected 成员;私有(private)继承,基类的 public和 protected成员都成为派生类的 private成员。 派生类中不可见基类的私有数据成员, 但这些数据存储单元依然被建立。 创建派生类对象时, 派生类的构造函数总是先调用基类构造函数来初始化派生类中的基类成员。 调用基类构造函 数可以通过初始化列表实
3、现数据成员的初始化。 调用析构函数的次序和调用构造函数的次序 相反。类继承关系中,覆盖成员出现访问的二义性,可以用作用域符显示指定类成员。为了避免多继承类格中的汇点类在派生类对象中产生不同副本,C+提供虚继承机制。 多继承提供了软件重用的强大功能,也增加了程序的复杂性。 7.1 类之间的关系 面向对象技术中, 类是数据和操作的集合, 它们之间有三种主要关系: hasA, usesA和 isA。 hasA表示类的包含关系,用类成员表示。 usesA表示一个类部分地使用另一个类,通过类之间成员函数的相互联系,定义友员或 对象参数传递实现。 isA表示一种分类方式,描述类的抽象和层次关系,用实继承机
4、制现。例如,植物分类 如图 7.1 所示。 图 7.1 植物分类系统 7.2 基类和派生类 C+中,描述类继承关系的语法形式是: class 派生类名 : 基类名表 数据成员和成员函数说明第 7 章 继承 第 2 页 共 23 页 (西昌学院信息技术系 韩德) 其中“基类名表“由以下语法形式构成: 访问控制 基类名 1 , 访问控制 基类名 2 , , 访问控制 基类名 n “访问控制“是表示继承权限的关键字,称为访问描述符: public 公有继承 private 私有继承 protected 保护继承 7.2.1 访问控制 一个派生类公有继承一个基类时,基类中所有公有成员成为派生类的公有(
5、public)成 员;基类中所有保护成员成为派生类的保护(protected)成员。 一个派生类私有继承一个基类时, 基类中所有公有成员和保护成员同时成为派生类的私 有(private)成员。 一个派生类保护继承一个基类时, 基类中所有公有成员和保护成员同时成为派生类的保 护(protected)成员。 不论派生类以何种方式继承基类, 都不能直接使用基类的私有 (private) 成员。 图 7.2 表 示了不同继承方式派生类成员的访问特性。 图 7.2 不同继承方式派生类成员的访问特性 1公有继承 以公有方式继承的派生类,基类的 public和 protected成员在派生类中的性质不变。
6、【例 71】公有继承的测试。 #include class A第 7 章 继承 第 3 页 共 23 页 (西昌学院信息技术系 韩德) public : void get_XY() cout x y void put_XY() cout h int get_V() return v void make_V() make_S() v = get_S() * h /使用基类成员函数 protected: int h, v void main() A objA B objB C objC cout “It is object_A :n“ objA.get_XY() objA.put_XY() cou
7、t “It is object_B : n“ objB.get_XY() objB.make_S() cout “S = “ objB.get_S() endl cout “It is object_C : n“ objC.get_XY() objC.get_H() objC.make_V() cout “ V = “ objC.get_V() endl 程序定义了 3 个类:从 A类派生 B 类,又从 B 类派生 C 类。A类称为 C类的间接基类。类 格见图 7.3。第 7 章 继承 第 4 页 共 23 页 (西昌学院信息技术系 韩德) 图 7.3 例 71 的类格 基类的私有成员在派生类
8、中不可见,但并不是说在建立派生类对象时,就不创建基类的 私有数据成员。 【例 72】测试派生类对象继承基类的私有数据成员。 #include class A public: A() x = 1 /A 类的构造函数 int out() return x /A 类成员函数,返回 thisx的值 void addX() x + private: int x class B : public A public: B() y = 1 /B 类构造函数 int out() return y / B 类成员函数,返回 thisy 的值void addY() y + private:int y void ma
9、in() A a cout“构造了对象 a:n“cout “a.x=“ a.out() endl B b cout“构造了对象 b:n“cout “b.x=“ b.A:out() endl /输出 b.xcout “b.y=“ b.out() endl /输出 b.ycout“对象 b 的数据成员+1:n“b.addX() /b.x+b.addY() /b.y+cout “b.x=“ b.A:out() endl /输出 b.xcout “b.y=“ b.out() endl /输出 b.y 2私有继承 以私有方式继承的派生类,基类的 public和 protected成员成为派生类的私有成员
10、。第 7 章 继承 第 5 页 共 23 页 (西昌学院信息技术系 韩德) 【例 73】私有继承的测试。把例 71 修改为类 B 私有继承类 A。 #include class A public : void get_XY() cout x y void put_XY() cout “x = “ x “, y = “ y n protected: int x, y class B : private A public : int get_S() return s void make_S() get_XY() / 调用基类成员函数 s = x * y private: int s void ma
11、in() B objB cout “It is object_B : n“ objB.make_S() cout “S = “ objB.get_S() endl 3保护继承 保护继承把基类的公有成员和保护成员作为派生类的保护成员,使其在派生类中屏蔽。 4访问声明 C+提供一种访问调节机制,使一些本来在派生类中不可见的成员变为可访问,称为访 问声明。 访问声明的格式为: 基类名 : :成员 值得注意的是: (1)访问声明仅仅调整名字的访问权限。 (2)访问声明不允许在派生类中降低或提升基类成员的可访问性。 (3)对重载函数名的访问声明将调整基类所有同名函数的访问域。 7.2.2 重名成员 C+
12、允许派生类的成员与基类成员重名。派生类中对重名成员访问时,屏蔽基类的同名 成员。如果要在派生类中使用基类的同名成员,可以显式地使用作用域符指定: 类名 : 成员 1重名数据成员 如果在派生类中定义了与基类同名数据成员,系统建立派生类对象时,分别建立不同的 存储空间。第 7 章 继承 第 6 页 共 23 页 (西昌学院信息技术系 韩德) 【例 74】重名数据成员。 #include class base public : int a , b class derived : public base public : int b , c void main () derived d d.a = 1
13、 d.base:b = 2 /base:b 使用的是 base 类的数据成员 b d.b = 3 / 这里使用的是 derived 类的数据成员 b d.c = 4 cout class A public: int a1, a2 A( int i1=0, int i2=0 ) a1 = i1 a2 = i2 void print() cout “a1=“ a1 t “a2=“ a2 endl class B : public A public: int b1, b2 B( int j1=1, int j2=1 ) b1 = j1 b2 = j2 void print() / 定义同名函数 co
14、ut “b1=“ b1 t “b2=“ b2 endl void printAB() A:print() / 派生类对象调用基类版本同名成员函数 print() / 派生类对象调用自身的成员函数 void main() B b b.A:print() 第 7 章 继承 第 7 页 共 23 页 (西昌学院信息技术系 韩德) b.printAB() 7.2.3 派生类中访问静态成员 如果在基类中定义了静态成员,将在整个类体系中共享。 【例 76】在派生类中访问静态成员。 #include class B public: static void Add() i+ static int i void
15、 out()cout class Base public: Base()cout“Base created n“ 第 7 章 继承 第 8 页 共 23 页 (西昌学院信息技术系 韩德) class D_class:public Base public: D_class()cout class Base public:int xBase(int i):x(i) class Derived:public Base int a public: Derived(int j):a(j*10), Base(2) cout“Base:x=“x“ nDerived:a=“aendl void main()
16、Derived d(1) 7.4 继承的应用实例 【例 79】我们考察一个点、圆、圆柱体的层次结构。首先定义点类 Point,然后从 Point 类派生圆类 Circle,最后从 Circle类派生圆柱体类 Cylinder。 首先定义点类 Point, 然后从 Point类派生圆类 Circle, 最后从 Circle类派生圆柱体类 Cylinder。 /Point.h #ifndef POINT_H #define POINT_H class Point friend ostream public: int a2; double x; /*/ ; class B : private A p
17、rivate: int b1; public int b2; double x; /*/ ; B b ; 对象 b 将会生成什么数据成员?与继承关系、访问特性、名字有关吗? 4若有以下声明语句: class A /*/ public : void sameFun(); ; class B : public A /*/ public : void sameFun(); ; void comFun() A a ; B b ; /*/ (1) 若在 A:sameFun中要调用 A:sameFun,语句形式如何?它将在什么对象上操作? (2) 在comFun中可以用什么方式调用A:sameFun?语句
18、形式如何?它们将在什么对象上操 作? 5 “虚基类”是通过什么方式定义的?如果类 A 有派生类 B,C,D,类 A是类 B 和类 C 的虚 基类,那它也一定是类 D 的虚基类吗?为什么? 6在具有虚基类的类体系中,建立派生类对象时以什么顺序调用构造函数?请用简单程序 说明,并上机验证你的分析。第 7 章 继承 第 20 页 共 23 页 (西昌学院信息技术系 韩德) 7.1 阅读下列程序,写出执行结果 1 #include class Base public: void get( int i,int j,int k,int l ) a = i; b = j; x = k; y = l; voi
19、d print() cout class Base1 public:第 7 章 继承 第 21 页 共 23 页 (西昌学院信息技术系 韩德) Base1( int i ) cout class A public: A(int i, int j) a=i; b=j ; void Add(int x, int y) a+=x; b+=y; void show() cout第 7 章 继承 第 22 页 共 23 页 (西昌学院信息技术系 韩德) class A public: A(const char *s)coutsendl; A() ; class B : virtual public A
20、public: B(const char *s1, const char *s2):A(s1) cout s2 endl ; ; class C : virtual public A public: C(const char *s1, const char *s2):A(s1) cout s2 endl ; ; class D : public B, public C public: D(const char *s1,const char *s2,const char *s3,const char *s4) : B(s1,s2),C(s1,s3),A(s1) cout s4 endl ; ;
21、void main() D *ptr = new D(“class A“,“class B“,“class C“,“class D“); delete ptr; 7.2 思考题 1函数和类两种程序模块都可以实现软件重用,它们之间有什么区别? 2请按照类成员的访问特性、类层次的继承特点,做出一张表格,总结各种类成员在基类、 派生类中的可见性和作用域。 3若有以下说明语句: class A private: int a1; public: int a2; double x; /*/ ; class B : private A private: int b1; public int b2; double x; /*/ ; B b ; 对象 b 将会生成什么数据成员?与继承关系、访问特性、名字有关吗? 4若有以下说明语句: class A /*/ public : void sameFun(); ;