函数重载
- 前言
- 一、函数重载概念
- 二、函数重载的分类
- 参数类型不同的函数重载
- 参数个数不同的函数重载
- 参数类型顺序不同的函数重载
- 三、函数重载的具体代码展示
- main.cpp
- 四、为什么为什么C++支持函数重载,而C语言不支持函数重载呢
前言
函数重载是指在同一个作用域内,可以定义多个名称相同但参数列表不同的函数。这些函数具有不同的参数个数、类型或顺序,以便编译器能够根据传入的参数来确定调用哪个函数。函数重载使得代码更加简洁,避免了命名上的冗余,并提高了代码的可读性和可维护性。通过重载,我们可以为不同的操作或数据类型提供统一的接口,使得函数的使用更加灵活和方便。
一、函数重载概念
自然语言中,一个词可以有多重含义,人们可以通过上下文来判断该词真实的含义,即该词被重载了。
比如:以前有一个笑话,国有两个体育项目大家根本不用看,也不用担心。一个是乒乓球,一个是男足。前者是“谁也赢不了!”,后者是“谁也赢不了!”
函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数或类型或类型顺序)不同,常用来处理实现功能类似数据类型不同的问题。
函数重载概念是编程中的一个重要特性,它允许在同一作用域内定义多个同名函数,但这些函数的参数列表(参数的数量、类型或顺序)必须不同。通过这种方式,函数重载为程序员提供了更大的灵活性,使得他们可以根据不同的参数类型和数量来调用不同的函数实现,从而实现功能的多样化和代码的复用。
在C++、Java等面向对象的编程语言中,函数重载是一种常见且有用的编程技巧。通过使用函数重载,开发者可以为类或者命名空间中定义的行为提供多个入口,以适应不同的数据类型或调用场景。例如,一个名为add
的函数可以被重载以处理整数加法、浮点数加法和复数加法等不同类型的数据。
重载函数时,编译器会根据函数调用时传递的参数类型和数量来决定调用哪一个版本的函数。这要求重载的函数在参数特征上必须有所区别,否则会导致编译错误。这种机制使得代码更加清晰易读,同时也提高了代码的复用性和可维护性。
需要注意的是,虽然函数重载提供了很大的便利,但过度使用也可能导致代码难以理解和维护。因此,在设计和实现函数重载时,开发者需要权衡其带来的好处和可能带来的问题,确保代码的可读性和可维护性。
总之,函数重载概念是编程中一种重要的技术,它允许我们根据参数的不同来调用不同的函数实现,从而提高了代码的复用性和灵活性。正确合理地使用函数重载,可以帮助我们编写出更加高效、易读和可维护的代码。
二、函数重载的分类
C++函数重载可以分为以下几种分类:
-
参数个数不同:在同一个作用域中,函数名相同但参数个数不同的多个函数被视为重载函数。
-
参数类型不同:在同一个作用域中,函数名相同但参数类型不同的多个函数被视为重载函数。
-
参数顺序不同:在同一个作用域中,函数名相同但参数顺序不同的多个函数被视为重载函数。
需要注意的是,返回值类型不是函数重载的条件,因为编译器无法通过返回值类型来确定调用哪个重载函数。此外,函数重载必须在同一个作用域内进行,否则编译器无法识别不同作用域中的重载函数。
参数类型不同的函数重载
// 参数类型不同
int Add(int left, int right)
{
cout << "int Add(int left, int right)" << endl;
return left + right;
}
double Add(double left, double right)
{
cout << "double Add(double left, double right)" << endl;
return left + right;
}
参数个数不同的函数重载
//参数个数不同
void f()
{
cout << "f()" << endl;
}
void f(int a)
{
cout << "f(int a)" << endl;
}
参数类型顺序不同的函数重载
//参数类型顺序不同
void f(int a, char b)
{
cout << "f(int a,char b)" << endl;
}
void f(char b, int a)
{
cout << "f(char b, int a)" << endl;
}
注意:像这种有歧义的输入编译器是会报错的,因为编译器不知道你是想要字符型还是ASCII码
三、函数重载的具体代码展示
main.cpp
#include<iostream>
using namespace std;
// 1、参数类型不同
int Add(int left, int right)
{
cout << "int Add(int left, int right)" << endl;
return left + right;
}
double Add(double left, double right)
{
cout << "double Add(double left, double right)" << endl;
return left + right;
}
// 2、参数个数不同
void f()
{
cout << "f()" << endl;
}
void f(int a)
{
cout << "f(int a)" << endl;
}
// 3、参数类型顺序不同
void f(int a, char b)
{
cout << "f(int a,char b)" << endl;
}
void f(char b, int a)
{
cout << "f(char b, int a)" << endl;
}
int main()
{
Add(10, 20);
Add(10.1, 20.2);
f();
f(10);
f(10, 'a');
f('a', 10);
//f('a', 'a');
return 0;
}
四、为什么为什么C++支持函数重载,而C语言不支持函数重载呢
C++支持函数重载,而C语言不支持函数重载的原因是因为它们在语言设计上有不同的目标和考虑。
C语言是一种相对简单的编程语言,它的设计目标是提供一种简洁、高效的工具来进行系统级编程。因此,C语言主要关注的是语言的简洁性和效率,而不是提供过多的语言特性。所以C语言中的函数只能有一个名称,没有函数重载的概念。
相比之下,C++是一种更为复杂和功能更强大的编程语言。它在C语言的基础上添加了许多面向对象的特性,并且支持更高级的编程抽象。其中一个特性就是函数重载。函数重载允许在同一个作用域内定义多个同名函数,但它们的参数类型或数量不同。这样可以方便地编写功能类似但输入输出不同的函数,提高了程序的灵活性和可读性。
除语言设计上有不同的目标和考虑之外,C++支持函数重载的原理是因为C++存在名字修饰(name Mangling)
在C/C++中,一个程序要运行起来,需要经历以下几个阶段:预处理、编译、汇编、链接。
我们以c语言为例,关于具体的编译和链接的过程可看——C语言从入门到实战——编译和链接
- 实际项目通常是由多个头文件和多个源文件构成,而通过C语言从入门到实战——编译和链接这篇文章,我们可以知道,【当前
a.cpp
中调用了b.cpp
中定义的Add
函数时】,编译后链接前,a.o
的目标文件中没有Add
的函数地址,因为Add
是在b.cpp
中定义的,所以Add
的地址在b.o
中。那么怎么办呢? - 所以链接阶段就是专门处理这种问题,链接器看到
a.o
调用Add
,但是没有Add
的地址,就会到b.o
的符号表中找Add
的地址,然后链接到一起。
ps:出现上述情况的原因就是因为编译器在链接的过程中没有找到函数的地址,我们可以检查是不是自己的函数写错了
-
那么链接时,面对
Add
函数,链接接器会使用哪个名字去找呢?这里每个编译器都有自己的函数名修饰规则。 -
由于
Windows
下vs
的修饰规则过于复杂,而Linux
下g++
的修饰规则简单易懂,下面我们使用了g++
演示了这个修饰后的名字。 -
通过下面我们可以看出
gcc
的函数修饰后名字不变。而g++
的函数修饰后变成【_Z+函数长度+函数名+类型首字母】
。-
采用C语言编译器编译后结果
-
结论:在
linux
下,采用gcc
编译完成后,函数名字的修饰没有发生改变。 -
采用
C++
编译器编译后结果
-
结论:在
linux
下,采用g++
编译完成后,函数名字的修饰发生改变,编译器将函数参数类型信息添加到修改后的名字中。 -
Windows
下名字修饰规则
对比Linux
会发现,windows
下vs
编译器对函数名字修饰规则相对复杂难懂,但道理都是类似的,我们就不做细致的研究了。 -
【扩展学习:C/C++ 函数调用约定–里面有对vs下函数名修饰规则讲解】
-
-
通过这里就理解了C语言没办法支持重载,因为同名函数没办法区分。而C++是通过函数修饰规则来区分,只要参数不同,修饰出来的名字就不一样,就支持了重载。
-
如果两个函数函数名和参数是一样的,返回值不同是不构成重载的,因为调用时编译器没办法区分。