非常感激我队友大爹给我的复习资料

多态按字面的意思就是多种形态。当类之间存在层次结构,并且类之间是通过继承关联时,就会用到多态。

C++ 多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <iostream>
using namespace std;

class Shape{
protected:
int width;
int height;
public:
Shape(int a=0, int b=0) : width(a), height(b) {}
int area(){
cout << "Parent class area: " << endl;
return 0;
}
};

class Rectangle : public Shape{
public:
Rectangle(int a=0, int b=0) : Shape(a, b) {}
int area(){
cout << "Rectangle class area: " << endl;
return (width*height);
}
};

class Triangle : public Shape{
public:
Triangle(int a=0, int b=0) : Shape(a, b) {}
int area(){
cout << "Triangle class area: " << endl;
return (width*height/2);
}
};

int main(void){
Shape * shape;
Rectangle rec(5, 6);
Triangle tri(4, 5);
shape = &rec;
shape->area();
shape = &tri;
shape->area();
return 0;
}

/*
该程序的输出结果为:
===================
Parent class area :
Parent class area :
===================
*/

静态链接

可以看到虽然指针指到了派生类上面,但它依然使用的依然是基类的成员函数。

导致错误输出的原因是,调用函数 area () 被编译器设置为基类中的版本,这就是所谓的静态多态,或静态链接 - 函数调用在程序执行前就准备好了。有时候这也被称为早绑定,因为 area () 函数在程序编译期间就已经设置好了。

但现在,让我们对程序稍作修改,在 Shape 类中,area () 的声明前放置关键字 virtual,如下所示:

1
2
3
4
virtual int area(){
cout << "Parent class area: " << endl;
return 0;
}

输出结果就变成了

1
2
Rectangle class area: 
Triangle class area:

此时,编译器看的是指针的内容,而不是它的类型。因此,由于 tri 和 rec 类的对象的地址存储在 *shape 中,所以会调用各自的 area () 函数。

正如您所看到的,每个子类都有一个函数 area () 的独立实现。这就是多态的一般使用方式。有了多态,您可以有多个不同的类,都带有同一个名称但具有不同实现的函数,函数的参数甚至可以是相同的。

虚函数

虚函数 是在基类中使用关键字 virtual 声明的函数。在派生类中重新定义基类中定义的虚函数时,会告诉编译器不要静态链接到该函数。

我们想要的是在程序中任意点可以根据所调用的对象类型来选择调用的函数,这种操作被称为动态链接,或后期绑定

纯虚函数

您可能想要在基类中定义虚函数,以便在派生类中重新定义该函数更好地适用于对象,但是您在基类中又不能对虚函数给出有意义的实现,这个时候就会用到纯虚函数。

我们可以把基类中的虚函数 area () 改写如下:

1
virtual int area() = 0;

= 0 告诉编译器,函数没有主体,上面的虚函数是纯虚函数

静态多态和动态多态

模板函数,函数重载,宏多态是静态多态。

虚函数的实现就是通常所说的动态多态,实现机制在于继承和虚函数。常用方法是创建一个父类的指针来指向不同的子类对象,调用虚函数时,会先找到子类虚表,通过子类虚表调用对应函数,实现运行时多态。

静态多态又叫编译时多态,动态多态又叫运行时多态。编译时的多态性为我们提供了运行速度快的特点,而运行时的多态性则带来了高度灵活和抽象的特点。

动态多态只需要一个多态函数,生成的可执行代码尺寸较小,静态多态必须针对不同的类型产生不同的模板实体,尺寸会大一些,但生成的代码会更快,因为无需通过指针进行间接操作。静态多态比动态多态更加类型安全,因为全部绑定都被检查于编译期。正如前面例子所示,你不可将一个错误的类型的对象插入到从一个模板实例化而来的容器之中。此外,正如你已经看到的那样,动态多态可以优雅地处理异质对象集合,而静态多态可以用来实现安全、高效的同质对象集合操作。