您好,欢迎访问三七文档
当前位置:首页 > 商业/管理/HR > 公司方案 > Ch2C++的重要性质
Ch2:C++的重要性质内容提要类及其成员-谈封装(encapsulation)基类与派生类-谈继承(Inheritance)This指针虚函数与多态(Polymorphism)类与对象大解剖Objectslicing与虚函数静态成员(变量与函数)C++程序的生与死:兼谈构造函数与释构函数四种不同的对象生存方式运行时类型识别(RTTI)动态创建(DynamicCreation)异常处理(ExceptionHandling)Template类及其成员-谈封装(encapsulation)世界是一个由各式各样的物体(对象,即object)所组成。任何实际的物体你都可以说它是对象.为了描述对象,我们应该先把对象的属性描述出来.给”对象的属性”一个比较学术的名词,就是“类”(class)对象的属性有两大成员,一是属性(property),一是方法(method).比较程序化的说法是成员变量(membervariable)和成员函数(memberfunction).一般而言,成员变量通常由成员函数处理.成员变量可以只在类内部处理,也可以开放给外界处理.以数据封装的目的而言,自然是前者较为稳妥,但有时候也不得不开放.为此,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*)&rect1);CRect::setcolor(3,(CRect*)&rect2);不过,由于CRect本身没有声明setcolor,它是从CShape继承过来的,所以编译器实际产生的代码是:CShape::setcolor(2,(CRect*)&rect1);CShape::setcolor(3,(CRect*)&rect2);多出来的参数,就是所谓的this指针。虚函数与多态我们希望能够准备一个display函数,给使用者调用,不管他根据我的这一大堆形状类派生其他什么奇形怪状的类,只要他想display,像下面这样做就行:为了支持这种能力,C++提供了所谓的虚函数虚函数与多态-引例程序实作代码见P55指针的类型转换(1)假设我们有两个对象:CwageaWager;CsalesaSales(“李四”);销售员是时新职员之一,因此这样做是合理的:aWage=aSales;//合理,销售员必定是时新职员这样就不合理:aSales=aWager;//错误,时新职员未必是销售员如果你一定要转换,则必须使用指针,并且明显地做类型转换(cast)操作:CWage*pWager;CSales*pSales;CSalesaSales(“李四”);pWager=&aSales;//把一个“基类指针”指向派生类之对象,合理且自然pSales=(CSales*)pWager;//强迫转型。语法上可以,但不符合现实生活指针的类型转换(1)为了某种便利,我们也会想以一个“通用的指针”表示所有可能的职员类型。无论如何,销售员、时新职员、经理,都是职员,所以下面的操作合情合理:CEmployee*pEmployee;CWageaWager(“王五”);CSaleaSales(“李四“);CManageraManager(“张三”);pEmployee=&aWager;//合理,因为时新职员必是职员pEmployee=&aSales;//合理,因为销售员必是职员pEmployee=&aManager;//合理,因为经理必是职员也就是说,你可以把一个“职员指针”指向任何一种职员。这带来的好处是程序员设计的巨大弹性。譬如说你设计一个链表,各个元素都是职员(哪一种职员都可以),你的add函数可能因此希望有一个”职员指针”作为参数:Add(Cemployee*pEmp);//pEmp可以指向任何一种职员指针的引用如果你以一个基类之指针指向派生类之对象,那么经由此指针,你就只能够调用基类(而不是派生类)所定义的函数。因此:CsalesaSales(“李四”);CSales*pSales;CWage*pWage;pSales=&aSales;pWage=&aSales;//以基类之指针指向派生类之对象pWage-setSales(800.0);//错误(编译器会检测出来)//因为Cwage并没有定义setSales函数pSales-setSales(800.0);//正确,调用CSales::setSales函数虽然pSales和pWage指向同一个对象,但却因指针的原始类型而使两者之间有了差别.延续此例,我们看另一种情况:pWage-computePay();//pSales-computePay();//虽然pSales和pWage实际都指向CSales对象,但是两者调用的computePay却不同,到底调用哪个函数,必须视指针的原始类型而定,与指针实际所指之对象无关.基类指针指向派生类对象如果你以一个“基类之指针”指向“派生类之对象”,那么经由该指针你只能够调用基类所定义的函数。派生类指针指向基类对象如果以一个“派生类之指针”指向一个“基类之对象”,你必须先做明显的转型操作(explicitcast)。这种做法很危险,不符合真实生活经验,在程序设计上也会给程序员带来困惑。指针原始类型决定调用的成员函数如果基类和派生类都定义了“相同名称之成员函数”,那么通过对象指针调用成员函数时,到底调用到哪一个函数,必须视该指针的原始类型而定,而不是视指针实际所指的对象的类型而定。一般化?函数的调用是依赖于指针的原始类型而不管它实际上指向何方(何种对象).因此,如果上述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”即可)。拥有纯虚函数者为抽象类(abstractclass),以别于所谓的具体类(concreteclass)。抽象类不能产生出对象实例,但是我们可以拥有指向抽象类的指针,以便于操作抽象类的各个派生类。虚函数派生下去仍为虚函数,而且可以省略virtual关键字。
本文标题:Ch2C++的重要性质
链接地址:https://www.777doc.com/doc-3278265 .html