什么是多态?
相同对象收到不同消息或不同对象收到相同消息时产生的不同的动作。在C++的类中,有着静态多态和动态多态。静态多态指的是函数的重载,而动态多态指的是晚绑定。
静态多态
class Rect //矩形类
{
public:
int calcArea(int width);
int calcArea(int width,int height);
};
动态多态
比如计算面积 当给圆形计算面积时使用圆形面积的计算公式,给矩形计算面积时使用矩形面积的计算公式。也就是说有一个计算面积的形状基类,圆形和矩形类派生自形状类,圆形与矩形的类各有自己的计算面积的方法。可见动态多态是以封装和继承为基础的。在java中,是默认使用晚绑定的,所以重写方法就能实现效果。但在C++中,需要使用虚函数才能达到效果。
例子:
#include <iostream>
using namespace std;
class Shape{
public:
double calArea(){
cout<<"Shape Area"<<endl;
return 0;
}
};
class Rect : public Shape{
private:
double a, b;
public:
Rect(double aa, double bb){
a = aa;
b = bb;
}
double calArea(){
cout<<"Rect Area"<<endl;
return a * b;
}
};
class Circ : public Shape{
private:
double r;
public:
Circ(double rr){
r = rr;
}
double calArea(){
cout<<"Circ Area"<<endl;
return 3.14 * r * r;
}
};
int main(){
Shape * rect = new Rect(3, 4);
Shape * circ = new Circ(5);
cout << rect->calArea()<<endl;
cout <<circ->calArea()<<endl;
}
结果为:
Shape Area
0
Shape Area
0
可见,不用虚函数的情况下,C++中即使重写了方法,用的是基类指针的时候,调用的还是基类的方法。
现在修改Shape类为以下内容:
class Shape{
public:
virtual double calArea(){
cout<<"Shape Area"<<endl;
return 0;
}
};
修改的位置为在double calArea()方法前加上关键字virtual。声明该方法为虚函数。而Rect和Circ类中的该方法不需要在calArea()方法前加virtual关键字也是可以的,默认是已经添加了的。此时main函数结果为:
Rect Area
12
Circ Area
78.5
注意
上述代码虽然能运行但会报一个警告:警告内容为:
Class ‘Circ’ has virtual method ‘calArea’ but non-virtual destructor
Class ‘Rect’ has virtual method ‘calArea’ but non-virtual destructor
Class ‘Shape’ has virtual method ‘calArea’ but non-virtual destructor
即使用虚函数时,没有使用虚析构函数。为什么需要写虚析构函数呢?看下面内容。
Shape类修改为如下内容:
class Shape{
public:
virtual double calArea(){
cout<<"Shape Area"<<endl;
return 0;
}
~Shape(){
cout<<"~Shape()"<<endl;
}
};
Circ类修改为以下内容:
//引入Point类:
class Point{
private:
double x, y;
public :
Point(double xx, double yy){
x = xx;
y = yy;
}
double getX(){
return x;
}
double getY(){
return y;
}
};
class Circ : public Shape{
private:
Point* point;
double r;
public:
Circ(double x, double y, double rr){
point = new Point(x,y);
r = rr;
}
double calArea(){
cout<<"Circ Area"<<endl;
return 3.14 * r * r;
}
~Circ(){
cout<<"~Circ()"<<endl;
delete point;
}
};
main函数如下:
int main(){
Shape * circ = new Circ(3, 4, 5);
cout <<circ->calArea()<<endl;
delete circ;
}
在Shape的析构函数不声明为虚函数的情况下,delete circ时只会调用Shape的析构函数,而不会调用Circ的析构函数。输出内容如下:
Circ Area
78.5
~Shape()
下面修改Shape类:
class Shape{
public:
virtual double calArea(){
cout<<"Shape Area"<<endl;
return 0;
}
virtual ~Shape(){
cout<<"~Shape()"<<endl;
}
};
在析构函数前加关键字virtual.输出结果为:
Circ Area
78.5
~Circ()
~Shape()
即先调用子类的虚析构函数,再调用基类的虚析构函数。这种情况下,在Circ类中使用new所占用的point指针所指向的堆内存才能够被释放,不会造成内存泄露。
还有一点,当不适用基类指针,而使用基类对象的时候,不管使用还是不使用虚函数,调用的都是基类的方法,而不会调用重写的方法,就是说要实现上述多态,除了使用虚函数外,还要使用基类指针。