结合这c++ 代码,深入理解重载(overload)、隐藏(hide) 和覆盖(override,也叫重写) 这三个核心概念,用这段代码作为核心案例,由浅入深地拆解它们的区别和本质。
一、先明确三个概念的核心定义
这三个概念都是 c++ 中函数名复用的场景,但适用范围、规则和目的完全不同,先看一张对比表建立整体认知:
| 概念 | 中文名称 | 适用范围 | 核心特征 | 本质目的 |
|---|---|---|---|---|
| overload | 重载 | 同一个作用域内 | 函数名相同,参数列表(个数/类型/顺序)不同,返回值不影响 | 方便同一功能的不同参数调用 |
| override | 覆盖/重写 | 子类与父类之间 | 子类重写父类的虚函数,函数签名(名+参数+const)完全一致 | 实现多态(运行时动态绑定) |
| hide | 隐藏/遮蔽 | 子类与父类之间 | 子类函数名覆盖父类同名函数(无论参数/虚函数与否),编译期静态屏蔽 | 作用域优先原则导致的“遮蔽” |
覆盖是特殊的隐藏。
二、结合代码逐个拆解
逐个分析这三个概念,并用扩展代码演示效果。
1. 重载(overload)—— 同一作用域内的“同名不同参”
核心规则:
- 必须在同一个类/同一个作用域中;
- 函数名完全相同;
- 参数列表必须不同(个数、类型、顺序,三者至少一个不同);
- 返回值、是否是虚函数不影响重载(但仅返回值不同不算重载)。
代码中的重载案例:
在 base 类中,两个 show 函数就是典型的重载:
class base
{
public:
base(int a):ma(a) {}
// 重载1:无参 show
virtual void show() {cout<<"base::show()"<<endl;}
// 重载2:带int参数的 show —— 与上面构成重载
virtual void show(int i) {cout<<"base::show(int)"<<endl;}
private:
int ma;
};
- 它们在
base这个同一作用域内; - 函数名都是
show; - 参数列表不同(一个无参,一个int参数);
- 都是虚函数(但即使不是虚函数,依然是重载)。
重载的调用示例:
int main() {
base b(10);
b.show(); // 调用 base::show() —— 匹配无参版本
b.show(5); // 调用 base::show(int) —— 匹配int参数版本
return 0;
}
编译期编译器会根据实参类型自动匹配对应的重载函数,这是静态绑定(编译期确定调用哪个)。
2. 覆盖/重写(override)—— 子类对父类虚函数的“精准替换”
核心规则:
- 必须在子类与父类之间;
- 父类函数必须是虚函数(virtual);
- 子类函数的函数签名完全一致(函数名 + 参数列表 + const/volatile 属性完全相同);
- 子类函数可以加
override关键字显式声明(c++11 后推荐,编译器会检查是否真的覆盖)。
代码中的覆盖案例:
derive 类中的 show() 覆盖了 base 类中的无参 show():
class derive : public base
{
public:
derive(int a, int b):base(a), mb(b) {}
// 覆盖:与 base::show() 签名完全一致,且父类是虚函数
void show() override {cout<<"derive::show()"<<endl;} // 加override更规范
private:
int mb;
};
- 父类
base::show()是虚函数; - 子类
derive::show()函数名、参数列表(无参)完全一致; - 这就是“覆盖”,目的是实现多态。
覆盖的多态调用示例:
int main() {
base* ptr = new derive(1, 2); // 父类指针指向子类对象
ptr->show(); // 调用 derive::show() —— 运行时动态绑定(多态)
delete ptr;
return 0;
}
输出:derive::show()
这里因为覆盖了虚函数,运行时会根据对象的实际类型(derive)调用对应的函数,而不是指针类型(base),这是动态绑定。
3. 隐藏/遮蔽(hide)—— 子类对父类同名函数的“编译期屏蔽”
核心规则:
- 必须在子类与父类之间;
- 子类定义了与父类同名的函数(无论参数是否相同、是否是虚函数);
- 编译器在编译子类作用域时,会屏蔽父类所有同名函数,即使父类是重载/虚函数;
- 这是编译期静态行为,与多态无关。
代码中的隐藏案例(重点!):
你的代码中,derive 类定义了 show(),会隐藏父类 base 中所有名为 show 的函数(包括 show() 和 show(int)):
int main() {
derive d(1, 2);
d.show(); // 正常:调用 derive::show()
// d.show(5); // 编译错误!父类的 show(int) 被隐藏了
return 0;
}
为什么会报错?
因为子类 derive 中有 show() 函数,编译器在解析 d.show(5) 时,会优先在 derive 作用域找 show,发现只有无参的 show(),没有 show(int),且不会去父类找(被隐藏了),因此编译失败。
隐藏的其他场景(补充理解):
即使子类函数参数与父类不同,依然会隐藏:
class derive : public base
{
public:
derive(int a, int b):base(a), mb(b) {}
// 子类定义 show(double) —— 与父类 show(int) 不同参,但依然隐藏父类所有 show
void show(double d) {cout<<"derive::show(double)"<<endl;}
private:
int mb;
};
int main() {
derive d(1,2);
// d.show(5); // 编译错误!父类 show(int) 被隐藏,子类只有 show(double)
d.show(5.0); // 正常:调用 derive::show(double)
return 0;
}
如何解除隐藏?
如果想在子类中调用父类被隐藏的函数,有两种方式:
- 用
using声明,把父类函数引入子类作用域:
class derive : public base
{
public:
using base::show; // 解除父类所有 show 函数的隐藏
derive(int a, int b):base(a), mb(b) {}
void show() override {cout<<"derive::show()"<<endl;}
private:
int mb;
};
// 此时调用正常
int main() {
derive d(1,2);
d.show(); // 调用 derive::show()
d.show(5); // 调用 base::show(int) —— 隐藏被解除
return 0;
}
- 显式指定父类作用域调用:
int main() {
derive d(1,2);
d.base::show(5); // 直接调用父类的 show(int),不受隐藏影响
return 0;
}
三、容易混淆的点对比
1. 覆盖 vs 重载
| 维度 | 覆盖(override) | 重载(overload) |
|---|---|---|
| 作用域 | 跨类(父类→子类) | 同作用域(同一类) |
| 函数签名 | 必须完全一致 | 必须不同(参数列表) |
| 虚函数要求 | 父类必须是虚函数 | 无要求(可虚可非虚) |
| 绑定方式 | 运行时动态绑定 | 编译期静态绑定 |
2. 覆盖 vs 隐藏
| 维度 | 覆盖(override) | 隐藏(hide) |
|---|---|---|
| 前提 | 父类是虚函数 + 签名一致 | 只要子类有同名函数(无论参数/虚函数) |
| 本质 | 多态的实现方式 | 作用域优先导致的屏蔽 |
| 绑定方式 | 运行时动态绑定 | 编译期静态屏蔽 |
| 父类函数可用性 | 父类虚函数被“替换”但可通过作用域调用 | 父类同名函数被屏蔽,需显式调用 |
四、完整演示代码
把上述所有场景整合,可以直接运行测试:
#include <iostream>
using namespace std;
class base
{
public:
base(int a):ma(a) {}
// 重载1:无参虚函数
virtual void show() {cout<<"base::show()"<<endl;}
// 重载2:带int参数的虚函数(与上面构成重载)
virtual void show(int i) {cout<<"base::show(int) "<<i<<endl;}
private:
int ma;
};
class derive : public base
{
public:
using base::show; // 解除父类show的隐藏(注释掉这行看隐藏效果)
derive(int a, int b):base(a), mb(b) {}
// 覆盖:重写父类无参show
void show() override {cout<<"derive::show()"<<endl;}
// 新增:子类独有的show(double)(会隐藏父类show,除非加using)
void show(double d) {cout<<"derive::show(double) "<<d<<endl;}
private:
int mb;
};
int main() {
// 1. 测试base的重载
base b(10);
b.show(); // base::show()
b.show(5); // base::show(int) 5
// 2. 测试多态(覆盖)
base* ptr = new derive(1, 2);
ptr->show(); // derive::show() —— 动态绑定
ptr->show(6); // base::show(int) 6 —— 子类未覆盖该版本,调用父类
delete ptr;
// 3. 测试子类的隐藏与解除
derive d(1, 2);
d.show(); // derive::show()
d.show(7); // base::show(int) 7(加using后生效,否则编译错误)
d.show(3.14); // derive::show(double) 3.14
d.base::show(); // 显式调用父类show()
return 0;
}
总结
- 重载是同一作用域内“同名不同参”,编译期静态匹配,目的是简化调用;
- 覆盖是子类重写父类虚函数(签名一致),运行时动态绑定,是多态的核心;
- 隐藏是子类同名函数屏蔽父类所有同名函数(编译期行为),需用
using或作用域显式调用父类函数。
这三个概念的核心区别在于作用域和绑定时机:重载是“同作用域+编译期”,覆盖是“跨类+运行期”,隐藏是“跨类+编译期屏蔽”。
补充
#include <iostream>
using namespace std;
class base // base base(10);
{
public:
base(int a):ma(a) {}
// 虚函数
virtual void show() {cout<<"base::show()"<<endl;}
virtual void show(int i) {cout<<"base::show(int)"<<endl;}
private:
int ma;
};
class derive : public base
{
public:
derive(int a, int b):base(a), mb(b) {}
void show() {cout<<"derive::show"<<endl;}
private:
int mb;
};
补充说明
虚函数与重写:
base类中定义了两个虚函数show()和show(int i)。derive类重写了无参版本show(),但没有重写带参版本show(int i)。- 由于 c++ 的名称隐藏规则,
derive类中定义的show()会隐藏掉基类中所有名为show的函数(包括show(int i))。
典型调用行为:
int main() { derive d(1, 2); d.show(); // 正确,调用 derive::show() // d.show(10); // 编译错误!基类的 show(int) 被隐藏了 return 0; }如果想在
derive中也能调用base::show(int),需要在derive中添加using base::show;来解除名称隐藏。
到此这篇关于c++ 重载、隐藏、覆盖的区别小结的文章就介绍到这了,更多相关c++ 重载、隐藏、覆盖内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论