《第11章类间关系的实现》由会员分享,可在线阅读,更多相关《第11章类间关系的实现(34页珍藏版)》请在金锄头文库上搜索。
1、第第1111章章 类间关系的实现类间关系的实现11.1 一般一般特殊关系的实现特殊关系的实现11.2 多态性与虚函数多态性与虚函数11.3 整体整体部分关系的实现部分关系的实现11.4 关联关系的实现关联关系的实现11.5 关于类层次的总结关于类层次的总结学习目的:学习目的: 掌握类间关系的掌握类间关系的C+C+实现;实现; 了解多态性与虚函数的概念。了解多态性与虚函数的概念。圈糠砌吻漫死绘磅峡尖镐初棋僳告像尖聚泄上荡江刹鬃以筷仟吏窒祟腑稿第11章类间关系的实现第11章类间关系的实现11.1 11.1 一般一般特殊关系的实现特殊关系的实现11.1.1类的继承与派生类的继承与派生11.1.2赋值
2、兼容规则赋值兼容规则11.1.3两义性与作用域分辨两义性与作用域分辨C+C+提供了描述一般提供了描述一般特殊关系的语法,在特殊关系的语法,在C+C+中称为类的中称为类的派生或继承,通常分为单一继承和多重继承。在派生或继承,通常分为单一继承和多重继承。在C+ C+ 中常中常把一般把一般特殊关系中的一般类称为父类,而把特殊类称为特殊关系中的一般类称为父类,而把特殊类称为子类。子类。革柏颗骋填脾葵好艇芜尽旱惶士请湖佰皂肚固买抽牌摹咕朝驹看庆槛顽睁第11章类间关系的实现第11章类间关系的实现11.1.111.1.1类的继承与派生类的继承与派生1. 单一继承单一继承派生类说明格式:派生类说明格式:cla
3、ss : ;派生类初始化构造函数格式如下:派生类初始化构造函数格式如下:ClassName:ClassName(ArgList0) : DerivedClassName(ArgList1) 例例11.1 11.1 描述由矩形、正方形组成的平面图形系统描述由矩形、正方形组成的平面图形系统 类体中的成员为子类所特有的数据成员和成员函数,虽然没有在子类中类体中的成员为子类所特有的数据成员和成员函数,虽然没有在子类中写明所继承的父类成员,但是父类成员在一定限制下属于子类。写明所继承的父类成员,但是父类成员在一定限制下属于子类。 矩形矩形长长宽宽位置位置求面积求面积求周长求周长求位置求位置正方形正方形求
4、面积求面积求周长求周长求位置求位置边边 长长#include iostream.hclass CRectangle /矩形类矩形类 public: CPoint m_cpLocation;/图形所在位置图形所在位置 int m_nWidth;/图形的宽度图形的宽度 int m_nHeight;/图形的高度图形的高度 CRectangle(int nX, int nY, int nWidth, int nHeight); int GetArea();/求面积求面积 int GetPerimeter(); /求周长求周长 CPoint& GetPosition();/返回图形位置返回图形位置;勿饼
5、属晨义率筹滓挂碘岛抗绊挺倘剁龟惊铝灸痉稽材腻衙而肩上凌醉顶鞘第11章类间关系的实现第11章类间关系的实现CRectangle:CRectangle(int nX,int nY,int nW,int nH):m_cpLocation(nX,nY) m_nWidth=nW; m_nHeight=nH;int CRectangle:GetArea() return m_nWidth*m_nHeight; int CRectangle:GetPerimeter() return m_nWidth+m_nWidth+m_nHeight+m_nHeight; CPoint& CRectangle:GetP
6、osition() return this-m_cpLocation; class CSquare : public CRectangle /正方形类,派生自矩形类正方形类,派生自矩形类 public: CSquare(int nX, int nY, int nEdge); int GetEdge(); /返回边长返回边长;CSquare:CSquare(int nX, int nY, int nEdge) : CRectangle(nX, nY, nEdge, nEdge) int CSquare:GetEdge() return m_nWidth; void main() CRectang
7、le r(1,1,2,3); CSquare s(0,0,2); coutr.GetArea()endls.GetArea()endl;万缩籍斗雍教肥酋豫徒房肖凑湍篮唆味掖儿舀菠入垢慷痰滥笨闷曹需狸眼第11章类间关系的实现第11章类间关系的实现11.1.111.1.1类的继承与派生类的继承与派生2. 基类成员访问控制基类成员访问控制 有两个因素同时控制着派生类对基类成员的访问权限,这两个因素就是有两个因素同时控制着派生类对基类成员的访问权限,这两个因素就是基类类体中类成员的访问说明符,及派生类的派生方式。基类类体中类成员的访问说明符,及派生类的派生方式。基基类类成成员员在派生在派生类类中的中的
8、访问权访问权限限成成类员类员派生方式派生方式privateprotectedpublicprivate不可不可访问访问私有私有私有私有protected不可不可访问访问保保护护保保护护public不可不可访问访问保保护护公有公有基类的基类的private成员将不被子类继承,且不能被子类成员访问。成员将不被子类继承,且不能被子类成员访问。private派生方式:派生方式:基类成员(基类成员(private类除外)作为子类的类除外)作为子类的private类类型成员。型成员。public派生方式:派生方式:基类成员(基类成员(private类除外)作为子类的相同类型类除外)作为子类的相同类型成员。
9、成员。protected派生方式:派生方式: 基类成员(基类成员(private类除外)作为子类的类除外)作为子类的protected类型成员。类型成员。涛瘸冶遣乍溪卧唱霉理佯绣创期昭碎数厢很兔榜谓锻薄丈减蹋搀术尺赛股第11章类间关系的实现第11章类间关系的实现11.1.111.1.1类的继承与派生类的继承与派生3 多重继承多重继承 多重继承在多重继承在C+C+中实现方式如下:中实现方式如下:class : , ;派生类构造函数应该调用所有基类的构造函数对基类数据成员进行初始派生类构造函数应该调用所有基类的构造函数对基类数据成员进行初始化,格式如下:化,格式如下::(ArgList0) : (
10、ArgList1), (ArgListn) #include iostream.h#include stdlib.h#include string.hclass CWnd; / 引用性说明引用性说明#define MAXTEXTBUFFER 0xffffclass CPoint private: int m_x; int m_y; 4 继承与派生示例继承与派生示例 浚陵推佯扭淆残栖吨恃戈草憎澳浓违攘原惫河撬窟恭邪酵盲煎纲蛛翌克妓第11章类间关系的实现第11章类间关系的实现 public: CPoint(int x=0, int y=0) m_x=x; m_y=y; int GetX() ret
11、urn m_x; int GetY() return m_y; ;class WNDSTRUCT /本对象中含有窗口公共数据本对象中含有窗口公共数据 protected: char* m_pczWndName;/窗口名字窗口名字 /下述四个量表示窗口左上角和右下角的坐标下述四个量表示窗口左上角和右下角的坐标 CPoint m_cpTopLeft; CPoint m_cpBottomRight; /下述三个量用于建立窗口系统的树结构下述三个量用于建立窗口系统的树结构 CWnd* m_pParentWindow; /指向本窗口的父窗口指向本窗口的父窗口 CWnd* m_pChildFirst;/C
12、Wnd的指针数组,放着本窗口的子窗口的指针数组,放着本窗口的子窗口 CWnd* m_pSiblingFirst;/指向本窗口的兄弟窗口指向本窗口的兄弟窗口 char* m_pEditTextBuffer;/指向窗口编辑区文本缓冲区指向窗口编辑区文本缓冲区 WNDSTRUCT(const WNDSTRUCT& rWndArch) m_pczWndName=new charstrlen(rWndArch.m_pczWndName)+1; if(m_pczWndName=0) coutNo enough space!endl; exit(0); strcpy(m_pczWndName, rWndAr
13、ch.m_pczWndName); 翘所颇妄愤汞廷旭断瞬呀沂屯笛氮驶更烽潘士率访沉胜漾趋摧霞裙俯苟舀第11章类间关系的实现第11章类间关系的实现WNDSTRUCT() delete m_pczWndName; ;class CScreenObject : virtual private WNDSTRUCT public: void MoveToWindow(const CPoint& cpWndPos, int nWidth, int nHeight) HideWindow(); m_cpTopLeft=cpWndPos; m_cpBottomRight=CPoint(m_cpTopLeft.
14、GetX()+nWidth, m_cpBottomRight.GetY()+nHeight); RedrawWindow(); CScreenObject() delete m_pczWndName; void HideWindow() /*隐藏当前窗口隐藏当前窗口*/ void RedrawWindow() /*绘制并显示当前窗口绘制并显示当前窗口*/ ;胞昼绚间蚜糖聪酒汐瞬溺氛铬痘怪撤谦粳盔鸽沈崇彰帆皂滑胚乒吓砒尧鹿第11章类间关系的实现第11章类间关系的实现class CEditText : virtual private WNDSTRUCT public: CEditText(WNDS
15、TRUCT& rWndArch) : WNDSTRUCT(rWndArch) m_pEditTextBuffer=new charMAXTEXTBUFFER+1; if(m_pEditTextBuffer=0) coutNo enough space; exit(1); memset(m_pEditTextBuffer, 0, MAXTEXTBUFFER+1); CEditText() delete m_pEditTextBuffer; void TextCut(int nStart, int nEnd) char* TempStr=new charMAXTEXTBUFFER-nEnd; me
16、mset(m_pEditTextBuffer+nEnd, 0, MAXTEXTBUFFER-nEnd); memcpy(m_pEditTextBuffer+nEnd, TempStr, MAXTEXTBUFFER-nEnd); delete TempStr; /此处应该调用适当函数重新绘制编辑区显示内容此处应该调用适当函数重新绘制编辑区显示内容 /* TextPaste(), TextCopy() 等函数等函数 */;和小刀膀吁泡噬娠胎可狐索弄浸炕咱宽盟疟纵猩罐跌橇武割嗓缀硝拢英泌第11章类间关系的实现第11章类间关系的实现class CWindowTree : virtual private
17、 WNDSTRUCT public: CWindowTree(WNDSTRUCT& rWndArch) : WNDSTRUCT(rWndArch) void AddChild(CWnd* pChild) while(*m_pChildFirst!=0) +m_pChildFirst; *m_pChildFirst=pChild; /* 其他关于窗口树的操作其他关于窗口树的操作 */;class CWnd : public CWindowTree, public CEditText, public CScreenObject ; 昨桂詹灰测绅庭馅摹惯博琢汛瘦帖想遮实绥雷菱晰喇奇瘩捏晤启蒸板妮些第
18、11章类间关系的实现第11章类间关系的实现class CDerived : public CBase1, public CBase2 public: int b; CDerived() b=0x21; ;void main() CDerived obj; 11.1.111.1.1类的继承与派生类的继承与派生5. 派生类对象内存映像派生类对象内存映像 例例11.4class CBase public: int b0; CBase() b0=0x01; ;class CBase1 : public CBase public: int b1; CBase1() b1=0x11; ;class CBa
19、se2 : public CBase public: int b2; CBase2() b2=0x12; ;存储内容存储内容变量变量地地址址增增加加方方向向00 00 00 21b00 00 00 12b200 00 00 01b000 00 00 11b1起始地址起始地址00 00 00 01b0CBase2CBase2对象对象CBase1CBase1对象对象CDerivedCDerived对象对象荤惧巧泅棘澡狼礼答宝清汪淳犯可懂跌课台酋迂瘫刑冈俊逃栏艺拔群沽聂第11章类间关系的实现第11章类间关系的实现11.1.211.1.2赋值兼容规则赋值兼容规则1. 派生类对象可以赋值给父类对象派生类
20、对象可以赋值给父类对象对于对于例例11.4中的类,下列语句合法:中的类,下列语句合法:CBase b;CBase1 b1;b=b1;2. 2. 派生类的对象可以用于基类引用的初始化派生类的对象可以用于基类引用的初始化 对于对于 例例11.411.4,下列语句合法:,下列语句合法:CBase1 b1;CBase& refBase=b1;对于对于例例11.4下列语句合法:下列语句合法:CBase1 b1;CBase *pBaseObj=&b1;3. 3. 派生类对象的地址可以赋值给指向基类的指针派生类对象的地址可以赋值给指向基类的指针 存储内容存储内容变量变量00 00 00 11b1起始地址起始
21、地址00 00 00 01b0CBase1CBase1对象对象CBaseCBase对象对象通过派生类通过派生类CBaselCBasel对象的对象的内存映像图可以看到这种内存映像图可以看到这种赋值的物理意义。赋值的物理意义。 沏附麦臭佃紧浊港弧嘎悍镣雕梳狙妇绅懒洞胡汉险梨蘑坡漂迅犊诲囤盖能第11章类间关系的实现第11章类间关系的实现11.1.311.1.3两义性与作用域分辨两义性与作用域分辨1. 作用域分辨作用域分辨如类的多个父类中具有相同名数据成员或成员函数,在引用该成员时如类的多个父类中具有相同名数据成员或成员函数,在引用该成员时可使用作用域分辨符可使用作用域分辨符 : : 来区分所引用的名
22、字究竟属于哪个父类。来区分所引用的名字究竟属于哪个父类。 例例11.5class CBase1 public: void MyFunc() coutThis is CBase1s MyFuncendl; ;class CBase2 public: void MyFunc() coutThis is CBase2s MyFuncendl; ;class CDerived : public CBase1, public CBase2 public: void func() MyFunc(); /错误!错误! 两义性!两义性!;void main() CDerived obj; obj.func()
23、;显然,派生类显然,派生类CDerivedCDerived中函数中函数func()func()对父类成员对父类成员函数函数MyFunc()MyFunc()的引用是具有二义性的,编译器无的引用是具有二义性的,编译器无法判断所要调用的是哪一个父类的成员函数,因法判断所要调用的是哪一个父类的成员函数,因此相应的语句出现语法错误。解决这种错误的办此相应的语句出现语法错误。解决这种错误的办法是在程序中使用作用域分辨符直接指明所要引法是在程序中使用作用域分辨符直接指明所要引用的是哪个类的用的是哪个类的MyFunc()MyFunc(),因此将派生类,因此将派生类CDerivedCDerived的定义改写如下
24、:的定义改写如下:class CDerived :public CBase1, public CBase2 public: void func() CBase1:MyFunc(); /调用调用CBase1类的成员函数类的成员函数MyFunc() CBase2:MyFunc(); /调用调用CBase2类的成员函数类的成员函数MyFunc() ;泞巩榜俩秃楚买睬捷怠支闷施彩曹婉萎扒妹奏酝勒恫仪绥褐恍送硼螟息鲍第11章类间关系的实现第11章类间关系的实现11.1.311.1.3两义性与作用域分辨两义性与作用域分辨2. 支配规则支配规则如果类如果类Y Y是类是类X X的一个基类,则的一个基类,则X
25、X中的成员中的成员namename支配基类中的同名成员。支配基类中的同名成员。如果在程序中要访问被支配的名字,可以使用作用域分辨符。如果在程序中要访问被支配的名字,可以使用作用域分辨符。 class A public: int a();class B : public virtual A public: int a();class C : public virtual A ;class D : public B, public C public: D() a();/ 无二义性无二义性. B:a() 支配支配 A:a. ;CBaseCBaseint bint bCBase1CBase1CBase
26、2CBase2CDerivedCDerivedint func()int func()CBaseCBaseint bint b从同一个类直接继承两次以上从同一个类直接继承两次以上祖韧镑钉寒服罢尺窜遗腾汪诧玫陆视愁袱彦盘亦汗搀攀砾囤矛睹卞啦柳汽第11章类间关系的实现第11章类间关系的实现11.1.311.1.3两义性与作用域分辨两义性与作用域分辨3. 虚基类虚基类多个父类由同一类派生,创建对象时内存中会有爷爷类多个实例(例多个父类由同一类派生,创建对象时内存中会有爷爷类多个实例(例11.4)。可采用两种方式消除二义性,其一使用)。可采用两种方式消除二义性,其一使用 :,其二将爷爷类作,其二将爷爷
27、类作为虚基类,使创建对象时内存中只有爷爷类的一个实例。为虚基类,使创建对象时内存中只有爷爷类的一个实例。例例11.7 派生类的两个父类具有一个共同的虚基类。派生类的两个父类具有一个共同的虚基类。class CBase public: int b0; CBase() b0=0x01; ;class CBase1 : public virtual CBase public: int b1; CBase1() b1=0x11; ;class CBase2 : public virtual CBase public: int b2; CBase2() b2=0x12; ;class CDerived
28、:public CBase1, public CBase2 public: int b; CDerived() b=0x21; ;void main() CDerived obj; 幂员茎心雌钮酵胁护幸单阐曙撰祭镑唇先置趁搓抹乾敝远郧丹猩曲囊测查第11章类间关系的实现第11章类间关系的实现11.1.311.1.3两义性与作用域分辨两义性与作用域分辨3. 虚基类虚基类口眨似各质烂劈疽猩辅临惜昧癣言忌体它服倚褒玲伤盂渠疼谬诫畜帕拼睬第11章类间关系的实现第11章类间关系的实现11.2 11.2 多态性与虚函数多态性与虚函数11.2.1 编译时刻的多态性编译时刻的多态性11.2.2 运行时刻的多态性
29、运行时刻的多态性11.2.3 虚函数虚函数11.2.4 纯虚函数与抽象类纯虚函数与抽象类广义的多态性可以理解为一个名字具有多种语义。面向对象中的多态广义的多态性可以理解为一个名字具有多种语义。面向对象中的多态性是指不同类的对象对于同一消息的处理具有不同的实现性是指不同类的对象对于同一消息的处理具有不同的实现, ,在在C+C+中表中表现为同一形式的函数调用,可能调用不同的函数实现。现为同一形式的函数调用,可能调用不同的函数实现。C+C+的多态性可分为两类,一类称为的多态性可分为两类,一类称为编译时刻多态性编译时刻多态性,另一类称为,另一类称为运运行时刻多态性行时刻多态性。与之相应的概念有。与之相
30、应的概念有静态联编静态联编(亦称静态绑定、静态集(亦称静态绑定、静态集束、静态束定等)、束、静态束定等)、动态联编动态联编(亦称动态绑定、动态集束、动态束定(亦称动态绑定、动态集束、动态束定等)。等)。 胖拌笔防呀肺瘴唆洲彻态伤逛姑尘弯员国跨朱磋讥紧哗慈支梳干照目割挂第11章类间关系的实现第11章类间关系的实现11.2.111.2.1 编译时刻的多态性编译时刻的多态性函数重载为一种常见的编译时刻多态性,编译时通过参数类型匹配,定函数重载为一种常见的编译时刻多态性,编译时通过参数类型匹配,定位所调用函数的具体实现,然后用该实现代码调用代替源程序中的函数位所调用函数的具体实现,然后用该实现代码调用
31、代替源程序中的函数调用。调用。例例11.9 编译时刻多态性。编译时刻多态性。#include iostream.hconst float PI=float(3.14);class CPoint private: int m_x; int m_y; public: CPoint(int x=0, int y=0); void Area() coutHere is a points area: 0endl; ;CPoint:CPoint(int x, int y) m_x=x; m_y=y;class CCircle : public CPoint private: float m_nRadius
32、; public: CCircle(int x=0, int y=0, float r=0) : CPoint(x, y) m_nRadius=r; void SetRadius(float r) m_nRadius=r; void Area() coutHere is a circles area: PI*m_nRadius*m_nRadiusendl; ;void main() CCircle c1; c1.Area();哦咕肄札嗜澄束刮馆谓警歉掠愚缨怎良傍照疲鸭则莱胸三坍瑰湍绳丫喂池第11章类间关系的实现第11章类间关系的实现11.2.211.2.2运行时刻的多态性运行时刻的多态性运行时
33、刻多态性的实现机制是动态联编,在程序运行时刻确定所要调用运行时刻多态性的实现机制是动态联编,在程序运行时刻确定所要调用的是哪个具体函数实现,这种联编形式的程序运行效率低于静态联编,的是哪个具体函数实现,这种联编形式的程序运行效率低于静态联编,因为要花额外开销去推测所调用的是哪一个函数。虽然动态联编的运行因为要花额外开销去推测所调用的是哪一个函数。虽然动态联编的运行效率低于静态联编,但是动态联编为程序的具体实现带来了巨大的灵活效率低于静态联编,但是动态联编为程序的具体实现带来了巨大的灵活性,使得对变化万千的问题空间对象的描述变得容易,使函数调用的风性,使得对变化万千的问题空间对象的描述变得容易,
34、使函数调用的风格比较接近人类的习惯。格比较接近人类的习惯。例例11.10 运行时刻的多态性。运行时刻的多态性。#include iostream.hconst float PI=float(3.14);class CPoint private: int m_x; int m_y; public: CPoint(int x=0, int y=0); void Area() coutHere is a points area: 0endl; ;CPoint:CPoint(int x, int y) m_x=x; m_y=y;贸泵越培虫搂逛压袒滨娟抒跑机谭鹤缚敬端私粒惭亨跌蹲俏覆逛爬泻柏逐第11章类
35、间关系的实现第11章类间关系的实现class CCircle : public CPoint private: float m_nRadius; public: CCircle(int x=0, int y=0, float r=0) : CPoint(x, y) m_nRadius=r; void SetRadius(float r) m_nRadius=r; void Area() coutHere is a circles area: PI*m_nRadius*m_nRadiusArea();羽袁咀澡沁姑剑轴束篷瞒矮碴混袜碴舆舱股居甄猾浅契绽匙贷牲鸯藏滑宾第11章类间关系的实现第11章类
36、间关系的实现11.2.311.2.3 虚函数虚函数类的一个成员函数被说明为虚函数表明它目前的具体实现仅仅是一种假类的一个成员函数被说明为虚函数表明它目前的具体实现仅仅是一种假设,只是一种适用于当前类的实现,在未来类的派生链条中有可能重新设,只是一种适用于当前类的实现,在未来类的派生链条中有可能重新定义这个成员函数的实现定义这个成员函数的实现 (override)。虚函数的使用方法如下:)。虚函数的使用方法如下:class vitual void MyFunction(); ;void :MyFunction() 当某一个成员函数在基类中被定义为虚函数,那么只要同名函数出现在当某一个成员函数在基
37、类中被定义为虚函数,那么只要同名函数出现在派生类中,如果在类型、参数等方面均保持相同,那么,即使在派生类派生类中,如果在类型、参数等方面均保持相同,那么,即使在派生类中的相同函数前没有关键字中的相同函数前没有关键字virtual,它也被缺省地看作是一个虚函数,它也被缺省地看作是一个虚函数,但为保证风格统一,建议在派生类的虚函数前仍然添加关键字但为保证风格统一,建议在派生类的虚函数前仍然添加关键字virtual。皿姻杰春孺吕铜硫寐檄设荆摧暑善获泊迸卸且驴油挑着扎限项镐酿锈旨沙第11章类间关系的实现第11章类间关系的实现11.2.311.2.3 虚函数虚函数不同语言环境实现虚不同语言环境实现虚函数
38、的机制不同,下函数的机制不同,下面通过类面通过类CDerived的的派生介绍派生介绍Visual C+中如何实现虚函数。中如何实现虚函数。(程序见备注)(程序见备注)1 虚函数的实现虚函数的实现迭薯慌届沏彩东沥麓榴厌斟旷督乱枕豫妻惜讲肾碴贼摄料用系扑摈琅工荆第11章类间关系的实现第11章类间关系的实现11.2.311.2.3 虚函数虚函数2 虚函数的使用虚函数的使用 虚函数的实现机制和调用方式与非虚函数不同,因此虚函数的使用具有特虚函数的实现机制和调用方式与非虚函数不同,因此虚函数的使用具有特殊性。殊性。虚函数的访问权限虚函数的访问权限 派生类中虚函数的访问权限并不影响派生类中虚函数的访问权限
39、并不影响虚函数的动态联编,例如下面的程序实例虚函数的动态联编,例如下面的程序实例 例例11.1111.11,其中,其中派生类派生类CDerivedCDerived中重新定义了虚函数中重新定义了虚函数Func4()Func4(),在程序的运,在程序的运行中由于虚函数的机制,在行中由于虚函数的机制,在CBase:Func3()CBase:Func3()中调用中调用Func3()Func3()时会调用时会调用CDerived:Func3()CDerived:Func3(),而该函数的访问权限是私有,而该函数的访问权限是私有的。的。成员函数中调用虚函数成员函数中调用虚函数 在成员函数中可以直接调用相应
40、在成员函数中可以直接调用相应类中定义或重新定义的虚函数,分析这类函数的调用次序类中定义或重新定义的虚函数,分析这类函数的调用次序时要注意成员函数的调用一般是隐式调用,应该将之看做时要注意成员函数的调用一般是隐式调用,应该将之看做是通过是通过thisthis指针的显式调用,参见下例:指针的显式调用,参见下例:喂墨崎浩曳各劝行核寅首便陋闰生众匹稗狙掂雇迷稗洋戴搓菲静诡伤杜西第11章类间关系的实现第11章类间关系的实现例例11.11 在成员函数中调用虚函数。在成员函数中调用虚函数。#include iostream.hclass CBase public: void Func1() cout CBa
41、se:Func1= ; Func2(); void Func2() cout ; Func3(); virtual void Func3() cout ; Func4(); virtual void Func4() cout outendl; ; 撬眠啄露啤遭甚鸟鼓移任索报腿鹃鬼棠沫挑砸玉蔼冰谨淮决很极沃甚剩译第11章类间关系的实现第11章类间关系的实现 class CDerived : public CBase private: virtual void Func4() cout out endl; public: void Func1() cout Derived:Func1= ; CBa
42、se:Func2(); void Func2() cout Derived:Func2= ; Func3(); ;void main() CBase* pBase; CDerived dObj; pBase=&dObj; pBase-Func1(); dObj.Func1();潍追颗道铝溺伦萤晚蛋揽譬睬井孩荫状进蜘捧并脉凑刹嗽娥逾旧启伏薄呈第11章类间关系的实现第11章类间关系的实现11.2.311.2.3 虚函数虚函数class CBase1 public: virtual void MyFunc() coutCBase1:MyFuncendl; ;class CBase2 public:
43、virtual void MyFunc() coutCBase2:MyFuncendl; ;class CDerived : public CBase1, public CBase2 public: virtual void MyFunc() coutCDerived:MyFuncMyFunc(); pB2-MyFunc(); 3. 多重继承与虚函数多重继承与虚函数程序中指向父类程序中指向父类CBase1、CBase2的指针的指针pB1、pB2分别被赋予了派分别被赋予了派生类生类CDerived对象的地址,由于对象的地址,由于MyFunc( )函数为虚函数,因此通过两个函数为虚函数,因此通过两
44、个指针调用该函数的结果是都调用指针调用该函数的结果是都调用了派生类的函数了派生类的函数 CDerived:MyFunc( ) 。采用这种方式能够使前期程序设采用这种方式能够使前期程序设计人员调用后期程序设计人员所计人员调用后期程序设计人员所实现的具体函数。实现的具体函数。回菊当电疾瘫澄隧虹武驯涟孙涝俞鸟讨喝换网厦漫顺虫殿非丧唱冻咏巫役第11章类间关系的实现第11章类间关系的实现11.2.311.2.3 虚函数虚函数class CBase public: virtual void MyFunc1() ;class CDerived1 : virtual public CBase public:
45、virtual void MyFunc1() coutCDerived1:MyFunc1endl; ; class CDerived2 : virtual public CBase public: virtual void MyFunc2() coutCDerived2:MyFunc2endl; MyFunc1(); ;3. 多重继承与虚函数多重继承与虚函数可以说明具有虚函数的虚基类,适当地使用这种方式能够提供作为父类的可以说明具有虚函数的虚基类,适当地使用这种方式能够提供作为父类的两个兄弟类实例之间的通信,是一种较好的通信方式两个兄弟类实例之间的通信,是一种较好的通信方式 。 class C
46、Derived : virtual public CDerived1, virtual public CDerived2 ;void main() CDerived dObj; dObj.MyFunc2();蜀卵负凝帮蕴躺挛袜昆毛灌掳憨郎洞民硷妥鸟即侩窜垮推屁额凑汝拨吧亲第11章类间关系的实现第11章类间关系的实现11.2.311.2.3 虚函数虚函数#include iostream.h#include string.hclass CBase public: virtual CBase() coutCBase:CBase(); ;class CDerived : public CBase p
47、ublic: virtual CDerived() coutCDerived:CDerived()endl; ;void main() CBase* pB=new CDerived; delete pB;4. 虚析构函数虚析构函数析构函数可以被说明为虚函数,利用虚析构函数,删除对象时不必考虑对析构函数可以被说明为虚函数,利用虚析构函数,删除对象时不必考虑对象的类型(父类或子类),虚函数机制将保证调用适当的析构函数。象的类型(父类或子类),虚函数机制将保证调用适当的析构函数。 输竹窜桶墩图淌骑涎尽要边丸耕盘洋软登视醛势再愁驯沟恍沧讨始戳篱舍第11章类间关系的实现第11章类间关系的实现11.2.4
48、11.2.4纯虚函数与抽象类纯虚函数与抽象类软件系统的功能由类层次中的各类所实现,不同的类提供了相应层次的软件系统的功能由类层次中的各类所实现,不同的类提供了相应层次的功能实现,通过类的用户接口可以调用这些功能,人们通常所习惯的不功能实现,通过类的用户接口可以调用这些功能,人们通常所习惯的不是将功能在不同类层次的实现用不同的接口表示,而是将概念上相似的是将功能在不同类层次的实现用不同的接口表示,而是将概念上相似的功能用一个统一的接口功能用一个统一的接口在最顶层在最顶层表示,例如:一个系统可能提供了表示,例如:一个系统可能提供了“打印打印”这一功能,但这一功能,但“打印打印”对其各组成部分的含义
49、不同,可能包括打印文本文对其各组成部分的含义不同,可能包括打印文本文件、打印照片、打印图形等,这些打印功能的具体实现由各个类提供,件、打印照片、打印图形等,这些打印功能的具体实现由各个类提供,但对整个系统来讲它们应该具有相同的接口,在调用时应能够根据具体但对整个系统来讲它们应该具有相同的接口,在调用时应能够根据具体情况调用其具体实现。虚函数可以帮助我们做到这一点,若干概念上相情况调用其具体实现。虚函数可以帮助我们做到这一点,若干概念上相似的操作可以用一个虚函数描述,该虚函数在较高层次上表示一种功能似的操作可以用一个虚函数描述,该虚函数在较高层次上表示一种功能的接口,而在不同类中对该虚函数的重新
50、定义就是该项功能不同层次上的接口,而在不同类中对该虚函数的重新定义就是该项功能不同层次上的实现,虚函数调用机制可保证虚函数的某个恰当的实现被调用。也就的实现,虚函数调用机制可保证虚函数的某个恰当的实现被调用。也就是说,利用虚函数,可以使系统中多个相似的功能具有统一的接口,改是说,利用虚函数,可以使系统中多个相似的功能具有统一的接口,改善了类的用户接口。方式如下:善了类的用户接口。方式如下:class virtual (ArgList)=0; ;临阂触补虾示吁淮矢脏容卜捉愈吏撇酸艾赏捡友已鄂掩刨变钧琼泞烹东氧第11章类间关系的实现第11章类间关系的实现11.3 11.3 整体整体部分关系的实现部
51、分关系的实现C+对整体对整体部分关系提供支持手段,对复合聚合,采用嵌入式对象的方部分关系提供支持手段,对复合聚合,采用嵌入式对象的方式,即属性的类型为类,例如消息窗口类可以如下定义:式,即属性的类型为类,例如消息窗口类可以如下定义:class CButton CButton() ;class CIcon CIcon() ;class CStudent CStudent(const char* pStudentName) ;class CGroup private: CStudent* m_pStudents; int m_nGroupID; CStudentCStudent组号:组号:int
52、m_nGroupIDint m_nGroupID学生:学生:CStudent* m_pStudentsCStudent* m_pStudents.CClassCClass设设 置置 学学 生生 :SetStudent(CStudent*)SetStudent(CStudent*)构造空组构造空组nIDnID :CGroup(int nID)CGroup(int nID).小组成员小组成员 5 525 25 班级成员班级成员班号:班号:int m_nClassIDint m_nClassID学生:学生:CStudent* m_pStudentsCStudent* m_pStudents.CCla
53、ssCClass设设 置置 学学 生生 :SetStudent(CStudent*)SetStudent(CStudent*)构造空班构造空班nIDnID :CClass(int nID)CClass(int nID).莆歧烘某嘶爹胸戳廉亨竭倍朱鳖馆燃抨抿家衙蚤颗殴壤涟应普孵晾纤锑芝第11章类间关系的实现第11章类间关系的实现public: void SetStudent(CStudent* pStudents) m_pStudent=pStudent; CGroup(int nID) m_nGroupID=nID; m_pStudents=new CStudnet5; /小组由小组由5名学生
54、组成名学生组成 ;class CClass private: CStudent* m_pStudents; int m_nClassID; public: CClass(int nID) m_nClassID=nID; m_pStudents=new CStudnet35; /班级由班级由35名学生组成名学生组成 眠胃国窜悍雌肄孜习菱癣旨氧卡灾谣闸了吓甄劣寝屈契赃否婆翘颂懒匹豌第11章类间关系的实现第11章类间关系的实现 CStudent* GetStudent() return m_pStudents; void SetStudent(CStudent* pStudents) m_pStud
55、ent=pStudent; ;void main() CStudent theWhole2550= CStudent(Marry), , CStudent(Tom) ; CClass MyClass3(3); /成立班级,但具体由哪些学生组成尚未确定成立班级,但具体由哪些学生组成尚未确定 MyClass3.SetStudent(theWhole+70); /本班学生由学校学生名册中第本班学生由学校学生名册中第71位开始的位开始的35人组成人组成 CGroup Group6(6); Goup6.SetStudent(MyClass3.GetStudent()+25); /第第6学习小组由班级学生
56、名册中第学习小组由班级学生名册中第26名开始的名开始的5个人组成个人组成 睛埋秆膛光壕辆腆酵陡尽迷咙卫字纺序挥你伺侦神椿芭我癌游矫器栏闻累第11章类间关系的实现第11章类间关系的实现class CCompany private: char* m_pName; /按图中要求按图中要求 CPerson* box; /图中标出老板可有可无,故用指针表示图中标出老板可有可无,故用指针表示 CPerson m_Employee20 /公司员工最多公司员工最多20;class CPerson private: char* m_pName; int m_nAge; CCompany* comp; /comp
57、为一个为一个CCompany的数组,因为可为多个公司工作的数组,因为可为多个公司工作 CPeron consort; /按图中的关系添加了这一个属性标明配偶按图中的关系添加了这一个属性标明配偶 int sex; /为标明配偶为男女,所以引入本人的性别为标明配偶为男女,所以引入本人的性别;11.4 11.4 关联关系的实现关联关系的实现同许多面向对象编程语言一样,对关联的实现同许多面向对象编程语言一样,对关联的实现C+没有提供专用语法,编程没有提供专用语法,编程者可以使用指向类的指针、成员对象等语法实现分析设计阶段描述的关联结者可以使用指向类的指针、成员对象等语法实现分析设计阶段描述的关联结构,
58、与实现整体构,与实现整体-部分结构类似,实际上整体部分结构类似,实际上整体-部分结构在部分结构在UML中是以关联特例中是以关联特例的身份出现的。的身份出现的。 公公 司司名称:名称:char* m_pNamechar* m_pName老板:老板:CPerson boxCPerson box. . .boxbox结婚结婚丈夫丈夫妻子妻子人人姓名:姓名:char* m_pNamechar* m_pName年龄:年龄:int m_nAgeint m_nAge. . .工工作作单单位位由由.掌管掌管0.10.1老板老板为为.工作工作 * 雇佣雇佣笋溃捶摈减沃贿算封咖卧忘亲笼湍矢锌提沿勾蝶娃羡乳骚孩霍画词烈肖辐第11章类间关系的实现第11章类间关系的实现11.5 11.5 关于类层次的总结关于类层次的总结11.5.1 认知规律与类层次认知规律与类层次11.5.2 构造函数的一般形式构造函数的一般形式11.5.3 成员函数的特征成员函数的特征(略)(略)剐嘻症屹卤侦楔伏诌竭摧著敝魄垛扩享兰逸雄膏片瘴饯禾骗搜匈祈张绿灵第11章类间关系的实现第11章类间关系的实现