观前提示
本篇文章共1891字,读完需要8分钟左右。
写在前面
本篇文章件将帮助你了解C++函数重载的功能,及其原理。相较于C,函数重载作为C++新加的功能,解决了在某些需要频繁调用相同处理方式使用处理不同类型数据的函数时,C语言函数调用复杂或者实现复杂的痛点。希望本篇文章能对你的函数重载学习有所帮助。
概念
在自然语言中,一个词可以有多种含义,碰到这种一词多义的时候,我们常通过上下文来判断该词的真正含义。在日常生活中,这种现象被称为一词多义,而在计算机语言中,则被称为重载。
而其实使用过某种程序设计语言编写过算术表达式的程序员就已经接触过重载了。事实上,如果读者朋友已经有对C++或者其他高级语言的一定了解,就会知道其实那就是操作符重载,不过那是后话了,本篇文章暂且不做过多解释。
//表达式1
1 + 3//调用了针对整型操作数的加法操作符
//表达式2
1.0 + 3.0//实际上调用了另一个专门针对浮点操作数的加法操作符
而对于函数来说,出现在相同作用域中的两个函数,如果具有相同名字而形参不同(参数个数,类型,或类型顺序),则称之为重载函数。
//例如:
int Add(int a, int b)
{
return a + b;
}
double Add(double a, double b)
{
return a + b;
}
注意:任何程序仅有一个main函数的实例。main函数不能重载。
使用
-
参数类型不同
//demo1 int Add(int a, int b) { cout << "int" << endl; return a + b; } double Add(double a, double b) { cout << "double" << endl; return a + b; }
-
参数个数不同
//demo2 int sub(int a, int b) { cout << "a - b" << endl; return a - b; } int sub(int a, int b,int c) { cout << "a - b - c" << endl; return a - b - c; }
-
参数类型顺序不同
//demo3 void test(int a, double b) { cout << "int-double" << endl; //... } void test(double a, int b) { cout << "double-int" << endl; //... }
函数重载与函数重命名的区别
当两个函数的返回类型和形参表完全相同,则第二个函数声明被视为第一个函数的重复声明。如果两个函数的形参表完全相同,但返回类型不同,则第二个声明是错误的。函数是不能仅仅基于不同的返回类型而实现重载的。(试想一下编译器要怎么样才能识别出你调用的是哪个函数呢?)
//demo4
int find(int num)
{
//...
return 1;
}
double find(int num)//error C2556: “double find(int)”: 重载函数与“int find(int)”只是在返回类型上不同
{
//...
return 1.0;
}
除此之外,有些看起来不一样的形参表本质上是相同的:
-
//demo5 void Print(int num) { //... } void Print(int) // parameter names are ignored { //忽略参数名的写法,此函数与上面的函数参数表是一样的 //... } //error C2084: 函数“void Print(int)”已有主体
第一个函数声明给形参命了名。形参名只是帮助文档,第二个函数形参表中依旧存在此参数类型,并没有修改形参表。
-
typedef char newchar; void Print(char) { //... } void print(newchar) // char and newchar are the same type { //... } //error C2084: 函数“void Print(char)”已有主体
对同一类型重命名,实际类型相同,还是重命名。
-
void Print(int, const char*) { //... } // default argument doesn't change the number of parameters // 缺省参数并没有改变参数表 void Print(int, const char* = "") { //,.. } //error C2084: 函数“void Print(int,const char *)”已有主体
两个函数的形参列表不同的只有第二个函数有默认实参(缺省参数)。对于第二个函数来说,默认实参并没有改变形参的个数,亦没有改变类型。无论实参是由用户还是由编译器提供的,这个函数都带有两个实参。
-
// const is irrelevent for nonreference parameters // 在这种情况下const修饰对于来说限制实参传递是没有用的 void Print(short) { //... } void Print(const short) // redeclaration { //... } //error C2084: 函数“void Print(short)”已有主体
两个函数的区别在于是否使用
const
修饰形参。这种差异并不影响传递至函数的对象。这是因为此时传递给函数的只是实参的一份拷贝,而这份拷贝不需要考虑形参是否有const
修饰,因为函数操纵的只是实参的副本,无法修改实参。所以,这两种形参,既可以传const
对象,亦可以传非const
对象,并无本质区别。那么,对于能限制到实参传递的形参,也就是引用形参,是可以通过
const
区分传递的实参是否为const
对象的。也就是说,此时是能够通过函数形参是否被const
修饰来实现函数重载的。类似的,指针形参也同理可以。
函数重载实现的本质——名字修饰(name Mangling)
首先,我们先了解一下C/C++是如何做到识别全局域中的函数的。在C/C++中,编译一个文件需要经历以下几个阶段:预处理、编译、汇编、链接。以下是一个.c文件编译成一个可执行文件的过程。
可以看到,在进行汇编的时候生成了一个叫做符号表的东西,对于C来说,符号表记录函数的信息只有函数名称及其地址(源文件只存在声明的函数,也会生成地址,最后地址链接时进行重定位)。而在链接阶段进行符号表合并与重定位时,C明显不具备处理同名函数的能力,在此处遇到函数名相同的函数便会直接报函数重命名的错误。
对于C++来说,符号表相较于C增加了名字修饰的功能。函数名字修饰时会使用参数表的信息修饰。此时,C++编译链接过程中就可以区分出同名函数。由于每个编译器都有自己的函数名字修饰的规则,且由于编译器规则十分复杂,感兴趣可以搜索Linux下g++的修饰规则,比较简单易懂。
所以,C++通过函数名字修饰规则来区分参数不同的同名函数,也就支持了函数重载。
结语
以上就是C++的函数重载的讲解,如果你觉得做的还不错的话请点赞收藏加分享,当然如果发现我写的有错误或者有建议给我的话欢迎在评论区或者私信告诉我。此外,关于名字修饰的讲解,本文并没有贴出例子,可能会比较难以理解,有需要的可以自行搜索。当然,我以后可能也会再写一篇来补充。
彩蛋
文章源代码