C++ 重载(Overload)、重写(Override)、隐藏(Hiding) 的区别
C++ 重载(Overload)、重写(Override)、隐藏(Hiding) 的区别
这三个概念是 C++ 面向对象的核心知识点,也是面试必问内容。下面我们从定义、发生条件、代码示例、底层原理全方位解析它们的区别。
一、核心区别对比表(速记版)
特性 | 重载(Overload) | 重写(Override) | 隐藏(Hiding) |
---|---|---|---|
作用域 | 同一作用域 | 父子类继承关系 | 父子类继承关系 |
函数名 | 相同 | 相同 | 相同 |
参数列表 | 必须不同 | 必须相同 | 可以相同或不同 |
virtual | 不需要 | 必须基类有virtual | 不需要 |
返回值 | 可不同 | 必须相同(协变除外) | 可不同 |
多态性 | 编译时决定 | 运行时多态 | 编译时决定 |
二、重载(Overload) - 同一作用域的函数变体
定义
在同一作用域内,函数名相同但参数列表不同(类型、顺序、数量)
关键代码示例
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; }
};
核心特点
- 必须在同一作用域(如同一个类中)
- 返回值类型可不同(但仅返回值不同不构成重载!)
- 调用时编译器根据参数类型决定调用哪个版本
- 典型应用:构造函数重载、运算符重载
三、重写(Override) - 多态性的核心实现
定义
在继承体系中,派生类重新定义基类的 virtual
函数,实现运行时多态。
关键代码示例
class Animal {
public:virtual void speak() { cout << "Animal sound" << endl; } // 虚函数
};class Dog : public Animal {
public:void speak() override { cout << "Wang Wang!" << endl; } // 重写
};Animal* animal = new Dog();
animal->speak(); // 输出 "Wang Wang!"(多态调用)
核心特点
- 必须通过基类指针/引用调用才能体现多态
- 函数签名必须完全相同(C++11可用
override
关键字检查) - 特殊情况:协变返回类型(允许返回派生类指针/引用)
class Base { public:virtual Base* clone() { return new Base(); } }; class Derived : public Base { public:Derived* clone() override { return new Derived(); } // 合法协变 };
四、隐藏(Hiding) - 最容易踩坑的隐藏规则
定义
当派生类定义了与基类同名函数(无论参数是否相同),隐藏基类的同名函数。
关键代码示例
class Base {
public:void func() { cout << "Base::func()" << endl; }void func(int) { cout << "Base::func(int)" << endl; }
};class Derived : public Base {
public:void func() { cout << "Derived::func()" << endl; } // 隐藏基类所有func版本
};Derived d;
d.func(); // 正确,调用Derived::func()
// d.func(1); // 编译错误!基类func(int)被隐藏
核心特点
- 隐藏所有基类同名函数(包括重载版本)
- 可通过
using
声明恢复可见性:class Derived : public Base { public:using Base::func; // 恢复基类func的所有重载void func() { cout << "Derived::func()" << endl; } };
- 与重写的区别:不需要virtual,不构成多态
五、对比案例分析
案例1:重载 vs 重写
class A {
public:virtual void foo(int) { cout << "A::foo(int)" << endl; }void bar(int) { cout << "A::bar(int)" << endl; }
};class B : public A {
public:void foo(int) override { cout << "B::foo(int)" << endl; } // 重写void bar(double) { cout << "B::bar(double)" << endl; } // 隐藏(非重载!)
};B b;
A* pa = &b;
pa->foo(1); // B::foo(int) (多态)
pa->bar(1); // A::bar(int) (无多态)
b.bar(1.0); // B::bar(double)
// b.bar(1); // 编译警告,隐式转换,仍然调用B::bar(double)
案例2:隐藏的陷阱
class Base {
public:void print(int x) { cout << "Base: " << x << endl; }
};class Derived : public Base {
public:void print(string s) { cout << "Derived: " << s << endl; } // 隐藏基类print(int)
};Derived d;
d.print("hello"); // Derived: hello
// d.print(42); // 编译错误!基类print(int)被隐藏
d.Base::print(42); // 正确,显式指定作用域
六、面试高频问题
Q1:如何强制检查是否正确重写?
✅ C++11 使用 override
关键字:
class Child : public Parent {void foo() override; // 如果Parent没有virtual foo(),编译报错
};
Q2:重载能否跨作用域?
❌ 不能!派生类定义同名函数会隐藏基类重载版本(除非使用using
)
Q3:虚函数能否重载?
✅ 可以!但重写时必须签名完全匹配(协变返回除外)
Q4:如何区分重写和隐藏?
✅ 看两点:
- 基类是否有
virtual
关键字 - 是否通过基类指针/引用调用表现出多态