第十一章继承和派生类.ppt

上传人:本田雅阁 文档编号:2530317 上传时间:2019-04-05 格式:PPT 页数:53 大小:977.51KB
返回 下载 相关 举报
第十一章继承和派生类.ppt_第1页
第1页 / 共53页
第十一章继承和派生类.ppt_第2页
第2页 / 共53页
第十一章继承和派生类.ppt_第3页
第3页 / 共53页
亲,该文档总共53页,到这儿已超出免费预览范围,如果喜欢就下载吧!
资源描述

《第十一章继承和派生类.ppt》由会员分享,可在线阅读,更多相关《第十一章继承和派生类.ppt(53页珍藏版)》请在三一文库上搜索。

1、Visual C+程序设计,徐 建 南京理工大学计算机系,【学习目标】 理解基类和派生类的概念 掌握继承的概念和用法 【重点与难点】 重点:继承的概念和使用;,第十一章 继承和派生类,11.1 继承与派生的基本概念 11.2 派生类的定义 11.3 派生类的构成 11.4 派生类成员的访问属性 11.5 派生类的构造函数和析构函数 11.6 多重继承,11.1 继承与派生的基本概念,保持已有类的特性而构造新类的过程称为继承。定义新类时可以从一个或多个既有类中继承(即拷贝)所有的数据成员和函数成员,然后加上自己的新成员或重新定义由继承得到的成员。 在已有类的基础上新增自己的特性而产生新类的过程称

2、为派生。 被继承的已有类称为基类(或父类) 派生出的新类称为派生类。,继承与派生的例子,继承与派生的基本概念,继承形成一个层次结构(继承关系是相对的),一个类可由其它类通过继承得到,继承得到的新类称派生类(子类),被继承的称为基类(父类)。,基类与派生类的关系:,派生类是基类的具体化 基类是派生类的抽象 具有继承关系的类之间必定拥有以下基本性质: 类间的共享特性; 类间的细微区别; 类间的层次结构。,使用继承的必要性 若组成一个系统的对象类均为互不包含的独立对象类,则将不可避免出现对象属性和行为的重复冗余,并且这种无层次关系的对象类既不符合现实世界的对象关系,也使对象类的定义、创建、使用和维护

3、复杂化 继承为代码重用和建立类定义的层次结构提供方便有效的手段。 例如公司的管理软件设计中需要定义一个客户类 Customer 和雇员类 Employee:,class Customer private: char name15; / 姓名 int age; / 年龄 char sex8; / 性别 double income; / 收入 public: void print(); /输出状态 ;,class Employment private: char name15; / 姓名 int age; / 年龄 char sex8; / 性别 char department20; / 部门 d

4、ouble salary; / 工资 public: void print(); / 显示输出状态 ;,两个类的数据成员和成员函数有许多相同之处。显然,如此定义两个类,造成的代码重复是不可避免的。 如果将 Customer 和 Employee 类定义中的相同成员抽取出来,定义一个新类 Person: class Person private: char name15; / 姓名 int age; / 年龄 char sex8; / 性别 public: void print(); / 显示输出状态 ;,Customer 和 Employee 都定义为 Person 的派生类, 那些在 Per

5、son 中已经定义的共同数据成员在 Customer 和 Employee 中就不需要再定义了,只需要在各自的定 义中增加自己的独有数据成员;而成员函数 print 也只 需要在 Person 所定义的行为操作基础上重新定义自己 的行为操作。 class Customer : public Person private: double income; / 收入 public: void print(); / 显示输出状态 ;,class Employee : public Person private: char department20; / 部门 double salary; / 工资 p

6、ublic: void print(); / 显示输出状态 ; 显然通过继承可以从基类 Person 派生出一组具有层次结构的新类,构成一个公司管理系统的主要对象类型。例如:,Person,使用继承机制和方法设计和建造类定义的层次结构 对于建立一个面向对象的软件系统是不可缺少的。,11.2 派生类的定义,定义派生类的一般格式: class : , , , ;,派生类的定义与构成,继承方式规定了派生类中对基类成员进行访问控制的方式。 继承方式包括三种:public(公有继承)、private (私有继承)和protected (保护继承)。如果省略继承方式,缺省的类的继承方式是private。

7、每一个继承方式,只对紧随其后的基类进行限定。,class Customer : public Person private: double income; / 新增加的数据成员“收入” public: void print(); / 重新定义基类的“显示状态” ; 从形式上比较,派生类定义与非派生类定义的差别仅在于定义首行中由 “:” 引出的派生表达式。其中: 派生方式:指明派生类继承基类成员的方式 基类名:指明派生类所继承的类。,11.3 派生类的构成,11.3 派生类的构成,从基类接收成员 调整从基类接收的成员 在声明派生类时增加的成员,派生类对象占有的存储空间 基类部分所有数据成员占有的

8、存储空间 新定义部分所有数据成员占有的存储空间,11.4 派生类成员的访问属性,类成员的访问属性是指类成员的被访问权限,这种权限随着作用域的变化而变化的,分为类内访问属性和类外访问属性。 考虑以下几种情形: 基类的成员函数访问基类成员 派生类的成员函数访问派生类新增的成员 基类的成员函数访问派生类的成员 派生类的成员函数访问基类的成员 在派生类外访问派生类的成员 在派生类外访问基类的成员,11.4 派生类成员的访问属性,继承方式导致基类成员的访问属性在派生类中发生变化; 基类成员在派生类中的访问属性: protected成员同时具有公有成员和私有成员的特征,派生类对基类的保护成员的访问与对公有

9、成员的访问相同,而派生类的实例对基类的保护成员的访问则与对私有成员的访问相同。,类内访问属性 由于派生类的成员分为继承的基类成员和自身的新 增成员,它们的类内访问属性是有所区别的。 基类成员的访问属性 封装性所限定的类成员类外访问权限确定了基类 成员在派生类定义中被访问的限定原则: 私有成员:不允许被访问,与派生类从基类的继承方式无关。 公有成员:允许被访问,与派生类从基类的继承方式无关。 新增成员的访问属性 所有的新增成员均允许被访问,与新增成员被设 定的访问属性(公有或私有)无关。,类外访问属性 派生类成员在类定义中声明的访问属性确定了派生类成员的类外访问属性: 基类成员的访问属性 私有成

10、员:不允许被访问,与派生类从基类 的继承方式无关。 公有成员:依据继承方式的不同,在基类中 被设定的公有属性会发生不同的变化。 私有继承:基类的公有成员变为派生类的 私有成员,因此在类外不允许被访问。 公有继承:基类的公有成员在派生类中仍 保持公有属性,因此在类外允许被访问。 新增加成员的访问属性 类成员在类定义中被声明的访问属性确定了类成 员的类外访问属性。,public派生:基类的公有成员如同是派生类的公有成员,即在派生类的外部也可访问基类的公有成员。基类中的私有成员仍是私有,即在基类外不能访问。,我站在基类外、派生类外, 我能看见和使用所有的基类 和派生类的公有成员。,class Stu

11、dent public: void get_value() cinnumnamesex; void display() cout“num: ”numendl; cout“name: ”nameendl; cout“sex: ”sexendl; private: int num; string name; char sex; ;,class Student1 : public Student public: void display1() cout“num: ”numendl; cout“name: ”nameendl; cout“sex: ”sexendl; cout“age: ”ageend

12、l; cout“address: ”addrendl; private: int age; string addr; ;,错误,错误,错误,private派生:基类中的公有成员相当于在派生类中的私有成员.即在派生类中可以访问基类的公有成员,而在派生类外,不能访问基类的公有成员.,class Student1 : private Student public: void display1() cout“age: ”ageendl; cout“address: ”addrendl; private: int age; string addr; ;,正确,正确,int main() Student1

13、 stud1 stud1.display(); stud1.display1(); stud1.age =18; return 0; ,正确,错误,错误,类外,protected派生:,保护成员在基类外、派生类外都是不可见的,仅在派生类中可以使用,也就是,对派生类,保护成员就象公有的一样。 目的:使保护成员具有隐蔽性,但为了在派生类中提高操作效率而使用。 因此:若一个成员需要有私有的特性而又希望在派生类中不要通过成员函数接口直接使用,可以利用保护成员的特点。,class Student public: void display() cout“num: ”numendl; cout“name:

14、”nameendl; cout“sex: ”sexendl; protected: int num; string name; char sex; ;,class Student1 : protected Student public: void display1() cout“num: ”numendl; cout“name: ”nameendl; cout“sex: ”sexendl; cout“age: ”ageendl; cout“address: ”addrendl; private: int age; string addr; ;,正确,正确,正确,int main() Stude

15、nt1 stud1 stud1.display1(); stud1.num =10023; return 0; ,正确,错误,11.5 派生类的构造函数和析构函数,初始化基类成员-派生类构造函数和基类构造函数的关系 派生类析构函数和基类析构函数的关系,定义了基类的构造函数,并在派生类中也定义了派生类的构造函数,那么,在产生派生类对象时,一方面系统调用派生类的构造函数来初始化在派生类中新增加的数据成员,另一方面系统调用基类的构造函数来初始化派生类中的基类成员。为了初始化基类成员,派生类的构造函数的一般格式为: :() : (), , (), (), , () /派生类新增成员的初始化 ,当说明派

16、生类的对象时, 系统首先调用各基类的构造函数(按说明顺序,自左向右),对基类成员进行初始化; 调用成员对象的构造函数(按说明顺序,自上向下) 执行派生类的构造函数。 如果某一个基类仍是派生类,则这种调用基类的构造函数的过程递归进行下去。当撤消派生类的对象时,析构函数的调用顺序正好与构造函数的顺序相反。,#include /Example 11_3 class Base1 int x; public: Base1(int a=30) x=a; cout“调用基类1的构造函数! “endl; Base1( ) cout“调用基类1的析构函数! “endl; ; class Base2 int y;

17、 public: Base2(int a) y=a; cout“调用基类2的构造函数! “endl; Base2( ) cout“调用基类2的析构函数! “endl; ; class Derived:public Base1, public Base2 int z; public: Derived(int a,int b):Base1(a),Base2(20) z=b; cout“调用派生类的构造函数! “ endl; Derived( ) cout“调用派生类的析构函数! “endl; ;,输出结果: 调用基类1的构造函数! 调用基类2的构造函数! 调用派生类的构造函数! 调用派生类的析构函

18、数! 调用基类2的析构函数! 调用基类1的析构函数!,1)int a=30改为int a 2)Base1(a)删去-编译时出错,删去,Base2(20) -编译时出错,void main(void) Derived c(100,200); ,如果一个基类同时说明了缺省构造函数和带有参数的构造函数,那么在派生类构造函数的说明中,既可以显式给出基类名和相应的参数,也可以完全不给出基类名,系统会自动调用相应的缺省构造函数。,派生类有一个以上的基类(不是所有面向对象的语言都支持多重继承) class 类名:类名1,类名2,., 类名n 成员说明; ; 每一个基类的类名前的继承方式用以限定这基类中的成员

19、在派生类中的访问权限,可以为空(含义是private),public, protected之一,其规则与单一继承的用法类同。每一个继承方式,只对紧随其后的基类进行限定。,11.6 多重继承,/Example 11_1 #include #include class Student protected: long No; public: Student(long no)No=no; long GetNo()return No; ;,class Emploee char *Depart; char *Job; public: Emploee(char *depart,char *job) Depa

20、rt=new char strlen(depart)+1; strcpy(Depart,depart); Job = new char strlen(job)+1; strcpy(Job,job); Emploee() if (Depart) delete Depart; if (Job) delete Job; char *GetDepart()return Depart; char * GetJob()return Job; ;,class Student_Emploee:public Student,public Emploee public: Student_Emploee(char

21、*depart,char *job,long n) :Student(n),Emploee(depart,job) void Show() cout“部门:“GetDepart()t“职务:“GetJob()n; cout“学号:“Non; ; void main() Student_Emploee a(“computer“,“teacher“,99062201); a.Show();,若No为private,则需改为GetNo(),class Point int X,Y; public: void InitP(int x, int y); void Move(int xOff, int yO

22、ff); int GetX() return X; int GetY() return Y; ; void Point:InitP(int x, int y) X=x;Y=y; void Point:Move(int xOff, int yOff) X+=xOff; Y+=yOff; class Rectangle : public Point int W,H; public: void InitR(int x, int y, int w, int h); int GetW() return W; int GetH() return H; ; void Rectangle:InitR(int

23、x, int y, int w, int h) InitP(x,y); W=w; H=h; ,#include /Example 11_2 void main(void) Rectangle rect; rect.InitR(2,3,10,20); rect.Move(3,2); coutrect.GetX(),rect.GetY(); cout,rect.GetW(),; coutrect.GetH()endl; ,5,5,10,20,二义性问题,若一个派生类是由二个或多个基类所派生,当不同基类中的成员具有相同的名字时,出现了重名的情况。这时在派生类使用到基类中的同名的成员时,出现了不唯一性

24、,这种情况称为冲突。即对基类成员的访问产生了二义性。,基类1的 成员 x fun(),基类2的 成员 x fun(),派生类,现在能看到两个x和fun(),你要给出一个 方法使我能区分两个来自 不同基类的x和fun(),1)改名 2)用作用域运算符:进行限定 .: .:() (这种关系只能一层) 3)在派生类中定义同名成员,在C+中,允许派生类中新增加的成员名与其基类的成员名相同,这种同名并不产生冲突。当没有使用作用域运算符时,则派生类中定义的成员名优先于基类中的成员名,这种优先关系称为支配规则。,#include /Example 11_4 class A public: int x; vo

25、id Show()cout “x=“xn; ; class B public: int y; void Show()cout “y=“yn; ; class C:public A,public B public: int y; ;,void main( ) C c1; c1.x=100; c1.y=200; /给派生类中的y赋值 c1.B:y=300; /给基类B中的y赋值 c1.A:Show(); c1.B:Show(); /用作用域运算符限定调用的函数 cout “y=“c1.yn; /输出派生类中的y值 cout “y=“c1.B:yn; /输出基类B中的y值 ,注意:一个类不能从同一个

26、类中直接继承一次以上,A(x,Show(),B(y,Show(),C(y,A:x,B:y),c1:,A:Show() B:Show(),x=100 y=300 y=200 y=300,A(x,Show(),B(y,Show(),C(A:x,B:y),c1:,A:Show() B:Show(),运行,A(x,Show(),B(y,Show(),C(y, A:x,B:y),c1:,A:Show() B:Show() Show(),运行,注意:二义性检查在访问控制权限或类型检查之前进行,/Example 11_5 #include class One public: int a; void b()

27、void c(float) ; class Two int a; public: void b() void c() ; class Three : public One,public Two public: void b() ;,void main(void) Three obj; obj.a=1; /A obj.b(); /B obj.c(10); /C ,obj.a=1; /A obj.b(); /B obj.c(10); /C,虚基类解决继承中的重复问题,由C继承,由B继承,void Print(void) coutx yendl; /error 不知道哪个x coutx zendl;

28、 /error不知道哪个x coutmendl; ,void Print(void) coutB:x yendl; coutC:x zendl; coutmendl; ,#include /Example 11_6 class A public: int x; A()x = 0; A(int n)x = n; ; class B:public A public: int y; B():A(4)y = 1; ; class C:public A public: int z; C()z = 2; ; class D:public B, public C public: int m; D()m =

29、3; void Print(void) coutB:x“ “yendl; coutC:x“ “zendl; coutmendl; ;,void main() D d; d.Print(); ,4 1 0 2 3,基类A: 公有数据:x,基类A:,基类C:virtual A,x(由A继承),z(C中定义),基类B:virtual A,x(由A继承),y(B中定义),类D:,m (D中定义),y(B中定义),x(由A继承),z(C中定义),virtual只对紧随其后的基类名起作用。该基类不管经过多少次派生,在其派生类中只能有该基类的一个拷贝。,#include /Example 11_7 clas

30、s A public: int x; A(int a) x=a; class B:virtual public A public: int y; B(int a, int b ):A(b) y=a; void PB() cout“x=“xt“y=“yn; ; class C:public virtual A public: int z; C(int a,int b):A(b) z=a; void PC() cout“x=“xt“z=“zn; ;,class D: public B,public C public: int m; D(int a, int b,int d,int e,int f)

31、:B(a,b),C(d,e),A(20) m=f; void Print(void) PB(); PC(); cout“m=“mn; ; void main(void) D d1(100,200,300,400,500); d1.Print(); d1.x=400; d1.Print(); ,执行以上程序,输出以下6行: x=20 y=100 x=20 z=300 m=500 x=400 y=100 x=400 z=300 m=500,构造函数问题: 若有虚基类,那么构造函数的调用顺序为:先按虚基类说明的顺序调用虚基类的构造函数,再按非虚基类(若有的话)说明顺序调用相应的构造函数,最后再调用此

32、派生类的构造函数。虚基类的构造函数仅仅做一次。,/Example 11_8 #include class X public: int x; X(int a)x=a; ; class A:virtual public X public : A(int a):X(a) ; class B:virtual public X public: B(int a):X(a) ; class C:public A,public B public: C(int a):A(20),B(30),X(40) ; void main() C c(30); coutc.xendl; A)20 B)30 C)40 D)50

33、,X(40)去掉,出错,若X(int a)改为: X(int a=20) 则下面的 ,X(40) 可去掉,C)40,子类型关系,子类型关系的定义如下: 有一个特定的类型S,当且仅当它提供了类型T的行为时,称类型S是类型T的子类型。 在公有继承方式下,可以实现子类型关系,即派生类S是基类T的子类型。,派生类对象能自动地当作基类对象使用,它包含基类的所有成员;反之,不成立。子类型具有传递性。,class B ; class D :public B ; B b,*p; D d; 1)派生类对象向基类对象赋值 b=d;(不能反过来) 2)派生类对象的地址可以赋给指向基类对象的指针 p=,b=d;,p=,能通过指针p访问属于 基类的成员,不能访 问D中的成员,可认为 D部分是不可见的,需要基类的任何地方都可以使用派生类的对象来替代。,#include /Example 11_9 class Base public: void Who() coutWho(); p= ,class Base. class Base. class Base. class Derived1. class Derived2.,作业,p391第5、7、8题。,

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 其他


经营许可证编号:宁ICP备18001539号-1