跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++

C++ 函数重载、隐藏与覆盖详解

深入解析 C++ 中函数重载、隐藏与覆盖的区别。重载指同一作用域内同名不同参,编译期静态绑定;覆盖指子类重写父类虚函数,实现运行时多态;隐藏指子类同名函数屏蔽父类同名函数,属编译期行为。通过对比表格与代码示例,明确了三者在作用域、签名要求及绑定机制上的核心差异,并提供了使用 using 声明解除隐藏的解决方案。

刀狂发布于 2026/3/22更新于 2026/9/471 浏览

结合以下 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;
}
如何解除隐藏?

如果想在子类中调用父类被隐藏的函数,有两种方式:

  1. 用 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;
}
  1. 显式指定父类作用域调用:
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;
}
总结
  1. 重载是同一作用域内'同名不同参',编译期静态匹配,目的是简化调用;
  2. 覆盖是子类重写父类虚函数(签名一致),运行时动态绑定,是多态的核心;
  3. 隐藏是子类同名函数屏蔽父类所有同名函数(编译期行为),需用 using 或作用域显式调用父类函数。

这三个概念的核心区别在于作用域和绑定时机:重载是'同作用域 + 编译期',覆盖是'跨类 + 运行期',隐藏是'跨类 + 编译期屏蔽'。


补充

#include <iostream>
using namespace std;

class Base {
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;
};
补充说明
  1. 虚函数与重写:
    • 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; 来解除名称隐藏。

目录

  1. 一、先明确三个概念的核心定义
  2. 二、结合代码逐个拆解
  3. 1. 重载(Overload)—— 同一作用域内的“同名不同参”
  4. 核心规则:
  5. 代码中的重载案例:
  6. 重载的调用示例:
  7. 2. 覆盖/重写(Override)—— 子类对父类虚函数的“精准替换”
  8. 核心规则:
  9. 代码中的覆盖案例:
  10. 覆盖的多态调用示例:
  11. 3. 隐藏/遮蔽(Hide)—— 子类对父类同名函数的“编译期屏蔽”
  12. 核心规则:
  13. 代码中的隐藏案例(重点!):
  14. 隐藏的其他场景(补充理解):
  15. 如何解除隐藏?
  16. 三、容易混淆的点对比
  17. 1. 覆盖 vs 重载
  18. 2. 覆盖 vs 隐藏
  19. 四、完整演示代码
  20. 总结
  21. 补充
  22. 补充说明

更多推荐文章

查看全部
  • Stable Diffusion Windows 环境搭建与使用指南
  • IT 行业饱和背景下,为何网络安全成为热门转行方向?
  • nvm 安装指定版本 Node.js 失败解决方案
  • Java 归并排序详解:分治思想、代码实现与面试考点
  • Whisper.cpp 量化模型清单与 ggml 格式下载指南
  • Whisper 音频转录实战
  • 自然语言处理在金融领域的应用与实战
  • Python 枚举类:定义、使用和最佳实践
  • 大语言模型安全指南:越狱攻击、提示注入与数据投毒
  • Xilinx FPGA 驱动 USB3.0 外设实战指南
  • ERNIE-4.5 模型单卡部署与心理健康机器人实战
  • Cursor Rules 实战指南:让 AI 按你的规矩写代码
  • YOLOv8 车牌定位模型训练与 OpenCV C++ 部署完整指南
  • Cursor 的代理配置:填一次就生效
  • AI Agent 架构:基础组成模块深度解析
  • ComfyUI 本地部署及 Stable Diffusion 使用指南
  • Cursor AI 编程工具深度解析与技术架构
  • GitHub Copilot 登录失败排查指南:7 个关键检查点
  • Spring AI 实战:基于 Ollama 构建离线私有化 AI 服务
  • 开放代理产品功能介绍

相关免费在线工具

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online

  • JSON 压缩

    通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online

  • JSON美化和格式化

    将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online