Ch2C++的重要性质

上传人:沈*** 文档编号:244557825 上传时间:2024-10-05 格式:PPT 页数:21 大小:391KB
返回 下载 相关 举报
Ch2C++的重要性质_第1页
第1页 / 共21页
Ch2C++的重要性质_第2页
第2页 / 共21页
Ch2C++的重要性质_第3页
第3页 / 共21页
点击查看更多>>
资源描述
单击此处编辑母版标题样式,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,*,Ch2:C+,的重要性质,内容提要,类及其成员,-,谈封装(,encapsulation,),基类与派生类,-,谈继承,(Inheritance),This,指针,虚函数与多态,(Polymorphism),类与对象大解剖,Object slicing,与虚函数,静态成员,(,变量与函数,),C+,程序的生与死,:,兼谈构造函数与释构函数,四种不同的对象生存方式,运行时类型识别,(RTTI),动态创建,(Dynamic Creation),异常处理,(Exception Handling),Template,类及其成员,-,谈封装(,encapsulation,),世界是一个由各式各样的物体(对象,即,object,)所组成。任何实际的物体你都可以说它是对象,.,为了描述对象,我们应该先把对象的属性描述出来,.,给”对象的属性”一个比较学术的名词,就是“类”,(class),对象的属性有两大成员,一是属性,(property),一是方法,(method).,比较程序化的说法是成员变量,(member variable),和成员函数,(member function).,一般而言,成员变量通常由成员函数处理,.,成员变量可以只在类内部处理,也可以开放给外界处理,.,以数据封装的目的而言,自然是前者较为稳妥,但有时候也不得不开放,.,为此,C+,提供了,private,public,和,protected,三种修饰词,.,一般而言,成员变量尽量,private,成员函数尽量,public,把数据声明为,private,不允许外界随意存取,只能通过特定的接口来操作,这就是面向对象的,封装性,(encapsulation),Example,如果以,Csquare,代表”正方形”这种类,正方形有,color(,成员变量,),正方形可以,display(,成员函数,).,基类与派生类,-,谈继承,(Inheritance),几个事实和问题,所有类都由,CShap,派生下来,所以它们都自然而然地继承了,CShape,的成员,包括变量和函数。也就是说,所有的形状都“暗自”,(,无法从各派生类的声明中看出来,),具备了,m_color,变量和,setcolor,函数。,两个矩形对象,rect1,和,rect2,各有自己的,m_color,,但关于,setcolor,函数却是共享相同的,CRect:setcolor(,其实更应该是,CShape:setcolor),。问题是:同一个函数如何处理不同的数据?为什么,rect1.setcolor,和,rect2.setcolor,明明都是调用,CRect:setcolor(,即,CShape:setcolor),,却能够有条不紊地分别处理,rect1.m_color,和,rect2.m_color,?,(this,指针,),既然所有类都有,display,操作,那么把它提升到老祖宗,CShape,去,然后再继承之,好吗?不好,因为,display,函数应该因不同的形状而操作不同。,如果,display,不能提升到基类去,我们就不能够以一个,for,循环或,while,循环干净漂亮地完成下列操作(此种操作模式在面向对象程序方法中重要无比),(,虚函数与多态,),this,指针,CRect:setcolor,如何处理不同对象中的,m_color?,答案是:成员函数有一个隐藏参数,名为,this,指针。当你调用:,rect1.setcolor(2);,rect2.setcolor(3);,编译器实际作出来的代码是:,CRect:setcolor(2,(CRect*),CRect:setcolor(3,(CRect*),不过,由于,CRect,本身没有声明,setcolor,,它是从,CShape,继承过来的,所以编译器实际产生的代码是:,CShape:setcolor(2,(CRect*),CShape:setcolor(3,(CRect*),多出来的参数,就是所谓的,this,指针。,虚函数与多态,我们希望能够准备一个,display,函数,给使用者调用,不管他根据我的这一大堆形状类派生其他什么奇形怪状的类,只要他想,display,像下面这样做就行,:,为了支持这种能力,C+,提供了所谓的,虚函数,虚函数与多态,-,引例,程序实作代码见,P55,指针的类型转换,(1),假设我们有两个对象:,Cwage aWager;,Csales aSales(“,李四”,),;,销售员是时新职员之一,因此这样做是合理的:,aWage=aSales;/,合理,销售员必定是时新职员,这样就不合理:,aSales=aWager;/,错误,时新职员未必是销售员,如果你一定要转换,则必须使用指针,并且明显地做类型转换(,cast,)操作:,CWage*pWager;,CSales*pSales;,CSales aSales(“,李四”,),;,pWager=/,把一个“基类指针”指向派生类之对象,合理且自然,pSales=(CSales*)pWager;/,强迫转型。语法上可以,但不符合现实生活,指针的类型转换,(1),为了某种便利,我们也会想以一个“通用的指针”表示所有可能的职员类型。无论如何,销售员、时新职员、经理,都是职员,所以下面的操作合情合理:,CEmployee*pEmployee;,CWage aWager(“,王五”,),;,CSale aSales(“,李四“);,CManager aManager(“,张三”,),;,pEmployee=/,合理,因为时新职员必是职员,pEmployee=/,合理,因为销售员必是职员,pEmployee=/,合理,因为经理必是职员,也就是说,你可以把一个“职员指针”指向任何一种职员。这带来的好处是程序员设计的巨大弹性。譬如说你设计一个链表,各个元素都是职员(哪一种职员都可以),你的,add,函数可能因此希望有一个”职员指针”作为参数:,Add(Cemployee*pEmp);/pEmp,可以指向任何一种职员,指针的引用,如果你以一个基类之指针指向派生类之对象,那么经由此指针,你就只能够调用基类(而不是派生类)所定义的函数。因此:,Csales aSales(“,李四”,);,CSales*pSales;,CWage*pWage;,pSales=,pWage=/,以基类之指针指向派生类之对象,pWage-setSales(800.0);/,错误,(,编译器会检测出来,),/,因为,Cwage,并没有定义,setSales,函数,pSales-setSales(800.0);/,正确,调用,CSales:setSales,函数,虽然,pSales,和,pWage,指向同一个对象,但却因指针的原始类型而使两者之间有了差别,.,延续此例,我们看另一种情况,:,pWage-computePay();/,pSales-computePay();/,虽然,pSales,和,pWage,实际都指向,CSales,对象,但是两者调用的,computePay,却不同,到底调用哪个函数,必须视指针的原始类型而定,与指针实际所指之对象无关,.,基类指针指向派生类对象,如果你以一个“基类之指针”指向“派生类之对象”,那么经由该指针你只能够调用基类所定义的函数。,派生类指针指向基类对象,如果以一个“派生类之指针”指向一个“基类之对象”,你必须先做明显的转型操作(,explicit cast,)。这种做法很危险,不符合真实生活经验,在程序设计上也会给程序员带来困惑。,指针原始类型决定调用的成员函数,如果基类和派生类都定义了“相同名称之成员函数”,那么通过对象指针调用成员函数时,到底调用到哪一个函数,必须视该指针的原始类型而定,而不是视指针实际所指的对象的类型而定。,一般化,?,函数的调用是依赖于指针的原始类型而不管它实际上指向何方,(,何种对象,).,因此,如果上述,while,循环中调用,pEmp-computePay,那么,while,循环索执行的将总是相同的运算,也就是,CEmployee:computePay(),虚函数与一般化,虚函数正是为了对“如果你以一个基类之指针指向一个派生类之对象,那么通过该指针你就只能够调用基类所定义的成员函数”这条规则反其道而行的设计。,如果我们把职员一例中所有四个类的,computePay(),函数前面都加上,virtual,保留字,使它们成为虚函数,那么:,Shape,例子,执行结果:如果把,virtural,拿掉,结果将是:,多态,(polymorphism),所谓多态是以相同的指令却能调用不同的函数。多态的实现依赖的是后期绑定技术:即编译器无法在编译时期判定,pEmp-computePay(),到底是调用哪一个函数,必须在执行期才能判断。与之相对应的是前期绑定或静态绑定,(non-virtual,函数,),,在编译时期就转换为一个固定地址调用。,Polymorphism,的目的,就是要让处理“基类之对象”的程序代码,能够完全无碍地继续适当处理“派生类之对象”,纯虚函数,对于,CShape,,它是抽象的,所以它根本不该有,display,这个操作。但为了在各具体派生类中绘图,我们又不得不在基类,CShape,中加上,display,虚函数。你可以定义它什么也不做(空函数):,或只是给各消息:,这两种做法都不高明,因为这个函数根本就不应该被调用(,CShape,是抽象的),我们根本就不应该定义它。不定义但又必须保留一块空间给它,于是,C+,提供了所谓的纯虚函数:,纯虚函数不需定义其实际操作,它的存在只是为了在派生类中被重新定义,只是为了提供一个多态接口。只要是拥有纯虚函数的类,就是一种抽象类,它是不能被实例化的,你不能够根据它产生一个对象。,结论,如果你期望派生类重新定义一个成员函数,那么你应该在基类中把此函数设为,virtual,。,以单一指令调用不同函数,这种性质称为,polymorphism,。,虚函数是,C+,语言的,polymorphism,性质以及动态绑定的关键。,既然抽象类中的虚函数不打算被调用,我们就不应该定义它,应该把它设为纯虚函数,(,在虚函数声明后面加上“,=0”,即可,),。,拥有纯虚函数者为抽象类(,abstract class,),以别于所谓的具体类(,concrete class,)。,抽象类不能产生出对象实例,但是我们可以拥有指向抽象类的指针,以便于操作抽象类的各个派生类。,虚函数派生下去仍为虚函数,而且可以省略,virtual,关键字。,
展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 管理文书 > 施工组织


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

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


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