多态是面向对象编程中的一个核心概念,它允许您使用相同的接口来操作不同的基础形式(数据类型)。C++ 中的多态可以通过基类的指针或引用来实现对派生类对象的操作。多态性分为两种:编译时多态(静态多态)和运行时多态(动态多态)。
编译时多态主要是通过函数重载和运算符重载实现的,而运行时多态则是通过虚函数和抽象基类实现的。下面主要介绍运行时多态。
运行时多态
运行时多态是通过虚函数来实现的。当一个类中声明了虚函数,它就可以被任何继承该类的子类覆盖(Override),实现不同的行为。具体调用哪个函数,是在程序运行时动态决定的。
虚函数
一个虚函数是基类中使用关键字 virtual
声明的函数,它在派生类中可以被重新定义。基类中的虚函数使得派生类有机会提供一个特定于该类的实现。
纯虚函数和抽象类
如果一个虚函数在基类中没有实现(即没有函数体),那么它就是一个纯虚函数,形式为 virtual ReturnType FunctionName() = 0;
。含有纯虚函数的类称为抽象类,它不能被实例化,只能被用作基类。
例子
假设有一个基类 Shape
,其中包含一个纯虚函数 draw()
。Circle
和 Rectangle
是 Shape
的两个派生类,它们各自实现了 draw()
函数。
#include <iostream>
// 基类 Shape
class Shape {
public:
virtual void draw() const = 0; // 纯虚函数
virtual ~Shape() {} // 虚析构函数,确保派生类对象的正确清理
};
// Circle 类,继承自 Shape
class Circle : public Shape {
public:
void draw() const override { // 覆盖 draw 函数
std::cout << "Drawing Circle" << std::endl;
}
};
// Rectangle 类,继承自 Shape
class Rectangle : public Shape {
public:
void draw() const override { // 覆盖 draw 函数
std::cout << "Drawing Rectangle" << std::endl;
}
};
void drawShape(const Shape& shape) {
shape.draw(); // 在运行时调用正确的函数
}
int main() {
Circle c;
Rectangle r;
drawShape(c); // 输出 "Drawing Circle"
drawShape(r); // 输出 "Drawing Rectangle"
return 0;
}
在这个例子中,drawShape
函数接受 Shape
类型的引用作为参数,并调用 draw
方法。当你传递 Circle
对象给 drawShape
时,由于 Circle
类覆盖了 draw
方法,所以调用的是 Circle
类的 draw
方法。同样地,当你传递 Rectangle
对象时,调用的是 Rectangle
类的 draw
方法。这就是多态性的体现。
图文解释
想象一个实际的场景,我们有不同形状的模具,每个模具可以打印出特定形状的图案。基类 Shape
就像是模具的蓝图,而派生类 Circle
和 Rectangle
分别是圆形和矩形模具的具体实现。
[ Shape ]
/ \
/ \
[Circle] [Rectangle]
每个模具(派生类)都知道如何处理它自己的 draw
操作。当我们在程序中要求一个形状(Shape
)进行绘制时,程序会查看具体的形状是什么(即对象的实际类型),并调用相应的绘制方法。
在代码层面,多态允许我们以统一的方式引用不同类型的对象,并在运行时根据对象的实际类型调用相应的方法,而不需要在编译时就确定具体的调用。这种动态绑定的能力是面向对象编程的强大特性之一。