C++ 中重载、重写与隐藏的区别详解

 更新时间:2026年08月24日 09:15:24   作者:布莱克605  
C++中的重载、重写和隐藏是处理同名函数的关键机制,但适用场景与规则不同,帮你彻底搞懂三者的定义、规则和区别,避免继承中的常见陷阱,写出更健壮的面向对象代码

1. 引言

在 C++ 面向对象编程中,重载(Overloading)重写(Overriding)隐藏(Hiding)是三个容易混淆但至关重要的概念。它们都涉及同名函数,但发生场景、作用机制和结果截然不同。理解这些区别对于编写正确、高效且易于维护的 C++ 代码至关重要。

本文将结合详细的 C++ 代码示例,深入剖析这三者的定义、规则、区别以及典型应用场景。

2. 函数重载(Overloading)

2.1 定义与规则

函数重载发生在同一个作用域内(例如同一个类中,或同一个命名空间下)。它允许定义多个同名函数,但这些函数的参数列表(参数的类型、个数或顺序)必须不同。函数的返回类型可以相同也可以不同,但仅返回类型不同不足以构成重载。

编译器在编译时根据调用时提供的实参类型和数量来决定调用哪个重载函数,这个过程称为静态绑定(早绑定)

2.2 代码示例

#include <iostream>
using namespace std;

class Calculator {
public:
    // 重载函数:参数个数不同
    int add(int a, int b) {
        return a + b;
    }
    int add(int a, int b, int c) {
        return a + b + c;
    }

    // 重载函数:参数类型不同
    double add(double a, double b) {
        return a + b;
    }

    // 错误示例:仅返回类型不同,不是有效的重载
    // double add(int a, int b) { return (double)(a + b); } // 编译错误
};

int main() {
    Calculator calc;
    cout << "calc.add(1, 2) = " << calc.add(1, 2) << endl;       // 调用 int add(int, int)
    cout << "calc.add(1, 2, 3) = " << calc.add(1, 2, 3) << endl; // 调用 int add(int, int, int)
    cout << "calc.add(1.5, 2.5) = " << calc.add(1.5, 2.5) << endl; // 调用 double add(double, double)
    return 0;
}

3. 函数重写(Overriding)

3.1 定义与规则

函数重写发生在继承层次结构中。它是指派生类(子类)重新定义基类(父类)中已有的虚函数(virtual function),以实现多态性。

重写必须满足以下严格条件:

  • 基类函数必须声明为 virtual(C++11 后可使用 override 关键字显式标记)。
  • 函数名、参数列表(类型、个数、顺序)和返回类型必须完全相同(协变返回类型除外)。
  • 访问权限可以不同(但通常建议保持一致)。

重写是实现运行时多态的关键。通过基类指针或引用调用虚函数时,实际调用的是指针或引用所指向的对象类型的版本,这个过程称为动态绑定(晚绑定)

3.2 代码示例

#include <iostream>
using namespace std;

class Animal {
public:
    // 基类虚函数
    virtual void speak() {
        cout << "Animal makes a sound." << endl;
    }
    virtual ~Animal() {} // 虚析构函数,良好实践
};

class Dog : public Animal {
public:
    // 重写基类虚函数
    void speak() override { // C++11 使用 override 确保正确重写
        cout << "Dog barks: Woof! Woof!" << endl;
    }
};

class Cat : public Animal {
public:
    // 重写基类虚函数
    void speak() override {
        cout << "Cat meows: Meow~" << endl;
    }
};

int main() {
    Animal* animal1 = new Dog();
    Animal* animal2 = new Cat();

    animal1->speak(); // 输出: Dog barks: Woof! Woof!
    animal2->speak(); // 输出: Cat meows: Meow~

    delete animal1;
    delete animal2;
    return 0;
}

4. 函数隐藏(Hiding)

4.1 定义与规则

函数隐藏也发生在继承层次结构中。当派生类定义了与基类同名的函数(无论参数列表是否相同,也无论基类函数是否为虚函数)时,基类的同名函数在派生类的作用域内就会被隐藏

关键点:

  • 隐藏与重载不同:重载要求在同一作用域,而隐藏是跨作用域的。
  • 隐藏与重写不同:重写要求基类函数是虚函数且签名严格匹配,而隐藏不要求。
  • 一旦发生隐藏,通过派生类对象直接调用该函数名时,只能看到派生类的版本。若要调用被隐藏的基类函数,需要使用作用域解析运算符 ::

4.2 代码示例

#include <iostream>
using namespace std;

class Base {
public:
    void func() {
        cout << "Base::func()" << endl;
    }
    void func(int x) { // 重载版本
        cout << "Base::func(int): " << x << endl;
    }
};

class Derived : public Base {
public:
    // 定义了同名函数,隐藏了基类中的所有 func 函数
    void func() {
        cout << "Derived::func()" << endl;
    }
    // 注意:这里并没有重载 Base::func(int),而是隐藏了它。
};

int main() {
    Derived d;
    d.func();       // 正确:调用 Derived::func()
    // d.func(10);  // 错误!Base::func(int) 被隐藏了,无法直接调用。

    // 使用作用域解析运算符调用被隐藏的基类函数
    d.Base::func();      // 正确:调用 Base::func()
    d.Base::func(100);   // 正确:调用 Base::func(int)

    Base* ptr = &d;
    ptr->func();    // 正确:调用 Base::func() (非虚函数,静态绑定)
    ptr->func(200); // 正确:调用 Base::func(int)
    return 0;
}

5. 核心区别对比总结

特性重载 (Overloading)重写 (Overriding)隐藏 (Hiding)
作用域同一作用域(如类内)不同作用域(继承关系)不同作用域(继承关系)
函数关系同名不同参同名同参(虚函数)同名(参数可同可不同)
virtual 关键字不需要基类函数必须为 virtual不需要
绑定方式静态绑定(编译时)动态绑定(运行时)静态绑定(编译时)
目的提供同一操作的不同版本实现多态性派生类覆盖基类实现(非多态)
调用基类版本根据参数自动选择通过基类指针/引用调用子类版本需使用 BaseClass::function()

6. 常见误区与最佳实践

6.1 误区:误将隐藏当作重载

在继承中,如果派生类定义了与基类同名的函数,即使参数列表不同,它也会隐藏基类中所有同名函数(包括重载版本),而不是在派生类作用域内形成重载。

class Base {
public:
    void show(int) { /* ... */ }
};
class Derived : public Base {
public:
    void show(double) { /* ... */ } // 隐藏了 Base::show(int),并未重载
};
// Derived d; d.show(10); // 错误!需要类型转换,因为 Base::show(int) 被隐藏了。

解决方案: 如果希望在派生类中也能使用基类的重载函数,可以使用 using 声明将其引入派生类作用域。

class Derived : public Base {
public:
    using Base::show; // 引入 Base 中的所有 show 函数
    void show(double) { /* ... */ } // 现在与引入的 show(int) 形成重载
};
// Derived d; d.show(10); // 正确:调用 Base::show(int)

6.2 最佳实践

  • 重写时使用 override 关键字(C++11及以上):让编译器检查是否真正重写了虚函数,避免因签名错误导致的意外隐藏。
  • 为多态基类声明虚析构函数:确保通过基类指针删除派生类对象时行为正确。
  • 理解隐藏机制:明确区分“想重写”和“想隐藏”的意图,避免因隐藏导致基类功能意外不可用。
  • 优先使用重写实现多态:隐藏通常不是设计首选,它破坏了通过基类接口操作派生类对象的统一性。

7. 总结

重载、重写和隐藏是 C++ 中处理同名函数的三种不同机制:

  • 重载是横向的,在同一层次提供功能变体,编译时决定。
  • 重写是纵向的,在继承层次中实现多态,运行时决定,是面向对象的核心。
  • 隐藏也是纵向的,但它是名字遮盖,会屏蔽基类的同名函数,通常需要谨慎使用。

掌握它们的区别,有助于你更精准地控制函数的行为,编写出符合预期、易于理解和维护的 C++ 代码。

到此这篇关于C++ 中重载、重写与隐藏的区别详解(附代码示例)的文章就介绍到这了,更多相关C++ 重载、重写与隐藏区别内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C++实现哈夫曼树编码解码

    C++实现哈夫曼树编码解码

    这篇文章主要为大家详细介绍了C++实现哈夫曼树编码解码,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-04-04
  • 详解C语言数据结构之栈

    详解C语言数据结构之栈

    这篇文章主要为大家介绍了C语言数据结构之栈,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-01-01
  • C语言实现扫雷代码

    C语言实现扫雷代码

    这篇文章主要为大家详细介绍了C语言实现扫雷代码,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-06-06
  • C++的optional用法实例详解

    C++的optional用法实例详解

    编程中我们可能会遇到要处理可能为空的变量,比如说容器,基本类型,或者说对象实例,下面通过实例代码介绍C++的optional用法,感兴趣的朋友一起看看吧
    2024-02-02
  • 基于C++实现TCP聊天室功能

    基于C++实现TCP聊天室功能

    这篇文章主要为大家详细介绍了基于C++实现TCP聊天室功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-07-07
  • 浅谈C语言中strcpy,strcmp,strlen,strcat函数原型

    浅谈C语言中strcpy,strcmp,strlen,strcat函数原型

    下面小编就为大家带来一篇浅谈C语言中strcpy,strcmp,strlen,strcat函数原型。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-04-04
  • C++用mysql自带的头文件连接数据库

    C++用mysql自带的头文件连接数据库

    现在正做一个接口,通过不同的连接字符串操作不同的数据库。要用到mysql数据库。通过网上的一些资料和自己的摸索,大致清楚了C++连接mysql的方法。可以通过2种方法实现。第一种方法是利用ADO连接,第二种方法是利用mysql自己的api函数进行连接。今天主要来讲解下使用API
    2016-07-07
  • C语言中队列的结构和函数接口的使用示例

    C语言中队列的结构和函数接口的使用示例

    队列只允许一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO的性质;队列可用数组和链表 的方法实现,使用链表的结构实现更优一些,因为如果使用数组节,出队列时删去首元素需要将整个数组前移,效率比较低
    2023-02-02
  • C++学习之指针的使用详解

    C++学习之指针的使用详解

    这篇文章主要为大家详细介绍了C++中指针的相关知识,文中的示例代码讲解详细,对我们的学习或工作有一定的价值,感兴趣的小伙伴可以了解一下
    2023-03-03
  • C++ 实现输入含空格的字符串

    C++ 实现输入含空格的字符串

    这篇文章主要介绍了C++ 实现输入含空格的字符串,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12

最新评论