函数重载之多态场景

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

函数重载时,如果重载函数参数分别为父类指针类型与子类指针类型,那么此时这两个函数可以形成重载吗,如果实参传子类对象,由于多态的存在,两个函数按道理来说都满足调用,此时是什么情况呢,详细说明一下


要彻底理解这个问题,需要先区分函数重载的核心规则多态的作用机制,这是两个不同阶段(编译期 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*)的重载版本,且这一过程与“多态”无关(多态不影响重载决议)。

关键原理:重载决议是「编译期行为」,基于实参的「静态类型」

函数重载的匹配逻辑由编译器在编译阶段完成,核心是「匹配优先级」:

  1. 精确匹配(实参类型 ≡ 形参类型)→ 优先级最高;
  2. 隐式转换匹配(如 Sub*Base*)→ 优先级次之;
  3. 其他匹配(如常量转换、提升转换等)→ 优先级更低。

当实参是 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()

五、总结

  1. 重载合法性:父类指针和子类指针作为参数的同名函数,可构成有效重载;
  2. 重载决议规则:编译期根据实参的静态类型匹配,子类指针实参优先匹配子类指针参数的重载版本(精确匹配 > 隐式转换);
  3. 多态的作用边界:多态仅影响重载函数内部的虚函数调用,不影响重载函数本身的选择;
  4. 核心区别:重载是“编译期选函数”,多态是“运行期执行函数内的虚函数”。

简言之:重载看「静态类型+匹配优先级」,多态看「动态类型+虚函数」,二者互不干扰。


总结

子类指针与父类指针作为函数参数差异构成函数重载
具体调用时按照匹配优先级去调用
没有重载时,若有一个父类指针类型,调用时传子类指针会隐式转换

但留有疑问
int 与 int& 前面说存在调用二义性不是有效重载

但int& 与const int& \ 父类指针与之类指针这里又提出匹配优先级说是有效重载了,感觉有点双标欺负人

另外后面看看还有必要钻钻牛角尖再测测父类指针、子类指针时,参数再加上const和&时调用是什么样子
主要是测了也没多大用,瞎废时间也提升不了什么东西

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值