一、多态
作用:
多态(Polymorphism) 是面向对象编程(OOP)的核心特性之一,它允许同一接口表现出不同的行为。
多态可以分为:
- 编译时多态(静态多态): 通过函数重载和运算符重载实现(在编译阶段确定调用哪个函数)。
- 运行时多态(动态多态): 通过虚函数(
virtual
)和基类指针/引用实现(在运行时确定调用哪个函数)。
特点:
- 提高代码复用性,基类提供统一接口,子类可实现不同功能。
- 扩展性强,无需修改基类即可新增功能。
- 提高程序灵活性,如使用工厂模式(Factory Pattern) 动态创建对象。
二、静态多态 vs 动态多态
特性 | 静态多态(编译时) | 动态多态(运行时) |
---|---|---|
实现方式 | 函数重载、运算符重载 | 继承 + 虚函数 |
调用决策时间 | 编译阶段 | 运行阶段 |
性能 | 快(直接调用) | 稍慢(需动态绑定) |
适用场景 | 处理不同参数类型/个数 | 统一接口,灵活扩展 |
三、静态多态(编译时多态)
静态多态在编译阶段确定调用哪个函数,通常通过以下方式实现:
- 函数重载(Function Overloading)
- 运算符重载(Operator Overloading)
- 模板(Template)
(1) 静态多态 - 函数重载
作用:
同一函数名,不同参数列表,编译时决定调用哪个版本,提高了代码的可读性和灵活性。
示例:
#include <iostream>
using namespace std;
class Math {
public:
int add(int a, int b)
{
return a + b;
}
double add(double a, double b)
{
return a + b;
}
};
int main() {
Math obj;
cout << obj.add(10, 20) << endl; // 调用 int 版本
cout << obj.add(2.5, 3.5) << endl; // 调用 double 版本
system("pause");
return 0;
}
(2) 静态多态 - 运算符重载
作用:
运算符重载(Operator Overloading) 允许我们为自定义类型(如class
或 struct
)定义运算符的行为,从而实现静态多态。
示例:
#include <iostream>
using namespace std;
class Complex {
public:
double real, imag;
Complex(double r, double i) : real(r), imag(i) {}
// 重载 +
Complex operator+(const Complex& c)
{
return Complex(real + c.real, imag + c.imag);
}
void display()
{
cout << real << " + " << imag << "i" << endl;
}
};
int main() {
Complex c1(1, 2), c2(3, 4);
Complex c3 = c1 + c2; // 静态多态,编译时决定调用哪个函数
c3.display();
system("pause");
return 0;
}
注意:
- 重载
+
运算符,使c1 + c2
具备类似内置数据类型的加法运算行为。
(3) 静态多态 - 模板
作用:
模板(Template) 是实现静态多态的常见方式,适用于泛型编程(Generic Programming),允许编写与类型无关的代码,提高代码复用性和效率。
示例:
#include <iostream>
using namespace std;
// 泛型模板函数
template <typename T>
T add(T a, T b)
{
return a + b;
}
int main() {
cout << add(10, 20) << endl; // 调用 int 版本
cout << add(2.5, 3.5) << endl; // 调用 double 版本
system("pause");
return 0;
}
注意:
- add(10, 20) —— 在编译阶段生成 int 版本。
- add(2.5, 3.5) —— 在编译阶段生成 double 版本。
- 编译时决定调用哪个版本,无额外运行时开销(静态绑定)。
四、 动态多态(运行时多态)
动态多态通过虚函数(virtual
)和基类指针/引用实现。
- 编译时无法确定调用哪个函数,只有在运行时才决定。
- 需要基类指针或引用指向子类对象,并重写(override)虚函数。
(1) 基本虚函数
作用:
基本虚函数(Regular Virtual Function) 是在基类中声明为 virtual,但子类可以选择是否重写。
- 如果子类重写,则调用子类版本(运行时动态绑定)。
- 如果子类不重写,则调用基类版本。
示例:
#include <iostream>
using namespace std;
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;
}
};
int main() {
Animal* a; // 基类指针
Dog d;
Cat c;
a = &d;
a->speak(); // 运行时确定调用 Dog::speak()
a = &c;
a->speak(); // 运行时确定调用 Cat::speak()
system("pause");
return 0;
}
注意:
speak()
在Animal
中定义为virtual
,使a->speak()
在运行时决定调用哪个版本。- 没有
virtual
关键字时,调用的永远是Animal::speak()
(静态绑定)。 - 有
virtual
时,基类指针调用的是子类的speak()
(动态绑定)。
(2) 纯虚函数与抽象类
作用:
- 纯虚函数(
= 0
)表示该函数必须在子类中实现。 - 含有至少一个纯虚函数的类是抽象类,不能实例化。
示例:
#include <iostream>
using namespace std;
class Shape {
public:
virtual void draw() = 0; // 纯虚函数
};
class Circle : public Shape {
public:
void draw() override
{
cout << "画一个圆" << endl;
}
};
class Square : public Shape {
public:
void draw() override
{
cout << "画一个正方形" << endl;
}
};
int main() {
Shape* s; // 不能实例化抽象类
Circle c;
Square sq;
s = &c;
s->draw(); // 输出 "画一个圆"
s = &sq;
s->draw(); // 输出 "画一个正方形"
system("pause");
return 0;
}
注意:
Shape
是抽象类(包含纯虚函数draw()
)。- 子类
Circle
和Square
必须实现draw()
,否则也是抽象类。 - 基类指针
s
运行时决定调用Circle::draw()
或Square::draw()
。
(3) 多态 + 工厂模式
作用:
工厂模式(Factory Pattern)是创建型设计模式,用于封装对象创建逻辑,使得客户端代码无需关心具体类的实现,只需要调用工厂方法来获取对象。
- 封装对象创建逻辑,降低耦合。
- 支持扩展,新增子类时无需修改已有代码(符合开闭原则)。
- 结合多态,使基类指针指向不同的子类对象,实现动态绑定。
示例:
**#include <iostream>
using namespace std;
class Animal {
public:
virtual void speak() = 0; // 纯虚函数
};
class Dog : public Animal {
public:
void speak() override
{
cout << "狗:汪汪!" << endl;
}
};
class Cat : public Animal {
public:
void speak() override
{
cout << "猫:喵喵!" << endl;
}
};
// 工厂函数
Animal* createAnimal(int type)
{
if (type == 1)
return new Dog();
if (type == 2)
return new Cat();
return nullptr;
}
int main() {
Animal* a = createAnimal(1);
a->speak(); // 动态创建狗
delete a;
a = createAnimal(2);
a->speak(); // 动态创建猫
delete a;
system("pause");
return 0;
}**
注意:
createAnimal()
返回不同的子类对象,基类指针a
运行时决定调用哪个speak()
。