提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
函数重载时,如果重载函数参数分别为父类指针类型与子类指针类型,那么此时这两个函数可以形成重载吗,如果实参传子类对象,由于多态的存在,两个函数按道理来说都满足调用,此时是什么情况呢,详细说明一下
要彻底理解这个问题,需要先区分函数重载的核心规则和多态的作用机制,这是两个不同阶段(编译期 vs 运行期)的特性,切勿混淆。
一、核心问题1:父类指针/子类指针作为参数,能否构成函数重载?
结论:可以构成重载。
函数重载的判定标准(C++):
在同一作用域内,函数名相同,且参数列表(参数类型/个数/顺序)不同(返回值不影响重载)。
父类指针(如 Base*)和子类指针(如 Sub*)是不同的类型(子类指针是独立的类型,仅可隐式转换为父类指针),因此满足“参数列表不同”的条件,完全可以构成重载。
示例基础代码(后文均基于此):
#include <iostream>
using namespace std;
// 父类
class Base {
public:
virtual void show() { // 虚函数,用于演示多态
cout << "Base::show()" << endl;
}
};
// 子类,继承Base
class Sub : public Base {
public:
void show() override { // 重写父类虚函数
cout << "Sub::show()" << endl;
}
};
// 重载函数1:参数为父类指针
void func(Base* b) {
cout << "调用 func(Base*):";
b->show(); // 若show是虚函数,此处体现多态
}
// 重载函数2:参数为子类指针
void func(Sub* s) {
cout << "调用 func(Sub*):";
s->show();
}
二、核心问题2:实参传子类指针时,调用哪个重载函数?
结论:优先调用参数为子类指针(Sub*)的重载版本,且这一过程与“多态”无关(多态不影响重载决议)。
关键原理:重载决议是「编译期行为」,基于实参的「静态类型」
函数重载的匹配逻辑由编译器在编译阶段完成,核心是「匹配优先级」:
- 精确匹配(实参类型 ≡ 形参类型)→ 优先级最高;
- 隐式转换匹配(如
Sub*→Base*)→ 优先级次之; - 其他匹配(如常量转换、提升转换等)→ 优先级更低。
当实参是 Sub* 类型时:
func(Sub*)是精确匹配,直接命中;func(Base*)仅能通过“Sub*隐式转换为Base*”匹配,优先级更低。
因此编译器会直接选择 func(Sub*),而非 func(Base*)。
验证示例1:实参为子类指针
int main() {
Sub* subPtr = new Sub();
func(subPtr); // 实参静态类型是Sub*,精确匹配func(Sub*)
delete subPtr;
return 0;
}
输出:
调用 func(Sub*):Sub::show()
验证示例2:实参为父类指针(指向子类对象)
若实参的静态类型是 Base*(即使动态指向 Sub 对象),编译器仍会匹配 func(Base*):
int main() {
Base* basePtr = new Sub(); // 静态类型Base*,动态类型Sub
func(basePtr); // 匹配func(Base*)
delete basePtr;
return 0;
}
输出:
调用 func(Base*):Sub::show()
这里的 Sub::show() 是多态(运行期根据对象动态类型调用),但「重载决议选择 func(Base*)」是编译期确定的——多态仅影响重载函数内部的虚函数调用,不影响重载函数本身的选择。
三、关键误区澄清:多态不影响重载决议
你提到的“由于多态的存在,两个函数都满足调用”是典型误区,需明确:
| 特性 | 决议阶段 | 核心依据 | 作用范围 |
|---|---|---|---|
| 函数重载 | 编译期 | 实参的静态类型 + 参数列表匹配 | 选择哪个重载函数(函数签名) |
| 多态(虚函数) | 运行期 | 对象的动态类型 | 重载函数内部,虚函数的具体调用 |
简单来说:
- 重载是“选哪个函数”(编译期定);
- 多态是“选中的函数里,虚函数怎么执行”(运行期定)。
即使子类对象同时满足“转父类指针”和“自身指针”,编译器也会优先选更匹配的重载版本,不会因为“多态”而模糊选择。
四、补充场景:无子类指针重载版本时的行为
若仅定义 func(Base*),未定义 func(Sub*),则 Sub* 实参会隐式转换为 Base*,调用 func(Base*):
// 仅保留func(Base*),删除func(Sub*)
void func(Base* b) {
cout << "调用 func(Base*):";
b->show();
}
int main() {
Sub* subPtr = new Sub();
func(subPtr); // Sub*隐式转Base*,调用func(Base*)
delete subPtr;
return 0;
}
输出:
调用 func(Base*):Sub::show()
五、总结
- 重载合法性:父类指针和子类指针作为参数的同名函数,可构成有效重载;
- 重载决议规则:编译期根据实参的静态类型匹配,子类指针实参优先匹配子类指针参数的重载版本(精确匹配 > 隐式转换);
- 多态的作用边界:多态仅影响重载函数内部的虚函数调用,不影响重载函数本身的选择;
- 核心区别:重载是“编译期选函数”,多态是“运行期执行函数内的虚函数”。
简言之:重载看「静态类型+匹配优先级」,多态看「动态类型+虚函数」,二者互不干扰。
总结
子类指针与父类指针作为函数参数差异构成函数重载
具体调用时按照匹配优先级去调用
没有重载时,若有一个父类指针类型,调用时传子类指针会隐式转换
但留有疑问
int 与 int& 前面说存在调用二义性不是有效重载
但int& 与const int& \ 父类指针与之类指针这里又提出匹配优先级说是有效重载了,感觉有点双标欺负人
另外后面看看还有必要钻钻牛角尖再测测父类指针、子类指针时,参数再加上const和&时调用是什么样子
主要是测了也没多大用,瞎废时间也提升不了什么东西

5200

被折叠的 条评论
为什么被折叠?



