C++面向对象知识点十二:多态
多态含义
面向对象的三大特性:封装性是基础,继承性是关键,而多态性是补充。
多态是建立在虚函数的基础之上的。
静态多态:函数重载,运算符重载,函数模板和类模板
动态多态:也称为运行时多态,即在程序运行时刻才能决定的东西。
静态联编:联编出现在编译连接阶段,又称为早期联编,以实现静态多态。
动态联编:程序中若出现函数调用,但在编译阶段无法确定调用哪一个函数,而只有到了程序的运行阶段才能确定调用哪一个函数,又称为滞后联编、晚期联编。动态联编技术是通过虚函数实现的。
虚函数
虚函数 是在基类中使用关键字 virtual 声明的函数。在派生类中重新定义基类中定义的虚函数时,会告诉编译器不要静态链接到该函数。程序想要的是在程序中任意点可以根据所调用的对象类型来选择调用的函数,这种操作被称为动态链接,或后期绑定。
1)当基类中把成员函数定义为虚函数后,在其派生类中的虚函数必须与基类中的虚函数同名,且函数的参数个数、参数类型必须一致。
2)基类中的虚函数前的关键字virtual不能缺省,派生类中的虚函数前的关键字virtual是可以缺省的。
3)动态多态必须通过基类对象的引用或基类对象的指针调用虚函数才能实现。
4)虚函数不能是友元函数、静态成员函数、内联函数。
5)构造函数不能定义为虚函数,析构函数可以定义为虚函数,也建议将析构函数定义为虚函数,以便实现撤销对象时的多态性。
6)在一般成员函数中调用虚函数,遵循动态多态规则;但在构造函数中调用虚函数,不会遵循动态多态规则,即调用的是类自身的虚函数。
class Point
{
protected:
double Px, Py;
public:
Point(double a = 0, double b = 0): Px(a), Py(b) {}
virtual double area() // 虚函数1
{
return 0.0;
}
};
class Rectangle: public Point
{
protected:
double Rx, Ry;
public:
Rectangle(double a = 0, double b = 0,double c = 0, double d = 0): Point(a,b), Rx(c), Ry(d) {}
virtual double area() // 虚函数2
{
return (Px-Rx)*(Py-Ry);
}
};
class Circle: public Point
{
protected:
double r;
public:
Circle(double a = 0, double b = 0,double c = 0): Point(a,b), r(c) {}
virtual double area() // 虚函数3
{
return 3.14*r*r;
}
};
double calcArea(Point &p)
{
return (p.area());
}
int main()
{
Point p(1,2);
Rectangle r(0,0,2,2);
Circle c(0,0,1);
cout << calcArea(p) << '\t' << calcArea(r) << '\t' << calcArea(c) << '\n';
}
输出结果:
纯虚函数
virtual <数据类型> <函数名> (<参数>) = 0;
=0 表明该定义中没有函数体,这与将虚函数定义成空函数是有区别的:
virtual <数据类型> <函数名> (<参数>)
{ }
上述定义中的函数体为{ },表示在函数体内部做任何工作。
若想要在基类中定义虚函数,以便在派生类中重新定义该函数更好地适用于对象,但是在基类中又不能对虚函数给出有意义的实现,这个时候就会用到纯虚函数。
class Shape
{
protected:
int width, height;
public:
Shape( int a=0, int b=0)
{
width = a;
height = b;
}
// pure virtual function
virtual int area() = 0;
};
抽象类
含有纯虚函数的类称为抽象类。
抽象类只能做派生类的基类,不能定义抽象类的对象
若派生类实现了基类所有的纯虚函数,则派生类就不再是抽象类,若没有实现基类所有的纯虚函数,派生类依然是抽象类。
一般来说,纯虚函数没有函数体。但从语法的角度上纯虚函数可以给出函数体。