HOWTO · C++
C++ 中的函数重载 VS 覆盖
本文演示如何在 C++ 中使用函数重载 VS 覆盖
本页内容
函数重载和函数重写在 C++ 中解决的是不同问题。函数重载是在同一作用域中为参数列表不同的函数使用相同名称;编译器会根据调用时传入的实参选择匹配项。函数重写则允许派生类为继承的 virtual 函数提供匹配实现;通过基类引用或指针调用时,程序会在运行时将调用派发给派生类对象。
C++ 中函数重载与函数重写的含义
函数重载是从参数不同的函数中选择合适的函数;函数重写则是在继承层次中替换函数行为。仅有不同的返回类型不足以构成重载,而函数重写要求基类函数是虚函数。override说明符会让编译器检查派生类中的声明是否确实匹配基类虚函数。
函数重载的工作方式
在同一作用域中声明多个同名函数,并为它们指定不同的参数列表。参数列表可以在参数数量或类型上有所不同。调用时,重载解析会根据实参及候选函数的参数类型进行选择。返回类型不会用于选择重载,因此,仅返回类型不同的两个声明不能组成有效的重载集合。
运行下面的示例:
本节中的代码以及显示的标签或输出仍为英文。
#include <iostream>
class Formatter {
public:
void show(int value) const {
std::cout << "integer: " << value << '\n';
}
void show(double value) const {
std::cout << "decimal: " << value << '\n';
}
};
int main() {
Formatter formatter;
formatter.show(7);
formatter.show(2.5);
}
integer: 7
decimal: 2.5
提供的运行证据确认,此示例已成功执行,并产生了所示输出。整数和小数实参会选择不同的 show 重载。添加重载时,请确保参数列表有实际区别,并且调用不会产生歧义。
函数重写的工作方式
基类将函数声明为 virtual;派生类则提供名称和参数列表相同,且限定符与返回类型兼容的函数。为派生类中的声明标注 override,这样不匹配就会产生编译期诊断。通过基类引用或指针调用函数时,虚函数派发会为该对象选择最终重写函数。
运行下面的示例:
本节中的代码以及显示的标签或输出仍为英文。
#include <iostream>
class Message {
public:
virtual void print() const {
std::cout << "message\n";
}
};
class Alert : public Message {
public:
void print() const override {
std::cout << "alert\n";
}
};
int main() {
Alert alert;
const Message& message = alert;
message.print();
}
alert
提供的运行证据确认,此示例已成功执行,并产生了所示输出。虽然 message 的静态类型是基类,但它引用的是 Alert 对象,因此这次虚函数调用会执行 Alert::print。基类也可以声明纯虚函数,例如使用 = 0;这样的类是抽象类,无法实例化,除非具体派生类实现了它的纯虚函数。
函数重载与函数重写:主要区别
| 对比方面 | 函数重载 | 函数重写 |
|---|---|---|
| 目的 | 为不同实参提供同名操作 | 为派生类型专门实现继承的行为 |
| 要求 | 同一作用域中存在不同的参数列表 | 存在继承关系,并且基类有匹配的 virtual 函数 |
| 选择方式 | 根据调用实参在编译期进行重载解析 | 通过基类引用或指针在运行时进行虚函数派发 |
| 有用的检查 | 确保调用会选中预期的重载 | 添加 override,检查声明是否匹配 |
重载可以不依赖继承或虚函数。函数重写则依赖继承和虚函数派发;它不会因为传入不同的参数列表而被选中。
常见错误及识别方法
派生类中的函数即使与基类函数同名,也不一定构成重写。如果基类函数不是虚函数,派生类声明会隐藏基类中的同名函数,而不会启用虚函数派发。如果参数、const限定符或函数类型的其他部分不匹配,预期的重写就可能失败;写上 override 可以让编译器在编译期指出这种不匹配。
不要只通过更改返回类型来尝试重载。如果需要多态行为,也不要按值传递派生类对象:将其复制到基类对象会发生对象切片,派生部分会被丢弃。使用基类引用或指针来保留动态派发。一般情况下,优先使用自动存储期对象或智能指针;如果要通过基类指针删除派生类对象,基类应具有虚析构函数。