重载、重写、覆盖
在C++中,重载(Overloading)、**重写(Overriding)和隐藏(Hiding)**是三种不同的函数行为机制,它们在函数名相同的情况下,通过不同的方式实现多态性或覆盖。下面详细解释它们之间的区别:
# 一、重载(Overloading)
定义: 重载是指在同一作用域内,函数名称相同但参数列表不同(参数类型或数量不同)的多个函数共存。
特点:
- 发生在同一作用域内。
- 函数名相同,参数列表不同。
- 返回类型可以相同也可以不同。
- 通过参数列表的不同来区分调用,属于编译时多态性。
示例:
void print(int i) {}
void print(double d) {}
void print(const std::string& s) {}
1
2
3
4
2
3
4
# 二、重写(Overriding)
定义: 重写是指在派生类中重新定义基类中已经存在的虚函数,函数签名(名称、参数列表、返回类型)必须完全相同。
特点:
- 发生在继承关系中。
- 函数必须被声明为
virtual
。 - 函数签名完全相同,包括名称、参数列表和返回类型。
- 实现运行时多态性,通过基类指针或引用调用时,会执行派生类的实现。
示例:
#include <iostream>
class Base {
public:
virtual void display() {
std::cout << "Base display" << std::endl;
}
};
class Derived : public Base {
public:
void display() override { // 使用 override 关键字可以让编译器检查是否正确重写
std::cout << "Derived display" << std::endl;
}
};
int main() {
Base* ptr = new Derived();
ptr->display(); // 调用的是 Derived 的 display
delete ptr;
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 三、隐藏(Hiding)
定义: 隐藏是指在派生类中定义了与基类同名的成员(函数或变量),此时基类中的同名成员在派生类作用域中被隐藏。
特点:
- 发生在继承关系中。
- 函数名相同,但参数列表可以相同也可以不同。
- 即使参数列表不同,基类的同名函数也会被隐藏。
- 如果需要访问基类的被隐藏成员,可以使用作用域解析符
::
或using
声明。
示例1:函数隐藏
#include <iostream>
class Base {
public:
void show() {
std::cout << "Base show" << std::endl;
}
};
class Derived : public Base {
public:
void show(int x) {
std::cout << "Derived show with int: " << x << std::endl;
}
};
int main() {
Derived d;
d.show(10); // 调用 Derived::show(int)
// d.show(); // 错误:Base::show 被隐藏,无法直接调用
d.Base::show(); // 正确:使用作用域解析符调用 Base::show
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
示例2:使用 using
声明解决函数隐藏
#include <iostream>
class Base {
public:
void show() {
std::cout << "Base show" << std::endl;
}
};
class Derived : public Base {
public:
using Base::show; // 引入 Base::show,使其在 Derived 中可见
void show(int x) {
std::cout << "Derived show with int: " << x << std::endl;
}
};
int main() {
Derived d;
d.show(10); // 调用 Derived::show(int)
d.show(); // 调用 Base::show
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 总结区别
特性 | 重载(Overloading) | 重写(Overriding) | 隐藏(Hiding) |
---|---|---|---|
发生范围 | 同一作用域内 | 继承关系中 | 继承关系中 |
函数签名 | 函数名相同,参数列表不同,返回类型可同可不同 | 函数名、参数列表、返回类型完全相同,且基类函数需为 virtual | 函数名相同,参数列表可同可不同 |
多态性 | 编译时多态性 | 运行时多态性 | 不涉及多态性,可能导致基类成员不可见 |
关键字 | 无需特殊关键字 | 基类函数需声明为 virtual ,派生类中可使用 override 关键字 | 可使用 using 声明或作用域解析符 :: 访问被隐藏的基类成员 |
调用方式 | 通过参数列表区分调用 | 通过基类指针或引用调用,实际执行派生类的实现 | 同名的基类成员在派生类作用域中被隐藏,需显式指定访问基类成员 |
注意事项:
- 使用
override
关键字可以让编译器检查函数是否正确重写,避免意外的函数隐藏。 - 在发生函数隐藏时,可以通过
using
声明将基类的同名函数引入派生类作用域,或者使用 作用域解析符::
明确指定调用基类的成员。 - 理解这三者之间的区别对于编写健壮和可维护的代码非常重要,尤其是在复杂的继承结构中。
希望以上解释能帮助你清晰地理解重载、重写和隐藏之间的区别!
编辑 (opens new window)
上次更新: 2024/09/13, 11:59:12