正文

第二课 C++经典知识回顾(三)2007-09-22 12:37:00

【评论】 【打印】 【字体: 】 本文链接:http://blog.pfan.cn/lym51/29594.html

分享到:

this指针: 如果成员函数Output被调用,一定是产生了一个对象实例,在这假设对象名称为a,并以a.Output形式调用的,Output的操作一定是针对对象a的。有时,成员函数需要访问它所依赖的那个对象,而不仅仅是这个对象中的其他成员。在类的成员函数中,可以用this关键字代表成员函数所依赖的那个对象的地址,所以,在成员函数中可以用this->成员的方式访问其它的成员,如CPoint(int x2,int y2)函数中用this->x2访问成员变量x2。在成员函数中,我们通常可以省略this->,直接访问类中的成员变量。在CPoint(int x2,int y2)函数中,由于函数参数变量x2,y2与成员CPoint中的成员变量x2,y2同名,要在该函数中访问成员变量x2,y2,可用this->x2,this->y2与参数变量x2,y2区分。小技巧:在以后的MFC编程中,如果在成员函数中想调用同类中的某个成员,可以使用VC++提供的自动列出成员函数功能,使用this->,VC++将列出该类中的所有成员,我们可以从列表中选择我们想调用的成员。自动列出成员函数功能,可以提高编写速度,减少拼写错误。特别是我们不能完全记住某个函数的完整拼写,但却能够从列表中辨别出该函数时,自动列出成员函数功能更是有用。事实上,在各种IDE编程环境中,我们通常都没有完全记住某些函数的完整拼写,只是记住其大概写法和功能,要调用该函数时都是从自动列出成员函数中选取的。这样能够大大节省我们的学习时间,我们没有花大量的时间去死记硬背许多函数,利用自动列出成员函数功能和帮助系统,却也能够在编程使顺利使用这些函数,等用的次数多了,也就在不知不觉中完全掌握了这些函数。   注意比较Output全局函数与Output成员函数的差别。对Output全局函数的调用,可以理解成“输出某个pt点的坐标”,是一种谓宾关系,是面向过程(或函数)Output的。对Output成员函数的调用,可以理解成“pt这个点对象执行输出动作”,是面向对象pt的。希望通过这样的比较,能够有助于读者理解c++中关于面向对象的概念。   四、类的继承与protected访问修饰符: 类是可以继承的,如果类B继承了类A,我们称A为基类(也叫父类),B为派生类(也叫子类)。派生类不但拥有自己新的成员变量和成员函数,还可以拥有基类的成员变量和成员函数。派生类的定义方法是: class 派生类名:访问权限 基类名称 {        ..... }; 要实现类B与类A的继承关系,我们在定义类B之前必须已定义了类A,并用如下的格式定义类B。 class B:public或private A {        .... }; 讲到类的继承后,我们再讲解另一种成员访问权限修饰符,protected。public,protected,private三种访问权限的比较: public定义的成员可以被在任何地方访问。 protected定义的成员只能在该类及其子类中访问。 private定义的成员只能在该类自身中访问。 派生类可以用public和private两种访问权限继承基类中的成员,如果在定义派生类时没有指定如何继承访问权限,则默认为private。如果派生类以private继承基类的访问权限,基类中的成员在派生类中都变成private类型的访问权限。如果派生类以public继承基类的访问权限,基类中的成员在派生类中仍以原来的访问权限在派生类中出现。注意:基类中的private成员不能被子类访问,所以private成员不能被子类所继承。 我们分析如下代码: class CAnimal {        public:               void eat();               void breathe(); } void CAnimal::eat() {        cout<<"eating"<<endl; } void CAnimal::breathe() {        cout<<"breathing"<<endl; } class CFish:public CAnimal {        public:               void swim();               void breathe();       } void CFish::swim() {        cout<<"swimming"<<endl; } void CFish::breathe() {        CAnimal::breathe();        cout<<"breathing"<<endl; } void main() {        CFish f;        f.eat();        f.swim();        f.breathe();        //下面的代码演示虚拟函数的多态性        CAnimal *pA;        pA=&f;        pA->breathe(); } 关于类的继承及类的访问特性可以参照如下表: 基类的访问特性 类的继承特性 子类的访问特性 Public Protected Private Public Public Protected No access1 Public Protected Private Protected Protected Protected No access1 Public Protected Private Private Private Private No access1                             由于CFish继承了CAnimal,所以在main函数中用CFish定义的对象f可以将CAnimal中定义的eat()成员函数当作自己的成员函数调用。f还调用了CFish中新定义的成员函数swim()。 对象f还调用了breathe()函数,大家发现在基类CAnimal和派生类CFish中都定义了breathe函数,在这种情况下调用的到底是哪个类中定义的函数呢?在这里,调用的是子类CFish中定义的函数。如果在子类与父类中都定义了同样的函数,当用子类定义的对象调用这个函数时,调用的是子类定义的函数,这就是函数的覆盖。函数的覆盖,我们可以用生活中的例子来比喻,儿子继承了父亲的许多方法,包括“结婚”这一行为,但父亲“结婚”用的是花轿,而儿子“结婚”用的却是汽车,儿子不能使用父亲“结婚”的方式。如果儿子结婚时,即要花轿,也要汽车,也就是在子类的成员函数定义中,要调用父类中定义的那个被覆盖的成员函数,其语法为,父类名::函数名(参数)。如CFish定义的breathe函数中使用的CAnimal::breathe()语句,就是调用CAnimal中的breathe函数。   在程序中main函数的结尾处的代码: CAnimal *pA; pA=&f; pA->breathe(); 上述代码定义了一个CAnimal类型的指针pA,pA指向CFish定义的对象f的地址,用指针pA去调用breathe函数,在这种情况下调用的到底是哪个类中定义的函数呢?简单的死记硬背只能管一时,不能管一世。我们还是从类型转换的原理上寻找答案。将鱼CFish对象的首地址直接赋值给动物CAnimal类型的指针变量,是不用强制类型转换的,编译器能够自动完成这种转换,子类对象指针能够隐式转换成父类指针。这个过程好比现实生活中将一条鱼当作一个动物是没有什么问题的,但要将一个动物当作鱼来对待是存在问题的。如果某一动物确实是一条鱼,我们就可以将这个动物强制类型转换成鱼。也就是说,要将父类类型的对象转换成子类对象,在程序中必须强制类型转换,编译才能通过,但要保证内存中的对象确实是那种被转换成的类型,程序在运行时才不会有问题。我们可以这样想象类型转换,用目标类型的内存布局,去套取要类型转换的对象的首地址开始的那一段内存块(大小为目标类型的大小),套取的内容即为转换后的结果。见图x,&f转换成pA后,转换完后的内容包含的breathe是CAnimal中定义的那个。   五、虚函数与多态性。 如果我们在CAnimal中定义的void breathe()函数前增加virtual关键字,即改为如下定义: class CAnimal {        public:               void eat();               virtual void breathe(); }; 则上面的代码 CAnimal *pA; pA=&f; pA->breathe();中breathe调用的是CFish中定义的那个,这就是编译器对虚函数调用的编译方式,这就是虚拟函数的多态性。如果在某个类的成员函数定义前加了virtual,这个函数就是虚函数,如果子类中有对该函数的覆盖定义,无论该覆盖定义是否有virtual关键字,都是虚拟函数。   五、类的书写规范与如何解决头文件重复引用问题。 操作符重载,匈牙利命名法。类继承中的构造函数调用顺序与指定父类中的构造函数。 关于完整的c++语法讲解,需要厚厚的一大本书,如果读者需要深入了解,请参看相关书籍。但只要掌握了本课中介绍的关于C++的知识,基本上就能够顺利学习以后的章节了,如有特殊需求,我们将在以后章节中用到时专门讲解。我们认为抱着问题学习的效果要比泛泛而学的效果好得多,并且学到一个新知识后马上便能看到其应用更能令人记忆深刻,举一反三。   实验步骤: 观察构造函数与析构函数的调用时机。

阅读(4547) | 评论(0)


版权声明:编程爱好者网站为此博客服务提供商,如本文牵涉到版权问题,编程爱好者网站不承担相关责任,如有版权问题请直接与本文作者联系解决。谢谢!

评论

暂无评论
您需要登录后才能评论,请 登录 或者 注册