题目
编写程序,深入剖析 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