C 语言

关注公众号 jb51net

关闭
首页 > 软件编程 > C 语言 > C++ 重载、重写与隐藏

C++中重载、重写与隐藏从语法到底层实现

作者:小徐不徐说

本文将从语法规则到底层实现,全面解析三者的差异,并深入探讨虚函数表(vtable)的工作原理,让你彻底掌握面试常问问题,感兴趣的可以了解一下

在 C++ 面向对象编程中,“重载(Overload)”、“重写(Override)” 和 “隐藏(Hide)” 是三个极易混淆的概念。它们虽都涉及函数名的复用,但其语义、作用机制和使用场景却截然不同。本文将从语法规则到底层实现,全面解析三者的差异,并深入探讨虚函数表(vtable)的工作原理。

一、函数重载(Overload)

✅ 定义

同一个作用域中,允许存在多个同名函数,只要它们的参数签名(参数个数、类型或顺序)不同。编译器会根据调用时的参数类型自动匹配最合适的函数版本。

📌 核心特性

示例代码

#include <iostream>
using namespace std;
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; }
};
int main() {
    Calculator calc;
    cout << calc.add(2, 3) << endl;       // 调用 add(int, int)
    cout << calc.add(2.5, 3.5) << endl;   // 调用 add(double, double)
    cout << calc.add(1, 2, 3) << endl;    // 调用 add(int, int, int)
    return 0;
}

总结

重载的本质是 “同名函数的多版本实现”,目的是让函数名更直观(如用 add 统一表示加法操作,无论参数是整数还是小数),其匹配逻辑由编译器在编译期完成。

二、函数重写(Override)

✅ 定义

派生类中重新实现基类中声明为 virtual 的函数,要求函数签名(函数名、参数列表、返回值类型)完全一致。重写是实现多态的核心机制。

📌 核心特性

示例代码

#include <iostream>
using namespace std;
class Shape {
public:
    // 基类中声明为虚函数
    virtual void draw() { cout << "绘制图形" << endl; }
};
class Circle : public Shape {
public:
    // 重写基类的 draw 函数,用 override 显式标注
    void draw() override { cout << "绘制圆形" << endl; }
};
class Rectangle : public Shape {
public:
    void draw() override { cout << "绘制矩形" << endl; }
};
int main() {
    Shape* shape1 = new Circle();
    Shape* shape2 = new Rectangle();
    // 运行时根据实际对象类型调用对应函数(多态)
    shape1->draw();  // 输出:绘制圆形
    shape2->draw();  // 输出:绘制矩形
    delete shape1;
    delete shape2;
    return 0;
}

注意事项

三、函数隐藏(Hide)

✅ 定义

当派生类中定义了一个与基类同名函数时,若该函数不是重写(即基类函数非虚函数,或参数签名不同),则派生类的函数会 “隐藏” 基类中所有同名函数 —— 即基类的同名函数在派生类中不可见,除非显式调用。

📌 核心特性

示例代码

#include <iostream>
using namespace std;
class Base {
public:
    void func(int x) { cout << "Base::func(int): " << x << endl; }
    void func(string s) { cout << "Base::func(string): " << s << endl; }
};
class Derived : public Base {
public:
    // 派生类定义同名函数,参数类型不同(隐藏基类所有 func 函数)
    void func(double x) { cout << "Derived::func(double): " << x << endl; }
};
int main() {
    Derived d;
    d.func(3.14);    // 调用 Derived::func(double)
    // d.func(10);    // 编译错误:基类的 func(int) 被隐藏
    // d.func("hi");  // 编译错误:基类的 func(string) 被隐藏
    // 显式调用基类被隐藏的函数
    d.Base::func(10);      // 输出:Base::func(int): 10
    d.Base::func("hi");    // 输出:Base::func(string): hi
    return 0;
}

如何避免隐藏?

若希望在派生类中同时保留基类的同名函数,可使用 using 声明引入基类函数:

class Derived : public Base {
public:
    using Base::func;  // 引入基类所有 func 重载函数
    void func(double x) { cout << "Derived::func(double): " << x << endl; }
};
// 此时可正常调用基类的 func 函数
int main() {
    Derived d;
    d.func(10);       // 调用 Base::func(int)
    d.func("hi");     // 调用 Base::func(string)
    d.func(3.14);     // 调用 Derived::func(double)
    return 0;
}

四、虚函数表(vtable):重写的底层实现

重写的核心是多态,而多态的底层依赖于虚函数表(vtable) 和虚指针(vptr) 机制。以下是其工作原理:

1. 虚函数表的本质

2. 实现过程(以之前的Shape类为例)

3. 虚函数表的特性

五、三者对比一览表

特性重载(Overload)重写(Override)隐藏(Hide)
作用域同一类 / 命名空间派生类与基类之间派生类与基类之间
函数关系同名不同参同名同参(基类为虚函数)同名(参数不同或基类非虚函数)
与继承的关系必须继承必须继承
虚函数要求无需基类必须声明 virtual基类通常非虚函数
绑定方式编译期静态绑定运行期动态绑定(vtable)编译期静态绑定
核心目的提供多版本函数接口实现多态(同一接口不同实现)屏蔽基类同名函数
调用方式直接调用,编译器自动匹配通过基类指针 / 引用调用派生类对象直接调用派生类函数,基类需显式调用

六、记忆口诀与实战建议

记忆口诀

实战建议

  1. 用 override 标注重写:明确意图,让编译器检查签名是否正确,避免意外隐藏。
  2. 避免同名函数在派生类中随意定义:若无需重写,尽量不要定义与基类同名的函数。
  3. 必要时用 using 引入基类函数:当需要在派生类中保留基类同名函数时,用 using 显式引入。
  4. 理解虚函数表机制:不仅能帮你掌握多态的本质,还能在调试复杂继承关系时快速定位问题。

结语

掌握重载、重写和隐藏的差异,是学好 C++ 面向对象编程的基础。其中,重写与虚函数表机制更是 C++ 多态的核心,理解其底层原理能让你在设计类层次结构时更加得心应手。记住:语法是表象,机制是本质,只有透过现象看本质,才能真正掌握这些概念。

到此这篇关于C++中重载、重写与隐藏从语法到底层实现的文章就介绍到这了,更多相关C++ 重载、重写与隐藏内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文