多态之一(继承和虚函数)
来源:互联网 发布:2017东南大学软件学院 编辑:程序博客网 时间:2024/04/30 10:08
多态是对于同一消息做出不同的反应。对应于C++语法,是同一函数在同样的输入下产生不同的反应。这里的同一函数表示“函数类型、函数名、参数”。多态有三种表现形式:类继承、虚函数和重载。
(1)类继承
类继承是最为基础的一种静态联编的多态方式。联编是通过编译和连接库文件而形成可执行文件的动作。继承是静态的,是因为它不能根据基类所引用的派生类对象而获取该对象对应的函数。
基类不能通过派生类的构造函数直接初始化,虽然派生类反过来可以直接使用基类构造函数进行初始化。但是,基类通过指针或者引用内存的方式引用派生类对象进行初始化。尽管如此,继承的静态性直接导致了基类对象不能引用派生类对象的函数,而是根据“就近优先”原则首先使用基类自身的函数。代码示例如下:
#include <stdio.h> //Standard namespace with many headfiles replaced to its C'using namespace std; //#define is a macro definition to replace PI with number 3.14#define PI 3.14 class Cshape{ //for each self-defined class, "CLASS" is essential public: float area() { areas =0.0; return areas; } private: float areas; //it doesn't matter whether areas declared or just return directly in area()};class Ctriangle:public Cshape{ public: Ctriangle(float high=0.0, float bottom=0.0) //Constructor { this->high = high; this->bottom = bottom; } float area() { areas = (float) (high * bottom / 2); return areas; } private: float high, bottom, areas;};class Ccircle: public Cshape{ public: Ccircle(float radius=0.0) //Constructor { this->radius = radius; } float area() { return (float) (radius * radius * PI / 2); } private: float radius;};int main(){ Ctriangle tri(6,5); printf("The area of triangle is %f.\n", tri.area()); Ccircle cir(4); printf("The area of circle is %f.\n", cir.area()); Cshape *shape1 = &tri; // a base pointer to the tri's memory printf("The area of shape1 is %f.\n", shape1->area()); Cshape &shape2 = cir; // a base var's memory value is cir's value printf("The area of shape2 is %f.\n", shape2.area()); return 0; }
根据上述介绍,shape1和shape2虽分别引用了tri和cir这两个派生类对象,但是其area()函数应该按照“就近优先”原则调用Cshape类的函数,而不是Ctriangle或者Ccircle类的函数。运行结果:
(2)虚函数
由此可见,类继承是一种挺笨的方法。要实现动态联编,可以通过虚函数实现。所谓的动态联编就是根据基类所引用的派生类对象,动态来获取该派生类对象的函数。方法很简单,在类继承的基础上,在基类的函数前面加上“virtual”关键字即可,该函数在各个派生类中仍然是虚函数,只不过省略了virtual关键字而已。需要注意的是:
- virtual修饰的必须是基类的函数成员,而非友元函数。友元函数只是表达与该类有友好共享数据成员的关系,并不属于该类,因此不能被继承。
- virtual不能修饰static函数成员。static函数成员仅能访问该类中的静态数据成员,但能够被该类所有对象共享,容易引起混乱。
- virtual只能修饰基类的public或protect函数成员。私有函数成员在派生类中是不能被访问的。
- virtual可以修饰基类的析构函数,却不能修饰基类的构造函数。在构造函数运行成功之前,任何对象都是不存在的。 -
在Cshape的area()函数前加一个virtual关键字,代码如下:
#include <stdio.h> //Standard namespace with many headfiles replaced to its C'using namespace std; //#define is a macro definition to replace PI with number 3.14#define PI 3.14 class Cshape{ //for each self-defined class, "CLASS" is essential public: virtual float area() //without virtual, the results are diff in shape1 & shape2 { areas =0.0; return areas; } private: float areas; //it doesn't matter whether areas declared or just return directly in area()};class Ctriangle:public Cshape{ public: Ctriangle(float high=0.0, float bottom=0.0) //Constructor { this->high = high; this->bottom = bottom; } float area() { areas = (float) (high * bottom / 2); return areas; } private: float high, bottom, areas;};class Ccircle: public Cshape{ public: Ccircle(float radius=0.0) //Constructor { this->radius = radius; } float area() { return (float) (radius * radius * PI / 2); } private: float radius;};int main(){ Ctriangle tri(6,5); printf("The area of triangle is %f.\n", tri.area()); Ccircle cir(4); printf("The area of circle is %f.\n", cir.area()); Cshape *shape1 = &tri; // a base pointer to the tri's memory printf("The area of shape1 is %f.\n", shape1->area()); Cshape &shape2 = cir; // a base var's memory value is cir's value printf("The area of shape2 is %f.\n", shape2.area()); return 0; }
根据上述介绍,shape1和shape2分别引用了tri和cir对象,其area函数应该是派生类对象的函数,而非基类函数。运行结果:
虚函数为什么能够实现动态选择派生类对象的函数呢?这是因为基类在定义虚函数时,在内部创建了一张VTable表(虚表)和一个指向表中函数的vptr指针。定义派生类时,派生类对应的函数版本也会被纳入基类的VTable表中,这样所有的area()函数都在同一张表中。在明确了基类引用哪个派生类对象后,vptr指针会移动到VTable表中该派生类所对应的area()版本处,这样就实现动态调用了。
(3)纯虚函数
有时候,在基类中完全不知道函数该怎样定义,只能依据各派生类视情况而定义,此时就需要用到纯虚函数。纯虚函数在基类中相当于一个空函数,它的存在在于提醒各派生类记得在各自类中定义该函数。在基类的定义如下:
class Cshape{
public:
virtual float area()=0;
};
运行结果跟虚函数的结果是一样的。包含至少一个纯虚函数的类叫做抽象类,是不能直接声明一个对象的,只有在定义了派生类对象后才能通过引用该对象来声明。
下一篇讲解重载。
- 多态之一(继承和虚函数)
- C++虚函数和多态继承
- 重读C++之一:封装、继承和多态
- 继承和虚函数
- C++里的继承和多态(下)——单继承、多继承、菱形继承(含虚拟函数的继承)
- [C/C++]继承、多态和虚函数整理
- 多继承,RTTI和虚函数
- 虚函数,多态继承
- 虚函数继承和虚继承
- 虚继承和虚函数继承
- 虚继承和虚函数继承
- 虚继承和虚函数继承
- 继承----有关虚函数和虚拟继承
- 菱形继承(虚函数)->菱形虚拟继承(虚函数)->多态系列问题
- c++继承(单继承,多继承,菱形继承和虚继承)详解
- C++里的继承和多态(中)——分析单继承、多继承、菱形继承(不含虚函数)
- 虚继承和虚函数
- 虚函数和虚继承
- Git五分钟教程
- UVa12096 set map vector stack的综合运用
- 京东云、新浪微博等专家畅谈Docker未来格局:开放与竞争(下)
- JAVA基础----接口
- PAT乙级 1011. A+B和C
- 多态之一(继承和虚函数)
- Android Studio 简介及导入 jar 包和第三方开源库方
- 浅谈JVM内存区域划分
- 判断一个单链表是否有环及环的链接点
- POJ 2773 Happy 2006
- 面试问题之:Dalvik VM和JVM的区别(1)
- Redis_字典
- Composer 因SSL出错而无法更新的解决办法
- 了解Jvm虚拟机1