同名隐藏
一、什么是同名隐藏?
在继承关系中,如果派生类定义了与基类同名的成员 (数据成员或成员函数),派生类的成员会隐藏基类中同名的成员。这种现象称为同名隐藏。
cpp
class Object {
public:
int value;
int num; // 基类成员
};
class Base : public Object {
public:
int num; // 派生类成员(与基类同名!)
int sum;
void fun() {
num = 10; // 默认访问派生类自己的 num
// 基类的 num 被隐藏了
}
};
二、同名隐藏的核心规则
2.1 就近原则
当派生类中定义了与基类同名的成员时,编译器遵循就近原则:优先使用派生类自己的成员。
cpp
class Object {
public:
int num = 100; // 基类的 num
};
class Base : public Object {
public:
int num = 200; // 派生类的 num(同名隐藏)
void fun() {
cout << num << endl; // 输出 200(派生类的 num)
// 基类的 num 被隐藏了
}
};
2.2 如何访问被隐藏的基类成员?
使用作用域解析运算符 :: 可以强制访问被隐藏的基类成员。
cpp
class Object {
public:
int num;
};
class Base : public Object {
public:
int num;
void fun() {
num = 10; // 访问派生类的 num
Object::num = 100; // 访问基类的 num(使用作用域解析)
}
};
int main() {
Base base;
base.num = 20; // 访问派生类的 num
base.Object::num = 200; // 访问基类的 num
return 0;
}
三、数据成员的同名隐藏
3.1 基本示例
cpp
#include <iostream>
using namespace std;
class Object {
public:
int value;
int num;
Object() : value(0), num(0) {}
};
class Base : public Object {
public:
int num; // 同名隐藏:隐藏了 Object::num
int sum;
Base() : num(100), sum(0) {} // 派生类的 num 初始化为 100
void fun() {
sum = 1;
value = 1; // value 在 Object 中,没有被隐藏
num = 10; // 访问派生类的 num(就近原则)
// 访问被隐藏的基类成员
Object::num = 100; // 访问 Object 中的 num
}
};
int main() {
Base base;
cout << "sizeof(Base) = " << sizeof(Base) << endl;
// 内存布局:Object::value(4) + Object::num(4) + Base::num(4) + sum(4) = 16 字节
base.fun();
// 访问派生类的 num
base.num = 122; // 修改 Base::num
cout << "base.num = " << base.num << endl; // 122
// 访问基类的 num(需要作用域解析)
base.Object::num = 100; // 修改 Object::num
cout << "base.Object::num = " << base.Object::num << endl; // 100
return 0;
}
注意:
基类和派生类各有一份独立的 num,互不影响!
3.3 访问权限对同名隐藏的影响
cpp
class Object {
public:
int pub_num;
protected:
int pro_num;
private:
int pri_num;
};
class Base : public Object {
private:
int pub_num; // 可以隐藏基类的 public 成员
int pro_num; // 可以隐藏基类的 protected 成员
// int pri_num; // 不能隐藏基类的 private 成员(不可见)
};
// 结论:只能隐藏基类中可见的成员(public 和 protected)
四、成员函数的同名隐藏
4.1 函数同名隐藏
cpp
class Object {
public:
void func() {
cout << "Object::func()" << endl;
}
void func(int x) {
cout << "Object::func(int): " << x << endl;
}
};
class Base : public Object {
public:
// 派生类定义了同名函数(参数不同)
void func(int x) { // 隐藏了 Object 中的所有 func 函数!
cout << "Base::func(int): " << x << endl;
}
};
int main() {
Base base;
base.func(12); // 调用 Base::func(int)
// base.func(); // 错误!Object::func() 被隐藏了
// base.func(3.14); // 错误!Object::func(int) 也被隐藏了
// 使用作用域解析访问被隐藏的基类函数
base.Object::func(); // 调用 Object::func()
base.Object::func(20); // 调用 Object::func(int)
return 0;
}
4.2 重要概念:函数重载 与 同名隐藏
| 概念 | 发生条件 | 作用域 | 示例 |
|---|---|---|---|
| 函数重载 | 同一作用域内,函数名相同,参数不同 | 同一个类 | Base 类中定义多个 func |
| 同名隐藏 | 不同作用域(基类和派生类),函数名相同 | 不同类 | 派生类定义 func,隐藏基类的所有 func |
cpp
class Object {
public:
void func() { } // 函数1
void func(int) { } // 函数2(重载)
};
class Base : public Object {
public:
void func(double) { } // 函数3(不是重载!是隐藏)
// 此时,函数1和函数2都被隐藏了
};
五、完整代码示例
cpp
#include <iostream>
using namespace std;
// ========== 数据成员同名隐藏 ==========
class Object {
public:
int value;
int num;
Object() : value(0), num(0) {
cout << "Object 构造: value=" << value << ", num=" << num << endl;
}
};
class Base : public Object {
public:
int num; // 同名隐藏
int sum;
Base() : num(100), sum(0) {
cout << "Base 构造: num(派生)=" << num << ", sum=" << sum << endl;
}
void fun() {
cout << "\n=== Base::fun() ===" << endl;
sum = 1;
value = 1; // 访问 Object::value
num = 10; // 默认访问 Base::num
cout << "Base::num = " << num << endl;
// 访问被隐藏的基类成员
Object::num = 100; // 访问 Object::num
cout << "Object::num = " << Object::num << endl;
}
};
// ========== 成员函数同名隐藏 ==========
class Animal {
public:
void speak() {
cout << "Animal::speak()" << endl;
}
void speak(int times) {
cout << "Animal::speak(int): " << times << " 次" << endl;
}
virtual void move() {
cout << "Animal::move()" << endl;
}
};
class Dog : public Animal {
public:
// 同名隐藏:隐藏了 Animal 中所有名为 speak 的函数
void speak() {
cout << "Dog::speak(): 汪汪" << endl;
}
// 虚函数可以被覆盖(override),不是隐藏
void move() override {
cout << "Dog::move(): 跑" << endl;
}
};
int main() {
cout << "========== 数据成员同名隐藏 ==========" << endl;
Base base;
base.fun();
cout << "\n外部访问:" << endl;
base.num = 200; // 修改 Base::num
cout << "base.num = " << base.num << endl;
base.Object::num = 300; // 修改 Object::num
cout << "base.Object::num = " << base.Object::num << endl;
cout << "\n========== 成员函数同名隐藏 ==========" << endl;
Dog dog;
dog.speak(); // 调用 Dog::speak()
// dog.speak(3); // 错误!Animal::speak(int) 被隐藏了
dog.Animal::speak(); // 调用 Animal::speak()
dog.Animal::speak(5); // 调用 Animal::speak(int)
dog.move(); // 调用 Dog::move()(虚函数覆盖,不是隐藏)
return 0;
}
六、同名隐藏 与 虚函数覆盖
| 特性 | 同名隐藏 | 虚函数覆盖 |
|---|---|---|
| 关键字 | 无特殊关键字 | virtual / override |
| 函数名相同 | 是 | 是 |
| 参数相同 | 不一定 | 必须完全相同 |
| 作用 | 隐藏基类所有同名函数 | 实现多态 |
| 基类函数可见性 | 被隐藏(需要 :: 访问) |
可被覆盖,仍可通过基类指针访问 |
| 多态行为 | 不支持 | 支持 |
cpp
class Base {
public:
void hide() { cout << "Base::hide" << endl; }
virtual void cover() { cout << "Base::cover" << endl; }
};
class Derived : public Base {
public:
void hide() { cout << "Derived::hide" << endl; } // 同名隐藏
void cover() override { cout << "Derived::cover" << endl; } // 覆盖
};
int main() {
Base* p = new Derived();
p->hide(); // 调用 Base::hide(隐藏不支持多态)
p->cover(); // 调用 Derived::cover(覆盖支持多态)
return 0;
}
七、总结
7.1 同名隐藏规则总结
| 成员类型 | 隐藏规则 | 如何访问基类成员 |
|---|---|---|
| 数据成员 | 派生类同名数据成员隐藏基类成员 | 基类名::成员名 |
| 成员函数 | 派生类同名函数隐藏基类所有同名函数 | 基类名::函数名() |
| 静态成员 | 同样适用同名隐藏规则 | 基类名::成员名 |
7.2 注意事项总结
cpp
// 1. 同名隐藏不受返回类型影响
class Object {
int getValue() { return 0; }
};
class Base : public Object {
double getValue() { return 0.0; } // 隐藏了 Object::getValue
};
// 2. 同名隐藏不受参数列表影响
// 只要函数名相同,基类的所有重载版本都会被隐藏
// 3. 可以通过 using 声明将基类函数引入派生类作用域
class Base : public Object {
public:
using Object::func; // 将基类的 func 引入派生类
void func(int x) { } // 现在 func() 和 func(int) 都可以访问
};