虚函数是动态多态性的基础,其调用的方式是动态连篇(简单解释为只有在程序运行时才决定调用的是基类还是基类的派生类,系统会根据指针指向的对象来决定要调用的函数)
非虚函数与其相反,是静态连篇(简单解释为在编译时期就已经决定了要调用的函数)
class Shape { public: Shape(){}; void draw() { cout <<"画图形"<<endl; } }; class Rectangle:public Shape { public: Rectangle(){}; void draw() { cout <<"画方形"<<endl; } }; class Circle:public Shape { public: Circle(){}; void draw() { cout <<"画圆形"<<endl; } }; int main() { Shape *s; s=new Rectangle(); s->draw(); s=new Circle(); s->draw(); return 0; }
我主程序中的意思是要画方形和画圆形,可执行的结果都是画图形(叫你两儿子出来,怎么出来的都是你啊,扯淡么),这很明显不符合我们的要求和多态的要求。所以虚函数必须应运而生。
class Shape { public: Shape(){}; virtual void draw() //注意这里设置为虚函数 { cout <<"画图形"<<endl; } }; class Rectangle:public Shape { public: Rectangle(){}; void draw() { cout <<"画方形"<<endl; } }; class Circle:public Shape { public: Circle(){}; void draw() { cout <<"画圆形"<<endl; } }; int main() { Shape *s; s=new Rectangle(); s->draw(); s=new Circle(); s->draw(); return 0; }
这就满足我们了,一个是画方形,一个是画圆形。
发现:即使不在基类shape当中把draw设置为虚函数,我们只要直接在主函数当中直接声明子类的对象,调用draw,这种情况下是符合多态的思想的。
这就有疑问了,为什么还需要virtual关键字(虚函数)的存在呢?
上面的例子只是一个两个派生类,如果shape有成百上千个派生类,难道我们调用派生类的draw的时候都声明一个派生类的对象吗?很明显不可能,所以虚函数的情况下,我们只要声明一个shape的指针就好了。
上面枚举的Shape类里面有个draw函数,我们用得上吗?用在哪里?感觉糊里糊涂的,只知道用个虚函数就够了。其实我们根本用不到Shape里面的draw函数,也没有必要将它具体化。因此有必要在基类Shape当中,将draw函数的定义除去(用纯虚函数代替),而将它的定义留给派生类。
例如:
class Shape { public: Shape(){}; virtual void draw()=0; };
析构函数与虚函数的联系:
关于虚函数,在多态当中,一定要将基类的析构函数设置为虚函数并将其实现,只有这样,才能够达到按对象构造的逆序来析构对象;否则,析构的时候只会析构派生类那一部分,那么基类的那一部分就无法成功析构了。
析构函数的主要作用:负责类内的资源的释放,如果不手动调用那么系统会自动调用。
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> using namespace std; class Shape { public: Shape(){}; virtual void draw()=0; virtual ~Shape() { cout << "shape类析构" <<endl; } }; class Rectangle:public Shape { public: Rectangle(){}; void draw() { cout <<"画方形"<<endl; } ~Rectangle() { cout << "Rectangle类析构" <<endl; } }; class Circle:public Shape { public: Circle(){}; void draw() { cout <<"画圆形"<<endl; } ~Circle() { cout << "Circle类析构" <<endl; } }; int main() { Shape *s; //只进行了定义,尚未开辟空间 s=new Rectangle(); s->draw(); delete s; s=new Circle(); s->draw(); delete s; return 0; }