先上一段代码,简单易懂:
#include <iostream>
class Graph{
protected:
double x,y;
public:
Graph(double x,double y){this->x=x;this->y=y;}
~Graph(){std::cout<<"~Graph "<<std::endl;}//-----------------2
void showArea(){ std::cout<<"GraphArea "<< std::endl;}//-----3
};
class Rectangle:public Graph{
public:
Rectangle(double x,double y):Graph(x,y){}
~Rectangle(){std::cout<<"~Rectangle "<<std::endl;}
void showArea(){std::cout<<"RectangleArea "<< x*y << std::endl;}
};
int main(){
Graph *graph;
Rectangle rectangle(10,5);
graph=&rectangle;
graph->showArea();
delete graph;
return 0;
}
看一下程序的输出:
GraphArea
~Graph
和我们的预期不太一样,我们想要的肯定是执行Rectangle的showArea而不是父类的函数。想要实现该功能,需要把语句(3)变成virtual的虚函数。之后,结果:
RectangleArea 50
~Graph
现在函数调用正常了,不过函数的析构函数还是不正常,同理,懂了吧,再加一个virtual,在语句(2)上。之后,结果:
RectangleArea 50
~Rectangle
~Graph
目前总结一下,正如我们看到的,父类类型的指针指向子类以后,如果父类的函数不是虚函数,则通过指针来执行该函数还是执行父类的函数。如果是虚函数,则执行子类的重载函数。鉴于这一点容易骗倒自己,所以最好将虚函数设计成纯虚函数,即修改语句(3)为:
virtual void showArea() = 0;
这样就有点像接口了,只写一个原型,让子类实现,并且不会出现错误。
总结虚函数:
- C++默认是私有继承,而要使用多态,必须使用公有继承。
- 虚函数无论被继承n次,还是虚函数。
- 虚函数必须是成员函数,不能是构造函数(可以是析构函数),不能是友元函数,不能是static函数,不能是内联函数。
- 为了满足virtual的性质,C++需要为每个拥有virtual的class分配额外的空间(一个虚函数表指针,该指针指向一个虚函数表,来决定调用那个函数)。所以如果某个calss不是为了作为基类使用,或者不失为了具备多态性,不要添加virtual。
- 不要在构造函数和析构函数内调用virtual函数(构造和析构期间,这类调用不会下降到子类),这会造成不确定事件。