第11章类间关系的实现.ppt

上传人:本田雅阁 文档编号:2547207 上传时间:2019-04-06 格式:PPT 页数:34 大小:646.01KB
返回 下载 相关 举报
第11章类间关系的实现.ppt_第1页
第1页 / 共34页
第11章类间关系的实现.ppt_第2页
第2页 / 共34页
第11章类间关系的实现.ppt_第3页
第3页 / 共34页
亲,该文档总共34页,到这儿已超出免费预览范围,如果喜欢就下载吧!
资源描述

《第11章类间关系的实现.ppt》由会员分享,可在线阅读,更多相关《第11章类间关系的实现.ppt(34页珍藏版)》请在三一文库上搜索。

1、第11章 类间关系的实现,11.1 一般特殊关系的实现 11.2 多态性与虚函数 11.3 整体部分关系的实现 11.4 关联关系的实现 11.5 关于类层次的总结,学习目的: 掌握类间关系的C+实现; 了解多态性与虚函数的概念。,11.1 一般特殊关系的实现,11.1.1 类的继承与派生 11.1.2 赋值兼容规则 11.1.3 两义性与作用域分辨,C+提供了描述一般特殊关系的语法,在C+中称为类的派生或继承,通常分为单一继承和多重继承。在C+ 中常把一般特殊关系中的一般类称为父类,而把特殊类称为子类。,11.1.1 类的继承与派生,1. 单一继承,派生类说明格式: class : ;,派生

2、类初始化构造函数格式如下: ClassName:ClassName(ArgList0) : DerivedClassName(ArgList1) 例11.1 描述由矩形、正方形组成的平面图形系统,类体中的成员为子类所特有的数据成员和成员函数,虽然没有在子类中写明所继承的父类成员,但是父类成员在一定限制下属于子类。,#include “iostream.h“ class CRectangle /矩形类 public: CPoint m_cpLocation; /图形所在位置 int m_nWidth; /图形的宽度 int m_nHeight; /图形的高度 CRectangle(int nX,

3、 int nY, int nWidth, int nHeight); int GetArea(); /求面积 int GetPerimeter(); /求周长 CPoint,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_nHeig

4、ht+m_nHeight; CPoint,11.1.1 类的继承与派生,2. 基类成员访问控制,有两个因素同时控制着派生类对基类成员的访问权限,这两个因素就是基类类体中类成员的访问说明符,及派生类的派生方式。,基类的private成员将不被子类继承,且不能被子类成员访问。 private派生方式: 基类成员(private类除外)作为子类的private类 型成员。 public派生方式: 基类成员(private类除外)作为子类的相同类型 成员。 protected派生方式: 基类成员(private类除外)作为子类的protected 类型成员。,11.1.1 类的继承与派生,3 多重继承

5、,多重继承在C+中实现方式如下: class : , , ;,派生类构造函数应该调用所有基类的构造函数对基类数据成员进行初始化,格式如下: :(ArgList0) : (ArgList1), (ArgListn) ,#include “iostream.h“ #include “stdlib.h“ #include “string.h“ class CWnd; / 引用性说明 #define MAXTEXTBUFFER 0xffff class CPoint private: int m_x; int m_y;,4 继承与派生示例,public: CPoint(int x=0, int y=0

6、) m_x=x; m_y=y; int GetX() return 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; /CWnd的指针数组,放着本窗口的子窗口 CWnd* m_p

7、SiblingFirst; /指向本窗口的兄弟窗口 char* m_pEditTextBuffer; /指向窗口编辑区文本缓冲区 WNDSTRUCT(const WNDSTRUCT ,WNDSTRUCT() delete m_pczWndName; ; class CScreenObject : virtual private WNDSTRUCT public: void MoveToWindow(const CPoint,class CEditText : virtual private WNDSTRUCT public: CEditText(WNDSTRUCT,class CWindowT

8、ree : virtual private WNDSTRUCT public: CWindowTree(WNDSTRUCT,class CDerived : public CBase1, public CBase2 public: int b; CDerived() b=0x21; ; void main() CDerived obj; ,11.1.1 类的继承与派生,5. 派生类对象内存映像,例11.4 class CBase public: int b0; CBase() b0=0x01; ; class CBase1 : public CBase public: int b1; CBas

9、e1() b1=0x11; ; class CBase2 : public CBase public: int b2; CBase2() b2=0x12; ;,CBase2 对象,CBase1 对象,CDerived 对象,11.1.2 赋值兼容规则,1. 派生类对象可以赋值给父类对象,对于例11.4中的类,下列语句合法: CBase b; CBase1 b1; b=b1;,2. 派生类的对象可以用于基类引用的初始化,对于例11.4,下列语句合法: CBase1 b1; CBase,对于例11.4下列语句合法: CBase1 b1; CBase *pBaseObj=,3. 派生类对象的地址可以

10、赋值给指向基类的指针,CBase1对象,CBase对象,通过派生类CBasel对象的内存映像图可以看到这种赋值的物理意义。,11.1.3 两义性与作用域分辨,1. 作用域分辨,如类的多个父类中具有相同名数据成员或成员函数,在引用该成员时可使用作用域分辨符 : 来区分所引用的名字究竟属于哪个父类。,例11.5 class CBase1 public: void MyFunc() cout“This is CBase1s MyFunc“endl; ; class CBase2 public: void MyFunc() cout“This is CBase2s MyFunc“endl; ; cla

11、ss CDerived : public CBase1, public CBase2 public: void func() MyFunc(); /错误! 两义性! ; void main() CDerived obj; obj.func(); ,显然,派生类CDerived中函数func()对父类成员函数MyFunc()的引用是具有二义性的,编译器无法判断所要调用的是哪一个父类的成员函数,因此相应的语句出现语法错误。解决这种错误的办法是在程序中使用作用域分辨符直接指明所要引用的是哪个类的MyFunc(),因此将派生类CDerived的定义改写如下: class CDerived : publ

12、ic CBase1, public CBase2 public: void func() CBase1:MyFunc(); /调用CBase1类的成员函数MyFunc() CBase2:MyFunc(); /调用CBase2类的成员函数MyFunc() ;,11.1.3 两义性与作用域分辨,2. 支配规则,如果类Y是类X的一个基类,则X中的成员name支配基类中的同名成员。如果在程序中要访问被支配的名字,可以使用作用域分辨符。,class A public: int a(); ; class B : public virtual A public: int a(); ; class C : p

13、ublic virtual A ; class D : public B, public C public: D() a();/ 无二义性. B:a() 支配 A:a. ;,从同一个类直接继承两次以上,11.1.3 两义性与作用域分辨,3. 虚基类,多个父类由同一类派生,创建对象时内存中会有爷爷类多个实例(例11.4)。可采用两种方式消除二义性,其一使用 :,其二将爷爷类作为虚基类,使创建对象时内存中只有爷爷类的一个实例。,例11.7 派生类的两个父类具有一个共同的虚基类。 class CBase public: int b0; CBase() b0=0x01; ; class CBase1

14、: public virtual CBase public: int b1; CBase1() b1=0x11; ; class CBase2 : public virtual CBase public: int b2; CBase2() b2=0x12; ;,class CDerived :public CBase1, public CBase2 public: int b; CDerived() b=0x21; ; void main() CDerived obj; ,11.1.3 两义性与作用域分辨,3. 虚基类,11.2 多态性与虚函数,11.2.1 编译时刻的多态性 11.2.2 运

15、行时刻的多态性 11.2.3 虚函数 11.2.4 纯虚函数与抽象类,广义的多态性可以理解为一个名字具有多种语义。面向对象中的多态性是指不同类的对象对于同一消息的处理具有不同的实现,在C+中表现为同一形式的函数调用,可能调用不同的函数实现。 C+的多态性可分为两类,一类称为编译时刻多态性,另一类称为运行时刻多态性。与之相应的概念有静态联编(亦称静态绑定、静态集束、静态束定等)、动态联编(亦称动态绑定、动态集束、动态束定等)。,11.2.1 编译时刻的多态性,函数重载为一种常见的编译时刻多态性,编译时通过参数类型匹配,定位所调用函数的具体实现,然后用该实现代码调用代替源程序中的函数调用。,例11

16、.9 编译时刻多态性。 #include “iostream.h“ const float PI=float(3.14); class CPoint private: int m_x; int m_y; public: CPoint(int x=0, int y=0); void Area() cout“Here 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; public: CCircl

17、e(int x=0, int y=0, float r=0) : CPoint(x, y) m_nRadius=r; void SetRadius(float r) m_nRadius=r; void Area() cout“Here is a circles area: “ PI*m_nRadius*m_nRadiusendl; ; void main() CCircle c1; c1.Area(); ,11.2.2 运行时刻的多态性,运行时刻多态性的实现机制是动态联编,在程序运行时刻确定所要调用的是哪个具体函数实现,这种联编形式的程序运行效率低于静态联编,因为要花额外开销去推测所调用的是哪

18、一个函数。虽然动态联编的运行效率低于静态联编,但是动态联编为程序的具体实现带来了巨大的灵活性,使得对变化万千的问题空间对象的描述变得容易,使函数调用的风格比较接近人类的习惯。,例11.10 运行时刻的多态性。 #include “iostream.h“ const float PI=float(3.14); class CPoint private: int m_x; int m_y; public: CPoint(int x=0, int y=0); void Area() cout“Here is a points area: “0endl; ; CPoint:CPoint(int x,

19、int y) m_x=x; m_y=y; ,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() coutArea(); ,11.2.3 虚函数,类的一个成员函数被说明为虚函数表明它目前的具体实现仅仅是一种假设,只是一种适用于当前类的实现,在未来类的派生链条中有可能重新定义这个成员函数的实现 (o

20、verride)。虚函数的使用方法如下:,class vitual void MyFunction(); ; void :MyFunction() ,当某一个成员函数在基类中被定义为虚函数,那么只要同名函数出现在派生类中,如果在类型、参数等方面均保持相同,那么,即使在派生类中的相同函数前没有关键字virtual,它也被缺省地看作是一个虚函数,但为保证风格统一,建议在派生类的虚函数前仍然添加关键字virtual。,11.2.3 虚函数,不同语言环境实现虚函数的机制不同,下面通过类CDerived的派生介绍Visual C+中如何实现虚函数。 (程序见备注),1 虚函数的实现,11.2.3 虚函数

21、,2 虚函数的使用,虚函数的实现机制和调用方式与非虚函数不同,因此虚函数的使用具有特殊性。 虚函数的访问权限 派生类中虚函数的访问权限并不影响虚函数的动态联编,例如下面的程序实例例11.11,其中派生类CDerived中重新定义了虚函数Func4(),在程序的运行中由于虚函数的机制,在CBase:Func3()中调用Func3()时会调用CDerived:Func3(),而该函数的访问权限是私有的。 成员函数中调用虚函数 在成员函数中可以直接调用相应类中定义或重新定义的虚函数,分析这类函数的调用次序时要注意成员函数的调用一般是隐式调用,应该将之看做是通过this指针的显式调用,参见下例:,例1

22、1.11 在成员函数中调用虚函数。 #include “iostream.h“ class CBase public: void Func1() cout CBase:Func1= “; Func2(); void Func2() cout “; Func3(); virtual void Func3() cout “; Func4(); virtual void Func4() cout out“endl; ;,class CDerived : public CBase private: virtual void Func4() cout out “ Derived:Func1= “; CB

23、ase:Func2(); void Func2() cout Derived:Func2= “; Func3(); ; void main() CBase* pBase; CDerived dObj; pBase= ,11.2.3 虚函数,class CBase1 public: virtual void MyFunc() coutMyFunc(); pB2-MyFunc(); ,3. 多重继承与虚函数,程序中指向父类CBase1、CBase2的指针pB1、pB2分别被赋予了派生类CDerived对象的地址,由于MyFunc( )函数为虚函数,因此通过两个指针调用该函数的结果是都调用了派生类的

24、函数 CDerived:MyFunc( ) 。 采用这种方式能够使前期程序设计人员调用后期程序设计人员所实现的具体函数。,11.2.3 虚函数,class CBase public: virtual void MyFunc1() ; class CDerived1 : virtual public CBase public: virtual void MyFunc1() cout“CDerived1:MyFunc1“endl; ; class CDerived2 : virtual public CBase public: virtual void MyFunc2() cout“CDerive

25、d2:MyFunc2“endl; MyFunc1(); ;,3. 多重继承与虚函数,可以说明具有虚函数的虚基类,适当地使用这种方式能够提供作为父类的两个兄弟类实例之间的通信,是一种较好的通信方式 。,class CDerived : virtual public CDerived1, virtual public CDerived2 ; void main() CDerived dObj; dObj.MyFunc2(); ,11.2.3 虚函数,#include “iostream.h“ #include “string.h“ class CBase public: virtual CBase

26、() cout“CBase:CBase()“; ; class CDerived : public CBase public: virtual CDerived() cout“CDerived:CDerived()“endl; ; void main() CBase* pB=new CDerived; delete pB; ,4. 虚析构函数,析构函数可以被说明为虚函数,利用虚析构函数,删除对象时不必考虑对象的类型(父类或子类),虚函数机制将保证调用适当的析构函数。,11.2.4 纯虚函数与抽象类,软件系统的功能由类层次中的各类所实现,不同的类提供了相应层次的功能实现,通过类的用户接口可以调用

27、这些功能,人们通常所习惯的不是将功能在不同类层次的实现用不同的接口表示,而是将概念上相似的功能用一个统一的接口在最顶层表示,例如:一个系统可能提供了“打印”这一功能,但“打印”对其各组成部分的含义不同,可能包括打印文本文件、打印照片、打印图形等,这些打印功能的具体实现由各个类提供,但对整个系统来讲它们应该具有相同的接口,在调用时应能够根据具体情况调用其具体实现。虚函数可以帮助我们做到这一点,若干概念上相似的操作可以用一个虚函数描述,该虚函数在较高层次上表示一种功能的接口,而在不同类中对该虚函数的重新定义就是该项功能不同层次上的实现,虚函数调用机制可保证虚函数的某个恰当的实现被调用。也就是说,利

28、用虚函数,可以使系统中多个相似的功能具有统一的接口,改善了类的用户接口。方式如下:,class virtual (ArgList)=0; ;,11.3 整体部分关系的实现,C+对整体部分关系提供支持手段,对复合聚合,采用嵌入式对象的方式,即属性的类型为类,例如消息窗口类可以如下定义:,class CButton CButton() ;,class CIcon CIcon() ;,class CStudent CStudent(const char* pStudentName) ; class CGroup private: CStudent* m_pStudents; int m_nGroup

29、ID; ,public: void SetStudent(CStudent* pStudents) m_pStudent=pStudent; CGroup(int nID) m_nGroupID=nID; m_pStudents=new CStudnet5; /小组由5名学生组成 ; class CClass private: CStudent* m_pStudents; int m_nClassID; public: CClass(int nID) m_nClassID=nID; m_pStudents=new CStudnet35; /班级由35名学生组成 ,CStudent* GetSt

30、udent() return m_pStudents; void SetStudent(CStudent* pStudents) m_pStudent=pStudent; ; void main() CStudent theWhole2550= CStudent(“Marry“), , CStudent(“Tom“) ; CClass MyClass3(3); /成立班级,但具体由哪些学生组成尚未确定 MyClass3.SetStudent(theWhole+70); /本班学生由学校学生名册中第71位开始的35人组成 CGroup Group6(6); Goup6.SetStudent(My

31、Class3.GetStudent()+25); /第6学习小组由班级学生名册中第26名开始的5个人组成 ,class CCompany private: char* m_pName; /按图中要求 CPerson* box; /图中标出老板可有可无,故用指针表示 CPerson m_Employee20 /公司员工最多20 ; class CPerson private: char* m_pName; int m_nAge; CCompany* comp; /comp为一个CCompany的数组,因为可为多个公司工作 CPeron consort; /按图中的关系添加了这一个属性标明配偶 int sex; /为标明配偶为男女,所以引入本人的性别 ;,11.4 关联关系的实现,同许多面向对象编程语言一样,对关联的实现C+没有提供专用语法,编程者可以使用指向类的指针、成员对象等语法实现分析设计阶段描述的关联结构,与实现整体-部分结构类似,实际上整体-部分结构在UML中是以关联特例的身份出现的。,11.5 关于类层次的总结,11.5.1 认知规律与类层次 11.5.2 构造函数的一般形式 11.5.3 成员函数的特征 (略),

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

当前位置:首页 > 其他


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