《派生类与继承》PPT课件

上传人:xt****7 文档编号:186923977 上传时间:2023-02-09 格式:PPT 页数:85 大小:810.50KB
返回 下载 相关 举报
《派生类与继承》PPT课件_第1页
第1页 / 共85页
《派生类与继承》PPT课件_第2页
第2页 / 共85页
《派生类与继承》PPT课件_第3页
第3页 / 共85页
点击查看更多>>
资源描述
第第5章章 派生类与继承派生类与继承内容提要:内容提要:u 派生类的概念派生类的概念u 派生类的构造函数和析构函数派生类的构造函数和析构函数u 多重继承多重继承u 虚函数与多态性虚函数与多态性u继承是面向对象程序设计的一个重要特性。继承是面向对象程序设计的一个重要特性。u它允许在既有类的基础上创建新的类,新类可以从一它允许在既有类的基础上创建新的类,新类可以从一个或多个既有类中继承函数和数据,而且可以重新定个或多个既有类中继承函数和数据,而且可以重新定义或加进新的数据和函数,从而形成类的层次或等级。义或加进新的数据和函数,从而形成类的层次或等级。u既有类称为基类或父类,在它基础上建立的新类称为既有类称为基类或父类,在它基础上建立的新类称为派生类或子类。派生类或子类。5.1 派生类的概念派生类的概念5.1.1 为什么要继承为什么要继承u现实世界中许多事物具有继承性,我们可以用层次分类的方现实世界中许多事物具有继承性,我们可以用层次分类的方法来描述它们之间的关系。法来描述它们之间的关系。例如:一个简单的汽车分类图例如:一个简单的汽车分类图汽车汽车运输汽车运输汽车专用汽车专用汽车客车客车货车货车消防车消防车洒水车洒水车u继承性也是程序设计中的一个非常有用的、有力的特性,它继承性也是程序设计中的一个非常有用的、有力的特性,它可以让程序员在既有类的基础上,通过增加少量代码或修改可以让程序员在既有类的基础上,通过增加少量代码或修改少量代码的方法得到新的类,从而较好的解决代码重用的问少量代码的方法得到新的类,从而较好的解决代码重用的问题。题。派生类的概念派生类的概念u从以上类的声明可以看出,这两个类中的数据成员和成员函从以上类的声明可以看出,这两个类中的数据成员和成员函数有很多相同的地方。只要在数有很多相同的地方。只要在person类的基础上再增加数据类的基础上再增加数据成员成员department和和salary,再对,再对print()成员函数稍加修改就成员函数稍加修改就可以定义出可以定义出employee类。像这样定义两个类,代码重复太严类。像这样定义两个类,代码重复太严重。为了提高代码的可重用性,就必须引入继承性,将重。为了提高代码的可重用性,就必须引入继承性,将employee类说明成类说明成person类的派生类,那些相同的成员在类的派生类,那些相同的成员在employee类中就不需要再定义了。类中就不需要再定义了。class person private:char name10;int age;char sex;public:void print();class employee private:char name10;int age;char sex;char department20;float salary;public:void print();派生类的概念派生类的概念5.1.2 派生类的声明派生类的声明1.声明一个派生类的一般格式声明一个派生类的一般格式/定义一个基类定义一个基类class person private:char name10;int age;char sex;public:/;/定义一个派生类定义一个派生类class employee:public person private:char department20;float salary;public:/;class 派生类名:派生方式派生类名:派生方式 基类名基类名 /派生类新增的数据成员和成员函数派生类新增的数据成员和成员函数;说明:说明:派生类名就是要声明的新类派生类名就是要声明的新类名。名。基类名是一个已经定义的类。基类名是一个已经定义的类。派生类的概念派生类的概念 派生方式可以是关键字派生方式可以是关键字private或或public。private指派生类从基类私有派生;指派生类从基类私有派生;public指派生类从基类公有派生。指派生类从基类公有派生。派生方式可以缺省,这时派生方式默认为派生方式可以缺省,这时派生方式默认为private。2.两种派生方式的特点两种派生方式的特点 无论是哪种派生方式,基类中的私有成员既不允许外部函数访无论是哪种派生方式,基类中的私有成员既不允许外部函数访问,也不允许派生类中的成员函数访问,但是可以通过基类提问,也不允许派生类中的成员函数访问,但是可以通过基类提供的公有成员函数访问。供的公有成员函数访问。公有派生与私有派生的不同点在于基类中的公有成员在派生类公有派生与私有派生的不同点在于基类中的公有成员在派生类中的访问属性。中的访问属性。u公有派生时,基类中的所有公有成员在派生类中也都是公有的公有派生时,基类中的所有公有成员在派生类中也都是公有的u私有派生时,基类中的所有公有成员在派生类中都是私有的私有派生时,基类中的所有公有成员在派生类中都是私有的派生类的概念派生类的概念3.私有派生私有派生(1)私有派生类对基类成员的)私有派生类对基类成员的访问访问u私有派生得到的派生类,对私有派生得到的派生类,对它基类的公有成员只能是私它基类的公有成员只能是私有继承,也就是说基类中的有继承,也就是说基类中的公有成员都只能成为私有派公有成员都只能成为私有派生类的私有成员,这些私有生类的私有成员,这些私有成员能够被派生类的成员函成员能够被派生类的成员函数访问,但是基类私有成员数访问,但是基类私有成员不能被派生类成员函数访问。不能被派生类成员函数访问。#include class base int x;public:void setx(int n)x=n;void showx()coutxendl;class derived:private base int y;public:void setxy(int n,int m)setx(n);y=m;void showxy()coutxyendl;int main()derived obj;obj.setxy(10,20);();return 0;showx();coutyendl;派生类的概念派生类的概念(2)外部函数对私有派生类继)外部函数对私有派生类继承来的成员的访问承来的成员的访问u私有派生时,基类的所有成私有派生时,基类的所有成员在派生类中都成为私有成员在派生类中都成为私有成员,外部函数不能访问。员,外部函数不能访问。#include class base int x;public:void setx(int n)x=n;void showx()coutxendl;class derived:private base int y;public:void sety(int m)y=m;void showxy()coutyendl;int main()derived obj;obj.setx(10);/非法非法 obj.sety(20);();/非法非法 ();return 0;派生类的概念派生类的概念3.公有派生公有派生u公有派生时,基类中的公有派生时,基类中的私有成员在派生类中仍私有成员在派生类中仍是私有成员,不允许外是私有成员,不允许外部函数和派生类中的成部函数和派生类中的成员函数直接访问,但是员函数直接访问,但是可以通过基类提供的公可以通过基类提供的公有成员函数访问。基类有成员函数访问。基类中的公有成员在派生类中的公有成员在派生类中仍是公有成员,外部中仍是公有成员,外部函数和派生类中的成员函数和派生类中的成员函数可直接访问。函数可直接访问。#include class base int x;public:void setx(int n)x=n;void showx()coutxendl;class derived:public base int y;public:void sety(int m)y=m;void showy()coutyendl;int main()derived obj;obj.setx(10);obj.sety(20);();();return 0;派生类的概念派生类的概念说明:说明:(1)派生类以公有派生的方式)派生类以公有派生的方式继承了基类,并不意味着派继承了基类,并不意味着派生类可以访问基类的私有成生类可以访问基类的私有成员。员。#include class base int x;public:void setx(int n)x=n;void showx()coutxendl;class derived:public base int y;public:void sety(int m)y=m;void show_sum()coutx+yendl;/非法非法 void showy()coutyendl;派生类的概念派生类的概念(2)在派生类中声明的名)在派生类中声明的名字支持基类中声明的同字支持基类中声明的同名的名字,即如果在派名的名字,即如果在派生类的成员函数中直接生类的成员函数中直接使用该名字的话,则表使用该名字的话,则表示使用派生类中声明的示使用派生类中声明的名字。名字。对于派生类的对象的引用,对于派生类的对象的引用,也有同样的结论。也有同样的结论。class X public:int f();class Y:public X public:int f();int g();void Y:g()f();/被调用的函数是被调用的函数是Y:f(),而不是而不是X:f()int main()Y obj;();/被调用的函数是被调用的函数是Y:f()obj.X:f();/被调用的函数是被调用的函数是X:f()派生类的概念派生类的概念公有派生和私有派生的访问特性公有派生和私有派生的访问特性基类成员基类成员基类私有成员基类私有成员基类公有成员基类公有成员派生方式派生方式privatepublicprivatepublic派生类成员派生类成员外部函数外部函数不可访问不可访问不可访问不可访问不可访问不可访问不可访问不可访问不可访问不可访问可访问可访问可访问可访问可访问可访问派生类的概念派生类的概念5.1.3 保护成员保护成员u保护成员:即保护成员:即protected成员,是成员,是C+提供的具有另一种访问提供的具有另一种访问属性的成员属性的成员uProtected说明符可以放在类声明的任何地方,通常将它放在说明符可以放在类声明的任何地方,通常将它放在私有成员声明之后,公有成员声明之前。私有成员声明之后,公有成员声明之前。u类声明的一般格式:类声明的一般格式:class 类名类名 private:私有数据成员和成员函数私有数据成员和成员函数protected:保护数据成员和成员函数保护数据成员和成员函数public:公有数据成员和成员函数公有数据成员和成员函数;派生类的概念派生类的概念u保护成员可以被派生类保护成员可以被派生类的成员函数访问,但是的成员函数访问,但是对于外界是隐藏的,外对于外界是隐藏的,外部函数不能访问它。部函数不能访问它。#include class samp int a;protected:int b;public:int c;samp(int n,int m)a=n;b=m;int geta()return a;int getb()return b;int main()samp obj(20,30);=99;=50;cout()endl;cout()“”endl;return 0;/非法非法派生类的概念派生类的概念u为了便于派生类的访问,为了便于派生类的访问,可以将基类私有成员中需可以将基类私有成员中需要提供给派生类访问的成要提供给派生类访问的成员定义为保护成员。员定义为保护成员。uC+规定:基类的保护成规定:基类的保护成员被派生类继承之后,若员被派生类继承之后,若为公有派生,则基类中的为公有派生,则基类中的保护成员在派生类中也为保护成员在派生类中也为保护成员;若为私有派生,保护成员;若为私有派生,则基类中的保护成员在派则基类中的保护成员在派生类中成为私有成员。生类中成为私有成员。#include class base protected:int a,b;public:void setab(int n,int m)a=n;b=m;class derive:public base int c;public:void setc(int n)c=n;void showabc()couta“”b“”cendl;int main()derive obj;obj.setab(2,4);obj.setc(3);();return 0;派生类的概念派生类的概念#include class base protected:int a;public:void seta(int sa)a=sa;class derive1:private base protected:int b;public:void setb(int sb)b=sb;class derive2:public derive1 int c;public:void setc(int sc)c=sc;void show()cout“a=”aendl;cout“b=”bendl;cout“c=”cendl;void main()base op1;op1.seta(1);derive1 op2;op2.setb(2);derive2 op3;op3.setc(3);op3.show();派生类的概念派生类的概念基类成员在派生类中的访问权限基类成员在派生类中的访问权限派生方式派生方式基类中的访问权限基类中的访问权限派生类中的访问权限派生类中的访问权限publicprivatepublicprivatepublicprivateprotectedprotectedprivateprivateprivateprivatepublicprotected5.2 派生类的构造函数和析构函数派生类的构造函数和析构函数派生类都有显式的或隐式的构造函数和析构函数。当创派生类都有显式的或隐式的构造函数和析构函数。当创建一个派生类对象时,如何调用基类的构造函数对基建一个派生类对象时,如何调用基类的构造函数对基类数据初始化,以及在撤销派生类对象时,又如何调类数据初始化,以及在撤销派生类对象时,又如何调用基类的析构函数来对基类对象的数据成员进行善后用基类的析构函数来对基类对象的数据成员进行善后处理,这是本节要讨论的问题。处理,这是本节要讨论的问题。派生类的构造函数和析构函数派生类的构造函数和析构函数1.派生类构造函数和析构派生类构造函数和析构函数的执行顺序函数的执行顺序u创建派生类对象时,首先执创建派生类对象时,首先执行基类的构造函数,随后再行基类的构造函数,随后再执行派生类的构造函数;执行派生类的构造函数;u当撤销派生类对象时,则先当撤销派生类对象时,则先执行派生类的析构函数,随执行派生类的析构函数,随后再执行基类的析构函数。后再执行基类的析构函数。#include class base public:base()cout“Constructing base classn”;base()cout“Destructing base classn”;class derive:public base public:derive()cout“Constructing derived classn”;derive()cout“Destructing derived classn”;int main()derive op;return 0;Constructing base classConstructing derived classDestructing derived classDestructing base class派生类的构造函数和析构函数派生类的构造函数和析构函数2.派生类构造函数和析派生类构造函数和析构函数的构造规则构函数的构造规则u当基类的构造函数没有当基类的构造函数没有参数,或没有显式定义参数,或没有显式定义构造函数时,派生类可构造函数时,派生类可以不向基类传递参数,以不向基类传递参数,甚至可以不定义构造函甚至可以不定义构造函数。数。#include class base public:base()cout“Constructing base classn”;base()cout“Destructing base classn”;class derive:public base public:derive()cout“Constructing derived classn”;derive()cout“Destructing derived classn”;int main()derive op;return 0;派生类的构造函数和析构函数派生类的构造函数和析构函数u派生类不能继承基类中的构造函数和析构函数。当基类含有派生类不能继承基类中的构造函数和析构函数。当基类含有带参数的构造函数时,派生类必须定义构造函数,以提供把带参数的构造函数时,派生类必须定义构造函数,以提供把参数传递给基类构造函数的途径。参数传递给基类构造函数的途径。uC+中,派生类构造函数的一般格式为:中,派生类构造函数的一般格式为:派生类构造函数名(参数表):基类构造函数名(参数表)派生类构造函数名(参数表):基类构造函数名(参数表)/;其中基类构造函数的参数,通常来源于派生类构造函数的参其中基类构造函数的参数,通常来源于派生类构造函数的参数表,也可以用常数值。数表,也可以用常数值。#include class base int i;public:base(int n)cout“Constructing base classn”;i=n;base()cout“Destructing base classn”;void showi()coutiendl;class derive:public base int j;public:derive(int n,int m):base(m)cout“Constructing derived classn”;j=n;derive()cout“Destructing derived classn”;void showj()coutjendl;void main()derive obj(30,40);();();return 0;Constructing base classConstructing derived class4030Destructing derived classDestructing base class派生类的构造函数和析构函数派生类的构造函数和析构函数u当派生类中含有对象成员时,其构造函数的一般形式为:当派生类中含有对象成员时,其构造函数的一般形式为:派生类构造函数名(参数表):基类构造函数名(参数表),派生类构造函数名(参数表):基类构造函数名(参数表),对象成员名对象成员名1(参数表),(参数表),对象成员名,对象成员名n(参数表)(参数表)/;u在定义派生类对象时,构造函数的执行顺序如下:在定义派生类对象时,构造函数的执行顺序如下:基类的构造函数基类的构造函数 对象成员的构造函数对象成员的构造函数 派生类的构造函数派生类的构造函数u撤销对象时,析构函数的调用顺序与构造函数的调用顺序正撤销对象时,析构函数的调用顺序与构造函数的调用顺序正好相反好相反#include class base int x;public:base(int n)x=i;cout“Constructing base classn”;base()cout“Destructing base classn”;void show()cout“x=”xendl;class derive:public base base d;public:derive(int i):base(i),d(i)cout“Constructing derived classn”;derive()cout“Destructing derived classn”;void main()derive obj(5);();return 0;Constructing base classConstructing base classConstructing derived classx=5Destructing derived classDestructing base classDestructing base class派生类的构造函数和析构函数派生类的构造函数和析构函数说明:说明:(1)当基类构造函数不带)当基类构造函数不带参数时,派生类不一定参数时,派生类不一定需要定义构造函数,然需要定义构造函数,然而当基类的构造函数哪而当基类的构造函数哪怕只带有一个参数,它怕只带有一个参数,它所有的派生类都必须定所有的派生类都必须定义构造函数,甚至所定义构造函数,甚至所定义的派生类构造函数的义的派生类构造函数的函数体可能为空,仅仅函数体可能为空,仅仅起参数的传递作用。起参数的传递作用。class base int i;public:base(int n)cout“Constructing base classn”;i=n;void showi()couti“n”;class derived:public base int j;public:derived(int n):base(n)cout“Constructing derived classn”;j=0;void showj()coutjendl;派生类的构造函数和析构函数派生类的构造函数和析构函数(2)若基类使用缺省构造函数或不带参数的构造函数,则在派)若基类使用缺省构造函数或不带参数的构造函数,则在派生类中定义构造函数时可略去生类中定义构造函数时可略去“:基类构造函数名基类构造函数名(参数表参数表)”;此时若派生类也不需要构造函数,则可不定义构造函数。此时若派生类也不需要构造函数,则可不定义构造函数。(3)如果派生类的基类也是一个派生类,则每个派生类只需负)如果派生类的基类也是一个派生类,则每个派生类只需负责其直接基类的构造,依次上溯。责其直接基类的构造,依次上溯。(4)由于析构函数是不带参数的,在派生类中是否要定义析构)由于析构函数是不带参数的,在派生类中是否要定义析构函数与它所属的基类无关,故基类的析构函数不会因为派生函数与它所属的基类无关,故基类的析构函数不会因为派生类没有析构函数而得不到执行,它们各自是独立的。类没有析构函数而得不到执行,它们各自是独立的。5.3 多继承多继承u单继承(单基派生):一个派生类只有一个直接基类。单继承(单基派生):一个派生类只有一个直接基类。u多继承(多基派生):一个派生类具有两个或两个以上的直多继承(多基派生):一个派生类具有两个或两个以上的直接基类。接基类。1.多继承的声明多继承的声明声明的一般形式:声明的一般形式:class 派生类名派生类名:派生方式派生方式1 基类名基类名1,派生方式派生方式n 基类名基类名n /派生类新增的数据成员和成员函数派生类新增的数据成员和成员函数;说明:说明:冒号后面的部分称基类表;冒号后面的部分称基类表;各基类之间用逗号分隔;各基类之间用逗号分隔;其中派生方式规定了按什么方式继承:其中派生方式规定了按什么方式继承:private或或public,缺省的派,缺省的派 生方式是生方式是private。多继承多继承class z:public x,y /;说明:说明:多继承中,公有派生和私有派生对于基类成员在派生类中的可访问性与单多继承中,公有派生和私有派生对于基类成员在派生类中的可访问性与单继承的规则相同。继承的规则相同。类类z公有继承了类公有继承了类x,私有继承了类私有继承了类yclass z:x,public y /;类类z私有继承了类私有继承了类x,公有继承了类公有继承了类yclass z:public x,public y /;类类z公有继承了类公有继承了类x和类和类y#include class X int a;public:void setX(int x)a=x;void showX()cout“a=”aendl;class Y int b;public:void setY(int x)b=x;void showY()cout“b=”bendl;class Z:public X,private Y int c;public:void setZ(int x,int y)c=x;setY(y);void showZ()showY();cout“c=”cendl;void main()Z obj;obj.setX(3);();obj.setY(4);();obj.setZ(6,8);();多继承多继承/错误错误/错误错误A=3B=6C=8多继承多继承说明:说明:对基类成员的访问必须是无二义的。对基类成员的访问必须是无二义的。class X public:int f();class Y public:int f();int g();class Z:public X,public Y public:int g();int h();假如定义类假如定义类Z的对象的对象obj:Z obj;则以下对函数则以下对函数f()的访问是二义的:的访问是二义的:();使用成员名限定可以消除二义性使用成员名限定可以消除二义性,例如:例如:obj.X:f();obj.Y:f();多继承多继承2.多继承的构造函数与析构函数多继承的构造函数与析构函数(1)多继承构造函数定义的一般形式:)多继承构造函数定义的一般形式:派生类构造函数名(参数表)派生类构造函数名(参数表):基类基类1 构造函数名(参数表)构造函数名(参数表),基类基类2 构造函数名构造函数名(参数表)(参数表),基类基类n 构造函数名(参数表)构造函数名(参数表)/;class Hard protected:char bodyname20;public:Hard(char*bdna);/;class Soft protected:char os10;char Lang15;public:Soft(char*o,char*lg);/;class System:public Hard,public Soft private:char owner10;public:System(char*ow,char*bn,char*o,char*lg):Hard(bn),Soft(o,lg);/;多继承多继承说明:说明:在定义派生类在定义派生类system的构造函数时,缀上了的构造函数时,缀上了Hard和和Soft的构造函数。的构造函数。class window /public:window(int top,int left,int bottom,int right);window();/;class scrollbar /public:scrollbar(int top,int left,int bottom,int right);scrollbar();/;class scrollbarwind:window,scrollbar /public:scrollbarwind(int top,int left,int bottom,int right);scrollbarwind();/;多继承多继承scrollbarwind:scrollbarwind(int top,int left,int bottom,int right):window(top,left,bottom,right),scrollbar(top,right-20,bottom,right)/多继承多继承说明:说明:在定义派生类在定义派生类scrollbarwind的构造函数时,也缀的构造函数时,也缀上了基类上了基类window和和scrollbar的构造函数。的构造函数。#include class X int a;public:X(int sa)a=sa;int getX()return a;class Y int b;public:Y(int sb)b=sb;int getY()return b;class Z:public X,private Y int c;public:Z(int sa,int sb,int sc):X(sa),Y(sb)c=sc;int getZ()return c;int getY()return Y:getY();int main()Z obj(2,4,6);int ma=();cout“a=”maendl;int mb=();cout“b=”mbendl;int mc=();cout“c=”mcendl;return 0;多继承多继承a=2b=4c=6多继承多继承(2)多继承的构造函数的执行顺序)多继承的构造函数的执行顺序u多继承的构造函数的执行顺序与单继承构造函数的执多继承的构造函数的执行顺序与单继承构造函数的执行顺序相同,也是遵循先执行基类的构造函数,再执行顺序相同,也是遵循先执行基类的构造函数,再执行对象成员的构造函数,最后执行派生类构造函数的行对象成员的构造函数,最后执行派生类构造函数的原则。原则。u在多个基类之间,严格按照派生类的声明时从左到右在多个基类之间,严格按照派生类的声明时从左到右的顺序来排列先后。的顺序来排列先后。u析构函数的执行顺序刚好与构造函数的执行顺序相反。析构函数的执行顺序刚好与构造函数的执行顺序相反。多继承多继承3.虚基类虚基类(1)虚基类的引入)虚基类的引入#include class base protected:int a;public:base()a=5;class base1:public basepublic:base1()coutbase1 a=aendl;class base2:public basepublic:base2()coutbase2 a=aendl;class derived:public base1,public base2 public:derived()coutderived a=aendl;int main()derived obj;return 0;多继承多继承 从图中可以看出,虽然从图中可以看出,虽然base1和和base2是从同一个基类是从同一个基类base派生而来的,但它们所对应的是基类派生而来的,但它们所对应的是基类base的不同拷贝。类的不同拷贝。类derived是是base1和和base2的派生类,因此类的派生类,因此类base是类是类derived的间接基类,它有两个拷贝与类的间接基类,它有两个拷贝与类derived相对应,一相对应,一个是个是base1派生路径上的拷贝,另一个是派生路径上的拷贝,另一个是base2派生路径上的派生路径上的拷贝。当类拷贝。当类derived要访问这个间接基类要访问这个间接基类base时,必须指定时,必须指定要访问的是哪个路径上的要访问的是哪个路径上的base拷贝。拷贝。derivedbase1base2basebase类的层次图类的层次图多继承多继承class derived:public base1,public base2 public:derived()coutderived a=aendl;base1:a解决方法:解决方法:指定访问路径指定访问路径 引入虚基类,即类引入虚基类,即类base只存在一个拷贝,则对只存在一个拷贝,则对a的引用就不的引用就不会产生二义性。会产生二义性。derivedbase1base2base多继承多继承(2)虚基类的概念虚基类的概念uC+中,如果想使公共的基类只产生一个拷贝,则可以将这中,如果想使公共的基类只产生一个拷贝,则可以将这个基类说明为虚基类。个基类说明为虚基类。u使用方法:从类使用方法:从类base派生新类时,使用关键字派生新类时,使用关键字virtual将类将类base说明为虚基类。说明为虚基类。#include class base protected:int a;public:base()a=5;class base1:virtual public basepublic:base1()coutbase1 a=aendl;class base2:virtual public basepublic:base2()coutbase2 a=aendl;class derived:public base1,public base2 public:derived()coutderived a=aendl;int main()derived obj;return 0;多继承多继承说明:说明:class L public:int next;class A :virtual public L ;class B :virtual public L ;class C :public A,public B public:void f()next=0;对于类对于类C而言,而言,L类是类是B类的虚基类,而是类类的虚基类,而是类A的真基类;但对于类的真基类;但对于类B而而言,言,L类还是类还是B类的真基类的真基类。类。如果派生时,如果派生时,A,B的顺的顺序变了序变了则对于类则对于类C而言,而言,L类是类是A类的虚基类,而是类类的虚基类,而是类B的真基类。的真基类。public B,public A 说明虚基类时,说明虚基类时,virtual和和public的顺序可以交换。的顺序可以交换。public virtual Lpublic virtual L在在多继承多继承 一个派生类的对象的地址可以直接赋给虚基类的指针,例如一个派生类的对象的地址可以直接赋给虚基类的指针,例如:C obj;L *ptr=&obj;这时不需要强制类型转换,并且,一个虚基类的引用可以引这时不需要强制类型转换,并且,一个虚基类的引用可以引用一个派生类的对象,例如:用一个派生类的对象,例如:C obj2;L&ref=obj2;反之则不行。反之则不行。无论在强制类型转换中指定什么路径,一个虚基类的指针或无论在强制类型转换中指定什么路径,一个虚基类的指针或引用不能转换为派生类的指针或引用。例如:引用不能转换为派生类的指针或引用。例如:C *P=(C*)(A*)ptr;将产生编译错误。将产生编译错误。多继承多继承(3)虚基类对象的初始化虚基类对象的初始化u虚基类的初始化与一般的多重继承的初始化在语法上是一样虚基类的初始化与一般的多重继承的初始化在语法上是一样的,但构造函数的调用顺序不同。的,但构造函数的调用顺序不同。u虚基类构造函数的调用顺序:虚基类构造函数的调用顺序:若同一层次中包含多个虚基类,这些虚基类的构造函数按若同一层次中包含多个虚基类,这些虚基类的构造函数按对它们说明的先后次序调用;对它们说明的先后次序调用;若虚基类由非虚基类派生而来,则仍然先调用基类构造函若虚基类由非虚基类派生而来,则仍然先调用基类构造函数,再调用派生类的构造函数;数,再调用派生类的构造函数;若同一层次中同时包含虚基类和非虚基类,应先调用虚基若同一层次中同时包含虚基类和非虚基类,应先调用虚基类的构造函数,再调用非虚基类的构造函数,最后调用派生类的构造函数,再调用非虚基类的构造函数,最后调用派生类构造函数。类构造函数。多继承多继承Class X:public Y,virtual public Z /;X one;定义类定义类X的对象的对象one时,将产生如下的调用次序:时,将产生如下的调用次序:Z();Y();X();说明:说明:Z是是X的虚基类,故先调用的虚基类,故先调用Z的构造函数,再调用的构造函数,再调用Y的构造函的构造函数,最后才调用派生类数,最后才调用派生类X自己的构造函数。自己的构造函数。多继承多继承#include class base public:base()coutBaseendl;class base2 public:base2()coutBase2endl;class level1:public base2,virtual public base public:level1()coutlevel1endl;class level2:public base2,virtual public base public:level2()coutlevel2endl;class toplevel:public level1,virtual public level2 public:toplevel()couttoplevelendl;toplevel view;void main();BaseBase2level2 Base2level1toplevel 多继承多继承分析:分析:表示类之间继承关系的示意图表示类之间继承关系的示意图base2basebase2level1level2toplevel多继承多继承class base ;class base2 ;class level1:public base2,virtual public base ;class level2:public base2,virtual public base ;class toplevel:virtual public level1,public level2 ;toplevel view;base()base2()level1()base2()level2()toplevel()base2basebase2level1level2toplevel多继承多继承class B ;class X:virtual public B ;class Y :virtual public B ;class Z :public B ;class AA:public X,public Y,public Z ;B()X()Y()B()Z()AA()BBXYAAZ这里这里AA具有两个具有两个B类的子类的子对象:对象:Z的的B和和x与与Y共享的共享的虚拟的虚拟的B。多继承多继承class V public :int v;class A public :int a;class B :public A,virtual public V ;class C :public A,Virtual public V ;class D :public B,public C public:void f();void D:f()v+;a+;B():V()A()B()C():A()C()D():D()AVABCD在在D D中仅仅一个中仅仅一个v v错误,具有二义性,错误,具有二义性,在在D D中有两个中有两个a a5.4 虚函数与多态性虚函数与多态性5.4.1 虚函数虚函数u对于普通成员函数的重载,可表达为下面的方式对于普通成员函数的重载,可表达为下面的方式:在同一个类中重载在同一个类中重载 在不同类中重载在不同类中重载 基类的成员函数在派生类中重载基类的成员函数在派生类中重载u重载函数的访问是在编译时区分的,有以下三种方法重载函数的访问是在编译时区分的,有以下三种方法:根据参数的特征加以区分,例如根据参数的特征加以区分,例如:Show(int,char)与与Show(char*,float)不是同一函数,编译能区分。不是同一函数,编译能区分。使用使用“:”加以区分,例如:加以区分,例如:Circle:Show有别于有别于Point:Show 根据类对象加以区分。根据类对象加以区分。()调用调用Circle:Show()()调用调用Point:Show()这里这里ACircle和和APoint分别是分别是Circle和和Point的对象。的对象。#include class A public:void fun()cout“In A”endl;class B:public A public:void fun()cout“In B”endl;class C:public B public:void fun()cout“In C”fun();虚函数与多态性虚函数与多态性u虚函数是重载的另一种表现形式。虚函数是重载的另一种表现形式。1、基类对象的指针指向派生类对象、基类对象的指针指向派生类对象 指向基类和派生类的指针变量是相关的,假设指向基类和派生类的指针变量是相关的,假设B_classB_class是基类,是基类,D_classD_class是从是从B_classB_class公有派生出来的派生类,则任何被说明公有派生出来的派生类,则任何被说明为指向为指向B_classB_class的指针也可以指向的指针也可以指向D_classD_class。B_class B_class *p;p;B_class B_ob;B_class B_ob;D_class D_ob;D_class D_ob;p=&B_ob;p=&B_ob;p=&D_ob;p=&D_ob;利用利用p,p,可以访问从基类可以访问从基类B_classB_class继承的成员,但继承的成员,但D_classD_class自己自己定义的成员,定义的成员,p p不能访问。不能访问。#include#include class B_class char name80;public:void put_name(char*s)strcpy(name,s);void show_name()coutnamen;class D_class:public B_class char phone_num80;public:void put_phone(char*num)strcpy(phone_num,num);void show_phone()coutphone_numput_name(Thomas Edison);p=&D_ob;p-put_name(Albert Einstein);();();dp=&D_ob;dp-put_phone(555555_1234);dp-show_phone();p-show_pone();(D_class*)p)-show_phone();p-show_phone();虚函数与多态性虚函数与多态性pdp基类指针指向派生类类对象,只能访问基类成员派生类指针指向派生类类对象,访问所有成员nameB_obThomas Edisonput_name()show_name()phone_numD_obAlbert Einsteinname555555_1234show_phone();put_phone()put_name()show_name()基类指针派生类指针(1)可以用一个指向基类的指针指向其公有派生类的对象。可以用一个指向基类的指针指向其公有派生类的对象。但是相反却不正确,即不能用指向派生类的指针指向一个基但是相反却不正确,即不能用指向派生类的指针指向一个基类的对象。类的对象。(2)希望用基类指针访问其公有派生类的特定成员,必须将基)希望用基类指针访问其公有派生类的特定成员,必须将基类指针用显式类型转换为派生类指针。例如:类指针用显式类型转换为派生类指针。例如:(D_class*)p)-show_phone();#includeclass Base protected:int x;public:Base(int a)x=a;void who()cout“base ”x“n”;class First_d:public Base public:First_d(int a):Base(a)void who()cout“First derivation”x“n”;class second_d:public Base public:second_d(int a):Base(a)void who()cout“Second derivation”xwho();p=&first_obj;p-who();p=&second_obj;p-who();();();base 1base 2base 3First derivation 2Second derivation 3虚函数与多态性虚函数与多态性u指向基类的指针指向基类的指针p,不管是指向基类的对象不管是指向基类的对象base_obj还是指向还是指向派生的对象派生的对象first_obj和和second_obj,p-who()调用的都是基调用的都是基类定义的类定义的 who()的版本的版本.u必须显式地用必须显式地用 ();();才能调用类才能调用类first_d和类和类second_d中定义的中定义的who()的版本。其的版本。其本质的原因在于普通成员函数的调用是在编译时静态区分。本质的原因在于普通成员函数的调用是在编译时静态区分。u如果随着如果随着p所指向的对象的不同所指向的对象的不同pwho()能调用不同类中能调用不同类中who()版本,这样就可以用一个界面版本,这样就可以用一个界面p-who()访问多个实现版访问多个实现版本本:Base中的中的who(),First_d 中的中的 who(),以及以及Second_d中的中的who(),这在编程时非常有用。这在编程时非常有用。u实际上,这表达了一种动态的性质,函数调用实际上,这表达了一种动态的性质,函数调用p-who()依赖依赖于运行时于运行时p所指向的对象。虚函数提供的就是这种解释机制。所指向的对象。虚函数提供的就是这种解释机制。虚函数与多态性虚函数与多态性2、虚函数的定义、虚函数的定义u虚函数是在基类中被冠以虚函数是在基类中被冠以virtual的成员函数,它提供了一种的成员函数,它提供了一种接口界面。接口界面。u虚函数可以在一个或多个派生类中被重新定义。虚函数可以在一个或多个派生类中被重新定义。u虚函数在派生类中重新定义时,要求其函数原型,包括返回虚函数在派生类中重新定义时,要求其函数原型,包括返回类型、函数名、参数个数、参数类型的顺序必须完全相同。类型、函数名、参数个数、参数类型的顺序必须完全相同。#includeclass Base protected:int x;public:Base(int a)x=a;virtual void who()cout“base ”x“n”;class First_d:public Base public:First_d(int a):Base(a)void who()cout“First derivation”x“n”;class second_d:public Base public:second_d(int a):Base(a)void who()cout“Second derivation”xwho();p=&first_obj;p-who();p=&second_obj;p-who();();();base 1First derivation 2Second derivation 3 First derivation 2Second derivation 3 second_obj x3Base:who()Second_d:who()first_obj x2Base:who()first_d:who()pbase_obj x1virtual Base:who()基类的虚函数基类的虚函数who()定义了一种接口,在定义了一种接口,在派生类中此接口定义派生类中此接口定义了不同的实现版本,了不同的实现版本,由于虚函数的解释机由于虚函数的解释机制,实现了制,实现了“单界面,单界面,多实现版本多实现版本”的思想。的思想。这种在运行时刻将函这种在运行时刻将函数界面与函数的不同数界面与函数的不同实现版本进行匹配的实现版本进行匹配的过程,称为运行时的过程,称为运行时的多态性。多态性。p-who()虚函数与多态性虚函数与多态性基类函数基类函数f具有虚特性的条件是:具有虚特性的条件是:(1)在基类中,将该函数说明为在基类中,将该函数说明为virtual函数。函数。(2)定义基类的公有派生类。)定义基类的公有派生类。(3)在基类的公有派生类中原型一致地重载该虚函数。在基类的公有派生类中原型一致
展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 图纸专区 > 课件教案


copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!