题目

编写程序,深入剖析 C++ 面向对象极其容易引起误解的概念——同名隐藏(Name Hiding)。演示当派生类定义了与基类同名但参数不同的成员函数时,无论基类函数是否带 virtual,派生类作用域都会将基类的所有同名重载函数彻底屏蔽隐藏(导致无法通过派生类对象直接调用基类版本的重载);随后展示使用 using Base::func; 声明将基类同名函数引入派生类作用域的解决之道。

输入格式:无需外部输入。

输出格式:输出同名隐藏被屏蔽与通过 using 恢复基类重载的测试对比。

数据范围与约定:编译器作用域查找规则(Name Lookup 先于 Overload Resolution)。

思路与算法

1. 为什么会被屏蔽(编译器两步查找法则)
C++ 编译器解析函数调用的顺序是:①名字查找(Name Lookup);②重载决议(Overload Resolution)。编译器在派生类作用域一旦找到该函数名,名字查找阶段即告终结,根本不会继续向父类作用域回溯查找!

2. 重写(Override)的硬性条件
只有基类函数是 virtual 且派生类函数与其函数名、参数列表、const 修饰完全一致时,才叫重写。

3. using Base::func 恢复机制
在派生类的 public 域中显式声明 using Base::func;,即可把基类的所有同名重载全部拉入当前派生类作用域中,与子类的新重载协同参与重载决议。

完整程序

#include <iostream>
#include <string>

class BaseCalculator {
public:
    virtual ~BaseCalculator() = default;

    // 基类的两个重载版本
    void compute(int x) {
        std::cout << "[Base::compute(int)] 整数计算: " << x * 2 << "\n";
    }

    void compute(double x) {
        std::cout << "[Base::compute(double)] 浮点计算: " << x * 2.0 << "\n";
    }
};

class DerivedCalculator : public BaseCalculator {
public:
    // 关键修正:将基类的所有 compute 重载版本引入当前作用域
    using BaseCalculator::compute;

    // 派生类引入的全新字符串重载
    void compute(const std::string& str) {
        std::cout << "[Derived::compute(string)] 字符串长度计算: " << str.length() << "\n";
    }
};

int main() {
    DerivedCalculator calc;

    std::cout << "--- 1. 调用派生类自身新增的重载 ---\n";
    calc.compute("Hello Modern C++");

    std::cout << "\n--- 2. 调用通过 using 恢复的基类重载 ---\n";
    // 若没有写 'using BaseCalculator::compute;',以下两行代码编译期会直接报错被隐藏屏蔽!
    calc.compute(42);
    calc.compute(3.14159);

    return 0;
}

运行示例

输出:

--- 1. 调用派生类自身新增的重载 ---
[Derived::compute(string)] 字符串长度计算: 16

--- 2. 调用通过 using 恢复的基类重载 ---
[Base::compute(int)] 整数计算: 84
[Base::compute(double)] 浮点计算: 6.28318