面向对象分析与设计 (2)精选PPT.ppt
面向对象分析与设计第1页,此课件共36页哦 因此,我们在前面学习了类的定义和构造因此,我们在前面学习了类的定义和构造设计的基础上,应该进一步研究类之间的关系,设计的基础上,应该进一步研究类之间的关系,特别地应该学习类之间的继承性和派生性,以特别地应该学习类之间的继承性和派生性,以全面掌握全面掌握C+面向对象程序设计方法。面向对象程序设计方法。n本章的学习内容本章的学习内容继承的概念继承的概念派生类的定义派生类的定义 派生类的构造函数和析构函数派生类的构造函数和析构函数继承属性的访问权限控制继承属性的访问权限控制多继承的概念多继承的概念 第2页,此课件共36页哦n类的继承:类的继承:就是根据就是根据一个类创建一个新类一个类创建一个新类的过程。的过程。n类的派生:类的派生:从已有类从已有类产生新类的过程就是产生新类的过程就是类的派生。类的派生。n通常将用来派生新类通常将用来派生新类的类称为的类称为基类基类(或(或父父类类),而将派生出来而将派生出来的新类称为的新类称为派生类派生类(或(或子类子类)。n新类自动具有已有类新类自动具有已有类的所有成员,并可根的所有成员,并可根据需要添加更多的成据需要添加更多的成员。员。4.1 继承、派生和类的层次关系继承、派生和类的层次关系第3页,此课件共36页哦4.2 派生类派生类 n派生类派生类是在继承基类的属性和是在继承基类的属性和操作的基础上增添新的属性和操作的基础上增添新的属性和操作而产生的新类。操作而产生的新类。n派生类是基类的特殊子类,基类派生类是基类的特殊子类,基类是抽取派生类的主要属性和操作是抽取派生类的主要属性和操作而得到的抽象描述。派生类继承而得到的抽象描述。派生类继承了基类的所有特性,但不等同于了基类的所有特性,但不等同于基类,否则就没有派生的必要了。基类,否则就没有派生的必要了。n继承关系体现了特殊与一般的继承关系体现了特殊与一般的关系。关系。第4页,此课件共36页哦n继承的一个作用继承的一个作用就是就是允许派生类在继承父允许派生类在继承父类共性的基础上,增类共性的基础上,增加新的属性和操作来加新的属性和操作来实现特殊功能;实现特殊功能;另一另一个作用是代码重用个作用是代码重用,从基类派生子类,子从基类派生子类,子类无需修改基类的代类无需修改基类的代码,就可以直接拥有码,就可以直接拥有基类的成员,然后增基类的成员,然后增加少量代码就可以实加少量代码就可以实现特殊功能,这就实现特殊功能,这就实现了代码的重用。现了代码的重用。第5页,此课件共36页哦class:;其中,其中,有有三种:公有继承、私三种:公有继承、私有继承和保护继承,有继承和保护继承,分别用关键字分别用关键字public、private和和protected表示。缺省情况下为表示。缺省情况下为私有继承。私有继承。class person /基类基类protected:char name11;char sex;int age;public:char*GetName();int GetSex();int Getage();class Student:public personprivate:char id9;float score;public:float GetScore();4.2.1 派生类的定义派生类的定义第6页,此课件共36页哦【例例4.1】派生类的定义派生类的定义 class A /定义一个基类定义一个基类A,A也称为超类、父类也称为超类、父类 int i;public:void set_i(int n)i=n;int get_i()return i;class B:public A /下划线处说明类下划线处说明类B将继承类将继承类A的公有成员的公有成员int j;public:void set_j(int n)j=n;int Multiply()return j*get_i();/可调用基类的可调用基类的get_i();/B不能访问不能访问A类的私有成员类的私有成员i。Main()B ob;ob.set_i(10);/初始化初始化ii,B可通过可通过A类的类的set_i()访问访问iob.set_j(4);/初始化初始化B中的中的j coutob.Multiply();/运行结果为运行结果为40return 0;第7页,此课件共36页哦n派生类的生成包含派生类的生成包含三个步骤:三个步骤:(1 1)吸收基类成员)吸收基类成员 吸收基类的部分成吸收基类的部分成员员,不吸收构造函数不吸收构造函数和析构函数。它是和析构函数。它是一个重用过程。一个重用过程。(2 2)改造基类成员)改造基类成员 一是通过派生类定一是通过派生类定义时的继承方式来义时的继承方式来控制;控制;二是通过定义同名二是通过定义同名成员屏蔽基类成员。成员屏蔽基类成员。它是一个扩充过程。它是一个扩充过程。4.2.2 派生类的生成过程派生类的生成过程 class person /基类基类 protected:char name11;char sex;int age;public:void Show();class Student:public personprivate:float score;public:void Show()person:Show();coutscoreendl;第8页,此课件共36页哦(3 3)添加派生类新成员)添加派生类新成员 仅仅继承基类的成仅仅继承基类的成员是不够的,需要在员是不够的,需要在派生类中添加新成员,派生类中添加新成员,以保证派生类在功能以保证派生类在功能上有所发展。同基类上有所发展。同基类的构造函数和析构函的构造函数和析构函数是不能被继承的,数是不能被继承的,需要加入新的构造函需要加入新的构造函数和析构函数完成一数和析构函数完成一些特别的初始化和扫些特别的初始化和扫尾清理工作。尾清理工作。class person /基类基类 protected:char name11;char sex;int age;public:void Show();class Student:public personprivate:float score;public:void Show()person:Show();coutscoreendl;第9页,此课件共36页哦 4.3 4.3 访问权限控制访问权限控制 从派生类的定义格式可知,有三种继承方式:从派生类的定义格式可知,有三种继承方式:公有、私有和保护。因此,派生类对基类成员公有、私有和保护。因此,派生类对基类成员的访问权限控制也从三个方面考虑:的访问权限控制也从三个方面考虑:(1)(1)公有继承的访问权限控制公有继承的访问权限控制;(2)(2)私有继承的访问权限控制私有继承的访问权限控制;(3)(3)保护继承的访问权限控制保护继承的访问权限控制;第10页,此课件共36页哦4.3.1 公有继承的访问权限控制公有继承的访问权限控制 当类的继承方式为当类的继承方式为公有继承时公有继承时:(1)在派生类中,基类的公有成员和保护成员被在派生类中,基类的公有成员和保护成员被继承后仍然作为派生类的公有成员和保护成员,继承后仍然作为派生类的公有成员和保护成员,派生类的成员可以直接访问它们派生类的成员可以直接访问它们;(2)基类的私有成员无法继承为派生类的私基类的私有成员无法继承为派生类的私有成员或其他成员,因此派生类的成员无有成员或其他成员,因此派生类的成员无法直接访问基类的私有成员。法直接访问基类的私有成员。(3)在类外,派生类的对象只可以访问继在类外,派生类的对象只可以访问继 承下承下来的基类公有成员。来的基类公有成员。第11页,此课件共36页哦【例例4.1】公有继承的访问权限控制公有继承的访问权限控制class A /定义一个基类定义一个基类A,A也称为超类、父类也称为超类、父类 int i;public:void set_i(int n)i=n;int get_i()return i;class B:public A /下划线处说明类下划线处说明类B将继承类将继承类A的公有成员的公有成员int j;public:void set_j(int n)j=n;int Multiply()return j*get_i();/可调用基类的可调用基类的get_i();/B不能访问不能访问A类的私有成员类的私有成员i。Main()B ob;ob.set_i(10);/初始化初始化i,B可通过可通过A类的类的set_i()访问访问iob.set_j(4);/初始化初始化B中的中的j coutob.Multiply();/运行结果为运行结果为40return 0;第12页,此课件共36页哦n当类的继承方式为当类的继承方式为私有继承时私有继承时:(1)在派生类中,基类的公有成员和保护成员被继承以在派生类中,基类的公有成员和保护成员被继承以后将作为派生类的私有成员,派生类的成员函数可以后将作为派生类的私有成员,派生类的成员函数可以直接访问它们直接访问它们;(2)基类的私有成员没有被继承过来,因此派生类的基类的私有成员没有被继承过来,因此派生类的成员无法访问基类的私有成员成员无法访问基类的私有成员;(3)在类外,无法访问派生类对象中从基类继承的所有成在类外,无法访问派生类对象中从基类继承的所有成员。员。(4)私有继承之后,基类的成员再也无法在以后的派生私有继承之后,基类的成员再也无法在以后的派生类中发挥作用,出于这种原因,一般不使用私有继承方类中发挥作用,出于这种原因,一般不使用私有继承方式。式。4.3.2 私有继承的访问权限控制私有继承的访问权限控制 第13页,此课件共36页哦例例4-2 私有继承举例私有继承举例#include#include class Person private:char name11;char sex;protected:int age;public:Person(const char*Name,int Age,char Sex);char*GetName()return name;int Getage();int GetSex;第14页,此课件共36页哦class Student:private Personprivate:char id9;float score;public:Student(char*pName,int Age,char Sex,char*pId,float Score)void Display();void Student:Display()cout“name:”GetName()t/访问变为私有的基类成访问变为私有的基类成 员函数员函数 cout“id:”idt;/直接访问本类私有成员直接访问本类私有成员 cout“age:”aget;/访问基类的保护成员访问基类的保护成员(变为私有的变为私有的)cout“score:”scoreendl;void main()Student s2(“wang min”,20,m,”03410102”,80);s2.Display();第15页,此课件共36页哦4.3.3 保护继承的访问权限控制保护继承的访问权限控制 当类的继承方式为当类的继承方式为保护继承时保护继承时:(1)(1)在派生类中,基类的公有成员和保护成员被继承以后在派生类中,基类的公有成员和保护成员被继承以后将作为派生类的保护成员,派生类的成员可以直接访问将作为派生类的保护成员,派生类的成员可以直接访问它们它们;(2)(2)基类的私有成员没有被继承,因此派生类的成员无法访问基基类的私有成员没有被继承,因此派生类的成员无法访问基类的私有成员。类的私有成员。(3)(3)在类外,无法访问派生类对象中从基类继承的所有成员。在类外,无法访问派生类对象中从基类继承的所有成员。(4)(4)与私有继承不同的是与私有继承不同的是,保护继承还没有完全终止基类的功能保护继承还没有完全终止基类的功能,即保护继承可以传递部分基类成员给派生类的派生类即保护继承可以传递部分基类成员给派生类的派生类,而私有而私有继承不可以。如果合理地利用保护继承继承不可以。如果合理地利用保护继承,就可以在类的复杂就可以在类的复杂层次关系中为共享访问与成员隐蔽之间找到一个平衡点层次关系中为共享访问与成员隐蔽之间找到一个平衡点,既既实现部分成员隐蔽实现部分成员隐蔽,又能方便部分成员的继承,实现代又能方便部分成员的继承,实现代码的高效重用和扩充。码的高效重用和扩充。第16页,此课件共36页哦n 不论哪种继承方式,派生类新定义成员均不能直不论哪种继承方式,派生类新定义成员均不能直接访问基类的私有成员,只能通过基类的公有成员函接访问基类的私有成员,只能通过基类的公有成员函数或保护成员函数访问基类的私有数据成员,而基类数或保护成员函数访问基类的私有数据成员,而基类的私有成员函数根本就不会继承,更谈不上使用。所的私有成员函数根本就不会继承,更谈不上使用。所以,除非仅限于本类使用,否则,一般不将成员函数以,除非仅限于本类使用,否则,一般不将成员函数定义为私有成员。定义为私有成员。第17页,此课件共36页哦n在定义派生类的构造函数时除了对自己的数据成员进行初在定义派生类的构造函数时除了对自己的数据成员进行初始化外始化外,还必须调用基类的构造函数初始化基类的数据成员。如还必须调用基类的构造函数初始化基类的数据成员。如果派生类中还有对象成员时,还应调用对象成员类的构造函数初果派生类中还有对象成员时,还应调用对象成员类的构造函数初始化对象成员。始化对象成员。派生类构造函数的一般格式如下:派生类构造函数的一般格式如下:():(),,(),),();5.4.1 派生类的构造函数派生类的构造函数 第18页,此课件共36页哦 例例4-3 派生类构造函数定义举例派生类构造函数定义举例#include#include class Person private:char name11;char sex;protected:int age;public:Person(const char*Name,int Age,char Sex);char*GetName()return(name);int Getage();int GetSex;第19页,此课件共36页哦class Student:public Personprivate:char id9;float score;public:Student(char*pName,int Age,char Sex,char*pId,float Score):person(pName,Age,Sex)strcpy(id,pId);score=Score;char*GetId(char*pId)return(id);float GetScore()return score;void Display();第20页,此课件共36页哦使用使用派生类构造函数时应注意派生类构造函数时应注意:(1)(1)当基类中没有显式定义构造函数时当基类中没有显式定义构造函数时,派派生类构造函数的定义可以省略对基类构生类构造函数的定义可以省略对基类构造函数的调用造函数的调用,而采用隐含调用。而采用隐含调用。(2 2)当基类的构造函数使用一个或多个参)当基类的构造函数使用一个或多个参数时,派生类必须定义构造函数,提供将数时,派生类必须定义构造函数,提供将参数传递给基类构造函数的途径。这时,参数传递给基类构造函数的途径。这时,派生类构造函数的函数体可能为空,仅起派生类构造函数的函数体可能为空,仅起到参数传递的作用。到参数传递的作用。第21页,此课件共36页哦例例4-4 4-4 派生类构造函数显式调用基类构造函数的调用顺序。派生类构造函数显式调用基类构造函数的调用顺序。#include class A public:A()cout“A Constructor1”endl;A(int i)x1=i;cout“A Constructor2”endl;void dispa()cout“x1=“x1endl;private:int x1;class B:public A public:B()cout“B Constructor1”endl;B(int i):A(i+10)x2=i;cout“B Constructor2”endl;void dispb()dispa();/调用基类成员函数调用基类成员函数 cout“x2=“x2endl;private:int x2;void main()B b(2);b.dispb();运行结果运行结果 A Constructor2 B Constructor2 x1=12 x2=2第22页,此课件共36页哦4.4.2 派生类的析构函数派生类的析构函数n由于基类的析构函数也不能被继承,因此,由于基类的析构函数也不能被继承,因此,必须定义派生类的析构函数。派生类的析构必须定义派生类的析构函数。派生类的析构函数必须通过调用基类的析构函数来做基类函数必须通过调用基类的析构函数来做基类的一些清理工作。的一些清理工作。n调用顺序是调用顺序是:先调用派生类的析构函数先调用派生类的析构函数;再调用对象成员类的析构函数(如果有对象成再调用对象成员类的析构函数(如果有对象成员)员);最后调用基类的析构函数,其顺序与调用构最后调用基类的析构函数,其顺序与调用构造函数的顺序相反。造函数的顺序相反。第23页,此课件共36页哦例例4-8继承方式下构造函数和析构函数的调用顺序继承方式下构造函数和析构函数的调用顺序#include class A public:A()cout“A Constructor”endl;A()cout“A Destructor”endl;class B:public A public:B()cout“B Constructor”endl;B()cout“B Destructor”endl;void main()B b;运行结果运行结果:A Constructor B Constructor B Destructor A Destructor第24页,此课件共36页哦4.5 多继承多继承n根据派生类继承基类的个数,将继承分为根据派生类继承基类的个数,将继承分为单继承单继承和和多继多继承承。n当派生类只有一个基类时称为当派生类只有一个基类时称为单继承单继承,如图如图(a)(a)和和(b)(b)所示所示;当派生类有多个基类时称为当派生类有多个基类时称为多继承多继承,如图如图(c)(c)所示所示。n单继承可以看作是多继承的一个特例,多继承可以看作单继承可以看作是多继承的一个特例,多继承可以看作是多个单继承的组合,它们有很多相同特性。是多个单继承的组合,它们有很多相同特性。第25页,此课件共36页哦4.5.1 多继承的定义格式多继承的定义格式 多继承可以看作是单继承的扩展,派生类与每个基类之间的关系可以多继承可以看作是单继承的扩展,派生类与每个基类之间的关系可以看作是一个单继承。在看作是一个单继承。在C+C+中,多继承的定义格式如下:中,多继承的定义格式如下:class class:1,n ;class window public:window(int top,int left,int bottom,int right);class scrollbar public:scrollbar(int top,int left,int bottom,int right);class scrollbarwind:public window,public scrollbar public:scrollbarwind(int top,int left,int bottom,int right);第26页,此课件共36页哦在多继承方式下,派生类构造函数的定义格式如下在多继承方式下,派生类构造函数的定义格式如下:4.5.2 多继承的构造函数多继承的构造函数():(),(),(),),();构造函数的调用顺序构造函数的调用顺序:先调用所有基类的构造函数初始化所有基类的数据成员。处于先调用所有基类的构造函数初始化所有基类的数据成员。处于同一层次的各基类构造函数的调用顺序取决于定义派生类时所指同一层次的各基类构造函数的调用顺序取决于定义派生类时所指定的基类顺序定的基类顺序,与派生类构造函数中所定义的成员初始化列,与派生类构造函数中所定义的成员初始化列表顺序无关。表顺序无关。如果派生类中还有对象成员时,还应调用对象成员类的构造如果派生类中还有对象成员时,还应调用对象成员类的构造函数初始化对象成员函数初始化对象成员。第27页,此课件共36页哦例例4-9 多继承方式下构造函数和析构函数的调用顺序。多继承方式下构造函数和析构函数的调用顺序。#include class A public:A(int i)a=i;cout“A Constructor”endl;void disp()cout“a=“aendl;A()cout“A Destructor”endl;private:int a;class B public:B(int j)b=j;cout“B Constructor”endl;void disp()cout“b=“bendl;B()cout“B Destructor”endl;private:int b;第28页,此课件共36页哦class C:public B,pulic A public:C(int k):A(k+2),B(k-2)c=k;cout“C Constructor”endl;void disp()A:disp();B:disp();cout“c=“cendl;c()cout“C Destructor”endl;private:int c;void main()C obj(10);obj.disp();运行结果:运行结果:B ConstructorB ConstructorA ConstructorA ConstructorC ConstructorC Constructora=12a=12b=8b=8c=10c=10C DestructorA DestructorB Destructor第29页,此课件共36页哦4.5.3 虚基类虚基类 在派生类中对基类成员的访问应该是唯一的。但是,在多继承在派生类中对基类成员的访问应该是唯一的。但是,在多继承方式下,可能造成对基类中某个成员的访问出现不唯一的情况,方式下,可能造成对基类中某个成员的访问出现不唯一的情况,称为对基类成员访问的二义性问题称为对基类成员访问的二义性问题,如下图所示。如下图所示。第30页,此课件共36页哦n解决的办法解决的办法 (1)(1)用作用域运算符用作用域运算符“:”进行限定进行限定;(2)(2)还可以将直接基类的共同基类设置为还可以将直接基类的共同基类设置为虚基类虚基类,则从不同路径继承过来的该类成员在内存中只拥则从不同路径继承过来的该类成员在内存中只拥有一个副本有一个副本,进而解决了同名成员的唯一标识问进而解决了同名成员的唯一标识问题。题。n虚基类的定义格式为虚基类的定义格式为:class:virtual 第31页,此课件共36页哦#include class A public:A()a=10;protected:int a;class A1:virtual public A public:A1()coutaendl;class A2:virtual public A public:A2()coutaendl;class B:A1,A2 public:B()coutaendl;void main()B obj;第32页,此课件共36页哦习题1.写出下面程序的运行结果:写出下面程序的运行结果:#include Class Apublic:A(int i,int j)a=i;b=j)void move(int x,int y)a+=x;b+=y;void show()cout“(”a“,”b“)”endl;private:int a,b;第33页,此课件共36页哦class B:public A public:B(int i,int j,int k,int l):A(i,j),x(k),y(l)void show()coutx“,”yendl;void fun()move(3,5);void f1()A:show();private:int x,y;第34页,此课件共36页哦void main()A aa(1,2);aa.show();B bb(3,4,5,6);bb.fun();bb.A:show();bb.B:show();bb.f1();第35页,此课件共36页哦3.3.编写一个学生和教师数据输入和显示程序。编写一个学生和教师数据输入和显示程序。学生数据有编号、姓名、班号和成绩,教师学生数据有编号、姓名、班号和成绩,教师数据有编号、姓名、职称和部门。要求设计数据有编号、姓名、职称和部门。要求设计这两个类的一个基类这两个类的一个基类person,person,使之包含编号和使之包含编号和姓名这两个属性和显示编号和姓名的操作函姓名这两个属性和显示编号和姓名的操作函数。(数。(实验题实验题)第36页,此课件共36页哦