重写 vs 重载 vs 多态
2025/8/8...大约 3 分钟C++
重写、重载与多态的区别
1. 基本概念对比
特性 | 重写(Override) | 重载(Overload) | 多态(Polymorphism) |
---|---|---|---|
定义 | 子类重新定义父类的虚函数 | 同一作用域内同名函数不同参数 | 同一接口表现出不同行为 |
作用域 | 必须在不同类(继承关系中) | 同一类或命名空间内 | 跨类层次结构 |
函数签名 | 必须完全相同 | 必须不同(参数类型/数量不同) | 通过虚函数机制实现 |
绑定时机 | 运行时动态绑定(晚绑定) | 编译时静态绑定(早绑定) | 运行时通过虚表实现动态绑定 |
2. 技术细节深入解析
2.1 重写(Override)
核心要求:
- 必须发生在继承体系中
- 父类函数必须有
virtual
关键字(或纯虚函数) - 函数名、参数列表和返回类型必须完全一致
- C++11可使用
override
关键字显式声明
典型示例:
class Base {
public:
virtual void show() { cout << "Base show"; }
};
class Derived : public Base {
public:
void show() override { cout << "Derived show"; } // 重写
};
2.2 重载(Overload)
核心特征:
- 同一作用域内的同名函数
- 参数列表必须不同(类型/数量/顺序)
- 返回类型可相同也可不同
- 与const修饰符也可构成重载
典型示例:
class Calculator {
public:
int add(int a, int b) { return a+b; }
double add(double a, double b) { return a+b; } // 重载
int add(int a, int b, int c) { return a+b+c; } // 重载
};
2.3 多态(Polymorphism)
实现机制:
- 基于虚函数表(vtable)实现
- 通过基类指针/引用调用派生类方法
- 包含编译时多态(重载)和运行时多态(重写)
典型示例:
Base* obj = new Derived();
obj->show(); // 输出"Derived show",体现多态性
3. 关键区别总结
语法层面:
- 重写要求父子类关系,重载只需同一作用域
- 重写函数签名必须一致,重载必须不同
- 多态是重写表现出的行为特性
实现机制:
- 重载是编译时决议(静态多态)
- 重写是运行时通过虚表决议(动态多态)
- 多态本质上是重写机制的应用表现
设计目的:
- 重载:提供相同功能的多种实现方式
- 重写:实现子类特定行为
- 多态:实现接口统一、实现多样
4. 特殊注意事项
C++11新特性:
override
关键字:显式标记重写,避免误写final
关键字:禁止后续重写
class Derived : public Base { public: void show() final { ... } // 禁止进一步重写 };
重载陷阱:
- 默认参数可能导致重载歧义
void func(int a); void func(int a, int b = 0); // 危险的重载
多态成本:
- 虚函数调用有额外开销(虚表查找)
- 构造函数/析构函数中虚函数机制不生效
5. 应用场景对比
场景 | 适用技术 | 原因 |
---|---|---|
同一操作不同类型参数 | 重载 | 编译期即可确定具体调用版本 |
扩展父类功能 | 重写 | 需要运行时动态绑定 |
设计通用接口框架 | 多态 | 实现"开闭原则",便于扩展 |
运算符扩展 | 运算符重载 | 需要保持语法一致性 |
理解这三者的区别对于设计良好的面向对象系统至关重要,它们共同构成了C++多态特性的完整体系。