多态之中的一个(继承和虚函数)
多态是对于同一消息做出不同的反应。相应于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)虚函数
由此可见。类继承是一种挺笨的方法。要实现动态联编。能够通过虚函数实现。所谓的动态联编就是依据基类所引用的派生类对象,动态来获取该派生类对象的函数。
方法非常easy。在类继承的基础上,在基类的函数前面加上“virtual”关键字就可以,该函数在各个派生类中仍然是虚函数,仅仅只是省略了virtual关键字而已。
须要注意的是:
- virtual修饰的必须是基类的函数成员。而非友元函数。友元函数仅仅是表达与该类有友好共享数据成员的关系。并不属于该类。因此不能被继承。
- virtual不能修饰static函数成员。
static函数成员仅能訪问该类中的静态数据成员,但能够被该类全部对象共享。easy引起混乱。
- virtual仅仅能修饰基类的public或protect函数成员。私有函数成员在派生类中是不能被訪问的。
- 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++类、继承、多态、虚函数
- 多态公有继承(虚函数)
- 48、C++ Primer 4th 笔记,句柄类,继承,虚函数等的一个综合例子(未完)
- 设计引导--一个鸭子游戏引发的设计理念(多态,继承,抽象,接口,策略者模式)
- 黑马程序员一一封装,继承,多态的一个基本概述
- 菱形继承(虚函数)->菱形虚拟继承(虚函数)->多态系列问题
- 关于C++中私有继承后虚函数的访问权限与私有继承后多态的问题
- php部分--面向对象三大特性-封装(另加连续调用的一个例子)、继承(重写、重载的例子)、多态;
- 多态继承的一个小例子,mark一下。
- C++ 虚函数与继承的一个例子
- 用一个简单示例来说明C#中的继承、封装和多态
- C++笔记(继承,多态,虚函数,模板函数,异常捕获)
- c++(一) 类 对象 重载 继承 多态 构造函数 虚函数 覆盖 纯虚函数等
- 学习Java继承和多态时的一个小demo
- 用一个简单示例来说明C#中的继承、封装和多态
- 多态继承情况下,有一个基类指针指向派生类对象,如何用它调用基类的虚函数?
- 一个关于继承和多态的问题(提问篇)
- C++继承-重载-多态-虚函数
- 面向对象的三大特点:封装,继承,多态,外加一个重载概念。
- C++ 继承 多态 虚函数 抽象