C++函数重载

本文介绍了C++中的函数重载概念,强调了重载与overwrite、override的区别,并详细讨论了常量性(const)如何影响函数重载。特别是,成员函数的常量性、引用和指针所指对象的常量性在函数重载中的作用。此外,还提到了引用在函数重载中的潜在二义性问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

什么是函数重载

  函数重载是C++的重要特性,他允许我们在参数列表不同的前提下定义同名函数。在函数调用时将编译器将根据函数名与调用时的参数类型决定调用的具体函数。

/*实现一个add()函数能够同时对整数和字符串做加法。*/

using namespace std;

//版本一:整数加法
int add(int l, int r) {
    return l+r;
}

//版本二:字符串拼接
const string& add(const string& l, const string& r) {
    string ret(l);
    ret.append(r);
    return ret;
}

函数的overload、overwrite和override

  无论是overwrite还是override都是在面向对象的继承过程中出现的现象。关于他们的关系和区别很多资料可以查找,这是一篇不错的参考博文。此处不做赘述。
  
  需要特别强调一点!如果派生类override或者overwrite了一个基类的函数,那么基类中该函数的所有重载版本在派生类中均不可见。即使我们的目的只是在派生类中重新实现基类成员函数的一个重载版本,我们也必须重新定义所有的重载函数(当然其实现可以是直接调用基类对应的成员函数)。

/*基类中同名函数各个重载版本在派生类中的可见性总是一致的*/

class Base {
public:
    //版本一
    void Func(int i);
    //版本二
    virtual void Func(const char * p_chr);
};

class Derived : public Base {
    //override基类Func()函数的第二个重载版本
    void Func(const char *p_chr) override;
};

int i;
const char *p = "hello";
Base().Func(i);    //正确。调用基类版本一
Base().Func(p);    //正确。调用基类版本二
Derived().Func(i); //正确。调用派生类override版本
Derived().Func(p); //错误。派生类中没有参数列表对应的函数声明。

常量性(const)与函数重载

  参数仅常量性不同是否可以重载?
  
  不一定!成员函数的常量性不同可以重载;参数为引用时常量性不同可以重载;指针所指对象的常量性不同可以重载;只有传值参数以及指针本身的常量性不同时是不可以重载的。
  
  以下情况,仅常量性不同的重载是合法的

//成员函数的常量性不同,可以重载
class C {
public:
    void FuncA(int i);
    void FuncA(int i) const;
};

//参数类型为引用时,常量性不同可以重载
void FuncB(int& i);
void FuncB(const int& i);

//指针所指对象的常量性不同,可以重载
void FuncC(int *p);
void FuncC(const int *p);

  成员函数的常量性可以认为是指针所指对象的常量性,当成员函数为const类型则说明调用该方法的类实例为const,相当于this指针为指向常量的指针。总结下来,引用和指针所指对象的常量性不同可以重载。(有些人将引用理解为指向对象的常量指针,这样一来剩下两种情况还可以进一步理解为:只有指针所指对象的常量性不同时可以重载。)
  
  以下情况,仅常量性不同不可以重载

//传值调用形参常量性不同,不可以重载
void FuncD(int i);
void FuncD(const int i);

//指针本身的常量性不同,不可以重载
void FuncE(int *p);
void FuncE(int * const p);

  实际上以上两种情况也可以归为一种。当我们使用传址调用(传指针)时,可以被看做是传递指针值得传值调用,指针自身的常量性不同不能重载。总结来看即为:非引用类型的参数本身的常量性不同不能重载

  那么,为什么非引用类型参数的常量性不同不能重载呢?C++的这样设计的道理何在?这还得从C语言说起,C++出于与C语言保持兼容的考虑,保留了C语言中的传值调用(参考Python、Java这些面向对象语言,他们根本没有这个概念,从C++的角度来看它们的函数调用都是引用传参)。而在传值调用过程中,由于函数内部总会在执行函数体之前复制一个实参副本,并在函数体内使用副本参与运算。这就导致实参本身(实参为指针时,指针所指对象就不一定不改变了。)在函数内部永远不会改变,函数返回时实参的值总是调用时实参的值。既然如此,那么实参是否为const类型根本无关紧要,即使不是const类型函数返回时也一定不会变。

  相反地,引用和指针所指对象的常量性则显得很重要了!因为这些对象在函数内部是有可能被改变的。
  

引用与函数重载

  引用和非引用是否可以重载?
  
  可以,但是(一般“但是”后面的才是重点)这很容易出现二义性错误。一般来说也没有这样重载的必要。

//定义两个函数,一个是引用,一个是传值
//版本一:非引用
void FuncV(double d) {
    //do something
} 

//版本二:引用
void FuncV(double & d) {
    //do something else
}

//目前为止,函数的声明和定义没有错误

int i = 1;
double d = 1.1;

/*以下调用正确。没有完全匹配的重载版本,但通过自动类型转化生成了临时
 *的double对象。这是一个右值对象,只能匹配版本一和版本二无法匹配。*/
FuncV(i);

/*错误,同名函数的两个重载版本均完全匹配。编译器无法决定使用哪一个*/
FuncV(d);       

  诚然,错误出现在函数调用者。但事实上这是函数设计者在“坑队友”,因为上述的重载函数设计几乎没有办法在实参为double时被正常的调用,总是会出现二义性。当然,“几乎没有”的意思就是方法还是有的。

  强制类型转化可以让上一段代码中的最后一个调用生效。(来自Stack Overflow)

//调用版本一
static_cast<void (*)(double)>(FuncV)(d);

//调用版本二
static_cast<void (*)(double &)>(funcV)(d);

  另外左值引用和右值引用也是可以重载的。不过右值引用目前还没弄透彻就不展开了。此处先打卡。

【参考资料】
  1. Stanley B.Lippman, Josee Lajoie, Barbara E.Moo 著, 王刚 杨巨峰 译, C++ Primer(第五版)中文版, 电子工业出版社。
  2. jszhangyili的博客,C++中 overload 、override、overwrite 之间的区别
  3. Scott Meyers 著, 侯捷 译,Effective C++(第三版)中文版,电子工业出版社。

### C++函数重载的概念 在 C++ 编程语言中,允许多个函数具有相同的名称,前提是这些函数数列表有所不同。这种特性被称为函数重载 (Function Overloading)[^2]。 通过函数重载,同一个函数名可以根据不同的输数执行不同的操作,从而提高代码可读性和灵活性。需要注意的是,返回值类型的差异不足以构成有效的函数重载条件;只有当形的数量、类型或顺序存在区别时才能形成合法的重载版本[^1]。 ### 实现方式与注意事项 为了实现函数重载,在定义同名的不同函数时需确保各函数之间至少有一个形式数上的差别: - 数数量不同; - 数数据类型各异; - 数次序有所变化。 如果两个函数仅因返回类型相异,则不会被视作有效重载,并可能导致编译错误[^3]。 ### 示例代码展示 下面给出几个简单的例子来说明如何正确运用这一机制: ```cpp #include <iostream> using namespace std; // 不同数数目 void display() { cout << "无数" << endl; } void display(int num) { cout << "整数:" << num << endl; } // 不同数类型 double add(double a, double b){ return a+b; } int add(int a,int b){ return a+b; } // 同一作用域内不允许仅有返回值类型不同 /* 错误示范 */ /* float divide(float dividend,float divisor); int divide(int dividend,int divisor); // 这里会引发冲突 */ int main(){ int i=5,j=7,k; float f1=2.0,f2=3.0,g; k=add(i,j); g=add(f1,f2); cout<<"Integer Sum:"<<k<<endl; cout<<"Float Sum:"<<g<<endl; return 0; } ``` 上述实例展示了基于数数量以及数类型的两种常见情况下的函数重载应用案例。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值