C实用教程郑阿奇主编12_第1页
C实用教程郑阿奇主编12_第2页
C实用教程郑阿奇主编12_第3页
C实用教程郑阿奇主编12_第4页
C实用教程郑阿奇主编12_第5页
已阅读5页,还剩50页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、第12章 继承和派生 12.1 继承和派生概述n12.1.1 继承的概念n1. 继承和概括n继承(inheritance),是指一个事物可以继承其父辈全部或部分特性,同时本身还有自己的特性。 n2. 类继承相关概念n在c+中,当一个新类从一个已定义的类中派生后,新类不仅继承了原有类的属性和方法,并且还拥有自己新的属性和方法,称为类的继承和派生。被继承的类称为基类(base class)或超类(super class)(又称父类),在基类或父类上建立的新类称为派生类(derived class)或子类(sub class)。3. 类层次关系的描述方法上述父类和子类的关系称为类层次或继承关系。在类

2、设计时,常常将这些关系用树来描述。例如,下图就是用树来描述学校人员类的层次关系。12.1.2 继承的特性n在c+中,类的继承具有下列特性:n(1)单向性。 n(2)传递性。 n(3)可重用性。n12.1.3 派生类的定义n在c+中,一个派生类的定义可按下列格式:nclass:,nnn; 12.2 继承方式 nc+继承方式有三种:public(公有)、private(私有)及protected(保护)n12.2.1 公有继承n公有继承(public)方式具有下列特点: n(1)在派生类中,基类的公有成员、保护成员和私有成员的访问属性保持不变。n(2)派生类对象只能访问派生类和基类的公有(publ

3、ic)成员。 例ex_publicderived 派生类的公有继承示例。 n#include n#include nusing namespace std;nclass cpersonnnpublic:ncperson(char *name, int age, char sex = m)nnstrncpy(this-name, name, 20);nthis-age = age; this-sex = sex;nnvoid setnameandsex( char *name, char sex = m) / 更改姓名和性别nnstrncpy(this-name, name, 20);this-

4、sex = sex;nnprotected:nvoid setage(int age)nnthis-age = age;nnvoid showinfo()/ 显示信息nncout姓名:nameendl;ncout性别:(sex=m?男:女)endl;ncout年龄:agestuno, no, 20);nnvoid setscore( float s1, float s2, float s3 )nnscore0 = s1;score1 = s2;score2 = s3;ntotal = s1 + s2 + s3;ave = total / (float)3.0;nnvoid setnoandag

5、e(char *no, int age)nstrncpy(this-stuno, no, 20);this-setage( age ); nvoid showall()nnshowinfo();/ 调用基类的成员函数ncout学号:stunoendl;ncout三门成绩:score0tscore1tscore2endl;ncout总成绩和平均分:totaltaveendl;nnprivate:ncharstuno20;/ 学号nfloatscore3,ave, total;/ 三门成绩、平均分和总分n;nint main()nncstudent one(liming, 21050101, 19

6、 ); / anone.setscore( 90, 80, 84);/ bnone.showall();/ 调用派生类的公有成员函数none.setnameandsex(wangfang, w);n / 调用基类的公有成员函数none.setnoandage(21050102, 18 );n / 调用派生类的公有成员函数none.showall();/ 调用派生类的公有成员函数nreturn 0;n程序运行结果如下:12.2.2 私有继承n私有继承(private)方式具有下列特点:n(1)在派生类中,基类的公有成员、保护成员和私有成员的访问属性都将变成私有(private),且基类的私有成员

7、在派生类中被隐藏。 n(2)由于基类的所有成员在派生类中都变成私有的,因此基类的所有成员在派生类的子类中都是不可见的。 n(3)派生类对象只能访问派生类的公有成员,不能访问基类的任何成员。例ex_privatederived 派生类的私有继承示例。 n#include n#include nusing namespace std;nclass cpersonnnpublic:ncperson(char *name, int age, char sex = m)nnstrncpy(this-name, name, 20);nthis-age = age; this-sex = sex;nnvoi

8、d setnameandsex( char *name, char sex = m)n / 更改姓名和性别nnstrncpy(this-name, name, 20);this-sex = sex;nnprotected:nvoidsetage(intage)nnthis-age=age;nnvoidshowinfo()/显示信息显示信息nncout姓名:姓名:nameendl;ncout性别:性别:(sex=m?男男:女女)endl;ncout年龄:年龄:agestuno, no, 20);nnpublic:nvoid setscore( float s1, float s2, float

9、s3 )nnscore0 = s1;score1 = s2;score2 = s3;ntotal = s1 + s2 + s3;ave = total / (float)3.0;nnvoid setnoandage(char *no, int age)nnstrncpy(this-stuno, no, 20);this-setage( age );nnvoid showall()nnshowinfo();/ 调用基类的保护成员函数ncout学号:stunoendl;ncout三门成绩:score0tscore1tscore2endl;ncout总成绩和平均分:totaltaveendl;n/

10、修改的代码nvoid setnameandsex( char *name, char sex = m) / 更改姓名和性别nncperson:setnameandsex(name, sex); / 调用基类的公有成员函数nnprivate:ncharstuno20;/ 学号nfloatscore3,ave, total;n / 三门成绩、平均分和总分n;nint main()nncstudent one(liming, 21050101, 19 );none.setscore( 90, 80, 84);none.showall();none.setnameandsex(wangfang, w)

11、;none.setnoandage(21050102, 18 );none.showall();nreturn 0;nn程序运行结果同前。12.2.3 保护继承n保护继承(protected)方式具有下列特点:n(1)在派生类中,基类的公有成员、保护成员的访问属性都将变成保护的。 n(2)同私有继承一样,在保护继承方式下,派生类中仍可访问基类的公有成员和保护成员。 n12.3 派生类的构造和析构 n12.3.1 构造和析构次 1. 单继承n如图12.2(a)所示,a类是b类的基类,b类又是c类的基类,它们是多层单继承方式n其代码如下: nclass annpublic:na()cout执行a的

12、构造函数endl;na()cout执行a的析构函数endl;n;nclass b: public annpublic:nb()cout执行b的构造函数endl;nb()cout执行b的析构函数endl;n;nclass c: public bnnpublic:nc()cout执行c的构造函数endl;nc()cout执行c的析构函数endl;n;nint main()nc c;nreturn 0;nn程序运行结果如下 2. 多继承如图12.2(b)所示,类a和类b是c类的基类,它们是多继承方式,其代码如下:nclass annpublic:na()cout执行a的构造函数endl; na()c

13、out执行a的析构函数endl; n;nclass b nnpublic:nb()cout执行b的构造函数endl; nb()cout执行b的析构函数endl; n;nclass c: publicb,publica npublic:nc()cout执行c的构造函数endl;nc()cout执行c的析构函数endl;n;nint main()nnc c;nreturn 0;nn程序运行结果如下:n 执行b的构造函数n 执行a的构造函数n 执行c的构造函数n 执行c的构造函数n 执行a的构造函数n 执行b的构造函数12.3.2 派生类数据成员初始化n一个派生类的构造函数的定义可有下列格式:(形参

14、表)n:基类1(参数表), 基类2(参数表), , 基类n(参数表),n对象成员1(参数表), 对象成员2(参数表), , 对象成员n(参数表)n 成员初始化列表例如,一个长方体类ccuboid,它从基类矩形类crect派生而来。 基类crect的数据成员是两个cpoint类对象ptlt和ptrb,分别表示矩形的左上角点和右下角点的位置。派生类ccuboid自身的数据成员有表示高度的fheight,表示底面中点位置的cpoint对象ptcenter,如右图所示。具体程序如下。例ex_classderived 派生类的构造和析构示例。 n#include nusing namespace std

15、;nclass cpointnnpublic:ncpoint( int x = 0, int y = 0)/ cnnxpos = x;ypos = y;ncoutcpoint构造函数endl;nnvoid showpos(bool isend = false)nncoutpos(xpos, ypos);nif (isend)coutendl;nnprivate:nint xpos, ypos;n;nclass crectnnpublic:ncrect( int x1 = 0, int y1 = 0, int x2 = 0, int y2 = 0)/bn: ptlt(x1, y1), ptrb(

16、x2, y2)nncoutcrect构造函数endl;nnvoid showpos()nnptlt.showpos(); cout, ; ptrb.showpos(true);nnprivate:ncpoint ptlt, ptrb;n;nclass ccuboid: publiccrectnnpublic:nccuboid( int x1, int y1, int x2, int y2, int height )/an: crect(x1, y1, x2, y2),nptcenter(x1+x2)/2, (y1+y2)/2),nfheight(height)nncoutccuboid构造函数

17、endl;nnvoid showall()nncout矩形的角点为:;crect:showpos();ncout底面矩形的中点为:;ptcenter.showpos(true);ncout高为:fheightendl;nnprivate:ncpoint ptcenter;nfloatfheight;n;nint main()nnccuboid one( 5, 5, 30, 30, 50);none.showall();nreturn 0;nn程序运行结果如下:12.4 二义性和虚基类 n12.4.1 二义性概述n一般来说,在派生类中对基类成员的访问应该是唯一的。但是多继承或多层继承可能造成对基

18、类中某成员的访问出现不唯一的情况,这种情况称为基类成员调用的二义性。 n二义性出现的情况可以有下列两种。n1. 同名成员来源于不同的基类n例ex_conflict1同名成员来源于不同的基类 n#include nusing namespace std;nclass annpublic: n int x;na(int a = 0) x = a; n;nclass b nnpublic:n int x;nb( int a = 0, int b = 0) x = a; n;nclass c : publicb,publicannpublic:nint z;nc(int a, int b, int c

19、)n:a(a), b(b)nnz = c;nn void print()nncoutx = xendl;/编译出错的地方ncoutz = zendl;nn;nint main()nnc c1(100,200,300);nc1.print();nreturn 0;n程序中,派生类c同时继承了a和b这两个基类,由于基类a和基类b都有数据成员x,当编译到“coutx = xendl;”语句时,无法确定成员x是来自基类a还是来自基类b,因此产生了二义性,从而出现编译错误。解决这个问题的简单方法是使用域作用运算符“:”来消除二义性,即将print函数实现代码改为:nvoid print()nncouta

20、:x=a:xendl;ncoutb:x=b:xendl;ncoutz = zendl;nn重新运行的结果如下 2. 同名成员来源于同一个基类n例ex_conflict2同名成员来源于同一个基类。n#include nusing namespace std;nclass anpublic: n int x;na(int a = 0) x = a; n;nclass b1 : public annpublic:n int y1;nb1( int a = 0, int b = 0)n:a(b)nny1 = a;nn;nclass b2 : public annpublic:n int y2;nb2(

21、 int a = 0, int b = 0)n:a(b)nny2 = a;nn;nclass c : public b1, public b2nnpublic:nint z;nc(int a, int b, int d, int e, int m)n:b1(a,b), b2(d,e)nnz = m;nn void print()nncoutx = xendl;/ 编译出错的地方ncouty1 = y1, y2 = y2endl;ncoutz = zendl;nn;nint main()nnc c1(100,200,300,400,500);nc1.print();nreturn 0;nn程序中

22、,b1类和b2类都是从基类a继承的派生类,这时在继承b1和b2的派生类c中就有两个基类a的拷贝。当编译器编译到“coutx = xendl;”语句时,因无法确定成员x是从类b1中继承来的,还是从类b2继承来的,因此产生二义性,从而出现了编译错误。n解决这个问题仍可使用前面的方法,即使用域作用运算符“ :”通过指定基类来消除二义性。例如,可将print函数实现代码改写为 nvoid print()nncoutb1:x=b1:xendl;ncoutb2:x=b2:xendl;ncouty1 = y1, y2 = y2endl;ncoutz = zendl;nn重新运行后的结果如下:12.4.2 二

23、义性解决方法n(1)当派生类和其基类中都有一个成员x时,在派生类中访问的x就是派生类的成员x,这是c+中类的局部优先原则。n(2)若a类是b类的基类,b类是c类的基类,当a类和b类都有一个成员x时,则在子类c中访问的x是b类的成员,这是c+中类的最近优先原则。 n对于出现二义性的第1种情况,指定作用域是最好的解决方法。但对于第2种情况,指定作用域虽是一种解决方法,但不是最好的办法。因为在派生类c中总有两个基类a的拷贝,不仅多占用内存,而且效率也不高。为此在实际应用中多采用虚基类的形式来解决。 12.4.3 虚基类和虚继承n1. 虚基类的定义n虚基类不是指基类是虚的,而是指在派生类中指定的基类是

24、虚继承方式,即使用下列格式定义派生类的继承方式:nclass:virtualn;n例ex_virtualbase虚基类的使用示例。 n#include nusing namespace std;nclass annpublic:n int x;na(int a = 0) x = a; n;nclass b1 : virtual public a/ 声明虚继承nnpublic:nint y1;nb1( int a = 0, int b = 0)n:a(b)nny1 = a;nnvoid print(void)nncoutb1: x = x, y1 = y1endl;nn; nclass b2 :

25、 virtual public a/ 声明虚继承nnpublic:nint y2;nb2( int a = 0, int b = 0)n:a(b)nny2 = a;nnvoid print(void)nncoutb2: x = x, y2 = y2endl;nn;nclass c : public b1, public b2nnpublic:nint z;nc(int a, int b, int d, int e, int m)n:b1(a,b), b2(d,e)nnz = m;nnvoid print()nnb1:print();b2:print();ncoutz = zendl;nn;ni

26、nt main()nnc c1(100,200,300,400,500);c1.print();nc1.x = 400;c1.print();nreturn 0;nn程序运行结果如下:n2. 虚基类的实质n在上述示例中,类a、b1、b2和c的层次关系可用图12.5来表示 图12.5 类的层次关系12.5 兼容n兼容是指在公有派生情况下,一个派生类对象可以赋给基类对象,这种情况又称为赋值兼容,或称类型自动转换。n12.5.1 赋值兼容规则n简单地讲,对于公有派生类来说,可以将派生类的对象直接赋给其基类对象,反之却不可以。 12.5.2 赋值兼容机理n例ex_casttest派生类赋值兼容测试示例

27、。 n#include n#include nusing namespace std;nclass cannpublic:nca(int x = 0) a = x; nint geta() return a; nprivate:nint a;n;nclass cb nnpublic:ncb(int x = 0) b = x; nint getb() return b; nprivate:nint b;n;nclass cc: public cb, public cannpublic:ncc( int y = 0) c = y;nint getc() return c; nprivate:nin

28、t c;n;nint main()nncc *c = new cc(5);ncoutgetc(),getb(),geta()endl;nca a(3);ncb b(4);nmemcpy(ca*)c,&a,sizeof(a); /anmemcpy(cb*)c,&b,sizeof(b);/bncoutgetc(),getb(),geta()endl;ndelete c;nreturn 0;nn程序运行结果如下: ex_castother 派生类赋值兼容其他情况示例。 n#include n#include nusing namespace std;nclass cannpublic

29、:nca(int x = 0) a = x; nint geta() return a; nvoid showaddr()nncoutca对象的地址:(unsigned)thisendl;ncoutta的地址:(unsigned)&aendl;nnprivate:nint a;n;nclass cb nnpublic:ncb(intx=0)b=x;nintgetb()returnb;nvoidshowaddr()nncoutcb对象的地址:对象的地址:(unsigned)thisendl;ncouttb的地址:的地址:(unsigned)&bendl;nnprivate:nin

30、tb;n;nclasscc:publiccb,publiccannpublic:ncc(inty=0)c=y;nintgetc()returnc;nvoidshowaddr()nnca:showaddr();ncb:showaddr();ncoutcc对象的地址:对象的地址:(unsigned)thisendl;ncouttc的地址:的地址:(unsigned)&cendl;nnprivate:nint c;n;nint main()nncc c(5);/ c对象中的数据成员c为5ncout- cc c(5);-endl;nc.showaddr();ncout- ca *a = &c;-showaddr();ncout- cb &b = c; -endl;ncb &b = c;/ bnb.showaddr();nb = cb(4);n*a = ca(3);ncoutc.getc(),c.getb(),c.geta()endl;nreturn 0;n程序运行结果如下: 12.6 综合应用实例n12.6.1 类间关系n1. 继承关系n2. 组合关系 n3. 共享关系12.6.2 设计实例n例ex_multi12综合应用实例 n#include n#include nusing namespace std;ncla

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论