一、多态的概念
多态(Polymorphism)一词源于希腊语,意为”多种形态”。在面向对象程序设计中,多态是对人类思维方式的一种直接模拟。
比如我们日常生活中说的”打球”的”打”,就是一个抽象的信息,具有多种含义。打篮球、打排球、打羽毛球,都使用了”打”来表示参与某种球类运动,但每种运动的具体规则、动作和器材却相差甚远。实际上,这就是对多种运动行为的抽象——用一个统一的词表达不同的具体操作。
在 C++ 中,多态性是指一段程序能够处理多种类型对象的能力。同一个操作作用于不同的对象,可以产生不同的执行结果。
二、多态的通俗理解
| 现实场景 | 统一操作 | 不同对象的不同表现 |
|---|---|---|
| 打球 | “打” | 篮球:运球投篮 / 排球:垫球扣球 / 羽毛球:挥拍扣杀 |
| 叫声 | “叫” | 狗:”汪汪” / 猫:”喵喵” / 鸭子:”嘎嘎” |
| 绘图 | “draw” | 圆形:画圆 / 矩形:画矩形 / 三角形:画三角形 |
| 移动 | “move” | 汽车:四个轮子转 / 船:螺旋桨推 / 飞机:喷气引擎 |
核心思想:对外提供统一的接口,内部执行不同的实现。
三、C++ 中多态的四种实现形式
C++ 中多态可以通过四种形式来实现:
| 多态形式 | 说明 | 典型例子 | 备注 |
|---|---|---|---|
| 强制多态 | 通过强制类型转换实现 | (double)5 / 2 | 即类型转换多态 |
| 重载多态 | 通过函数重载或运算符重载实现 | Add(int, int) 与 Add(float, float) | 编译时确定 |
| 类型参数化多态 | 通过函数模板或类模板实现 | template<typename T> T max(T a, T b) | 编译时确定 |
| 包含多态 | 通过基类指针或引用调用虚函数实现 | 基类指针指向派生类对象,调用虚函数 | 运行时确定 |
前三种属于编译时多态(静态多态),在编译阶段就确定了调用哪个函数。包含多态属于运行时多态(动态多态),在程序运行阶段才根据对象的实际类型确定调用哪个函数。
四、代码示例
以下代码演示了不同形式的多态:
#include <iostream>
#include <string>
using namespace std;
// ===== 1. 重载多态(函数重载)=====
int add(int a, int b)
{
cout << "调用 add(int, int): ";
return a + b;
}
double add(double a, double b)
{
cout << "调用 add(double, double): ";
return a + b;
}
// ===== 2. 包含多态(虚函数,运行时多态)=====
class Animal
{
public:
virtual void speak() // 虚函数
{
cout << "动物发出声音" << endl;
}
};
class Dog : public Animal
{
public:
void speak() override // 重写基类虚函数
{
cout << "狗叫:汪汪" << endl;
}
};
class Cat : public Animal
{
public:
void speak() override
{
cout << "猫叫:喵喵" << endl;
}
};
class Duck : public Animal
{
public:
void speak() override
{
cout << "鸭子叫:嘎嘎" << endl;
}
};
// ===== 3. 类型参数化多态(函数模板)=====
template <typename T>
T getMax(T a, T b)
{
return (a > b) ? a : b;
}
// ===== 4. 强制多态(类型转换)=====
void printValue(int x)
{
cout << "整数值:" << x << endl;
}
int main()
{
// ---- 重载多态 ----
cout << "===== 重载多态 =====" << endl;
cout << add(1, 2) << endl;
cout << add(1.5, 2.5) << endl;
cout << endl;
// ---- 包含多态 ----
cout << "===== 包含多态(运行时)=====" << endl;
Animal* ptr;
Dog dog;
Cat cat;
Duck duck;
ptr = &dog;
ptr->speak(); // 实际调用 Dog::speak()
ptr = &cat;
ptr->speak(); // 实际调用 Cat::speak()
ptr = &duck;
ptr->speak(); // 实际调用 Duck::speak()
cout << endl;
// ---- 类型参数化多态 ----
cout << "===== 类型参数化多态(模板)=====" << endl;
cout << "max(10, 20) = " << getMax(10, 20) << endl;
cout << "max(3.14, 2.71) = " << getMax(3.14, 2.71) << endl;
cout << "max('x', 'a') = " << getMax('x', 'a') << endl;
cout << endl;
// ---- 强制多态 ----
cout << "===== 强制多态 =====" << endl;
double d = 3.7;
printValue((int)d); // 强制类型转换,调用 printValue(int)
return 0;
}
分析:
- 重载多态:
add(1, 2)调用add(int, int),add(1.5, 2.5)调用add(double, double),编译器根据实参类型在编译时确定调用哪个版本。 - 包含多态:
Animal*指针可以指向Dog、Cat、Duck对象,调用speak()时,程序在运行时根据实际指向的对象类型决定调用哪个函数。同一行代码ptr->speak()产生了三种不同的输出。 - 类型参数化多态:
getMax是一个函数模板,可以处理int、double、char等多种类型,编译器根据传入的参数类型自动生成对应版本。 - 强制多态:通过
(int)d将double强制转换为int,使printValue能接受double类型的参数。
五、编译时多态 vs 运行时多态
| 特性 | 编译时多态(静态) | 运行时多态(动态) |
|---|---|---|
| 确定时机 | 编译阶段 | 运行阶段 |
| 实现方式 | 函数重载、模板、强制类型转换 | 虚函数 + 基类指针/引用 |
| 性能 | 无额外开销 | 有虚函数表查找开销 |
| 灵活性 | 编译后固定 | 运行时可动态切换 |
| 典型应用 | 运算符重载、swap 模板 | 插件架构、回调机制、框架设计 |