C++重载函数的用法(非常详细)
在 C++ 程序中,允许定义多个名称相同的函数,比如:
在 C++ 中,只要每个重载函数的参数列表不同,编译器就能正确地识别并调用正确的函数版本,从而避免混淆。
有人可能会问,一个函数名表示一个功能不好吗?为什么要用同一个函数名表示多个功能呢?这样不会造成混淆吗?
回到刚才的例子,如果我们想要在程序中实现“加法”,可以定义一个 Add() 函数:
同样是用于相加这个运算,却有着不同的函数名,这使得我们在调用这些函数时,不得不人为地根据具体情况选择合适的函数。这不仅非常麻烦,而且如果选择错误,还会导致结果出错。
C++ 中的函数重载机制就是用来解决这个问题的,该机制允许拥有相似功能的函数使用相同的函数名,而在实际调用时,编译器会根据实际参数的个数和数据类型进行匹配,最终确定调用哪个函数。这样就省去了选择函数的麻烦,避免了人为选择函数可能带来的错误,同时也使代码的形式更加统一。
准确来说,几个同名函数实现的功能相似但参数的个数或类型不同,就构成了函数重载,这些函数被称为重载函数。
在输出结果中,我们可以清楚地看到,第一次对 Add() 函数的调用,实际上执行的是 int 类型版本
为什么同样是对 Add() 函数的调用,两次执行的却是不同的函数呢?
因为主函数在第一次调用 Add() 函数时,给出的实际参数 2 和 3 的类型为 int 类型,而在 Add() 函数的两个重载版本中,只有第一个 int 类型版本与之匹配,不仅参数类型相同,同时参数个数也相同,所以执行第一个 int 类型版本的 Add() 函数。
同样,在第二次用 2.5 和 10.3 作为实际参数调用 Add() 函数时,因为小数常数在程序中被认定为 double 类型,所以编译器会找到第二个 double 类型版本的 Add() 函数与它匹配,所以最终会执行第二个 Add() 函数。
函数重载允许我们以统一的代码形式调用功能相近的多个函数,这样可以使代码更加简洁,同时省去选择函数的麻烦。
那么,什么时候需要使用函数重载呢?只要发现程序中有多个功能相似的函数,只是处理的数据不同,就可以使用函数重载。换句话说,只要多个函数表达的操作相同而操作的对象不同,就可以使用相同的函数名来表示相同的操作,通过使用不同的参数来表示不同的操作对象。
需要注意的是,不能将不同功能的函数定义为重载函数,虽然这样做在语法上是正确的,但这样做违背了“函数名应准确表达函数功能”的原则,可能导致误用函数。
例如:
但需要特别注意的是,如果两个函数只是返回值类型不同,则不能构成函数重载,例如:
定义了正确的重载函数后,在调用这些重载函数时,编译器将根据我们在调用时给出的实际参数找到与之匹配的重载函数的正确版本。
首先,编译器会进行严格的参数匹配,包括参数的数据类型和个数。如果编译器发现某个重载函数的参数类型和个数与函数调用时的实际参数相匹配,则优先调用这个重载函数。
例如:
如果编译器无法找到严格匹配的重载函数,它将尝试将参数类型转换为更高精度的类型进行匹配。例如,将 char 类型转换为 int 类型,将 float 类型转换为 double 类型等。如果转换类型后的参数能够找到与之匹配的重载函数,则调用此重载函数。
例如:
需要注意的是,参数可以从低精度的数据类型转换为高精度的数据类型进行匹配,但并不意味着高精度的数据类型也可以转换为低精度的数据类型进行匹配。由高精度到低精度的转换不仅会损失数据精度,更重要的是,由于转换过程中可能存在多种选择。
例如,一个 double 类型的参数既可以转换为 int 类型,也可以转换为 short 类型,这会导致编译器无法确定调用哪个版本的重载函数,从而无法完成重载。
除理解重载函数的匹配原则外,还需注意带有默认参数值的函数可能会给函数重载带来麻烦。例如,有如下两个重载函数:
因此在重载函数中,应该尽量避免使用默认参数,以便编译器能够准确无误地找到匹配的重载函数。
int Add(); int Add(int a, int b); int Add(float a,float b); int Add(int a,int b,int c);这是通过函数重载实现的,虽然这些 Add() 函数的名字相同,但它们处理的参数类型和个数不同,这使得编译器能够根据参数类型区分应该使用哪一个函数。
在 C++ 中,只要每个重载函数的参数列表不同,编译器就能正确地识别并调用正确的函数版本,从而避免混淆。
C++重载函数的声明
重载(overload)函数是指让具有相似功能的函数拥有相同的函数名。换句话说,就是同一个函数名可以用于功能相似但又各有差异的多个函数。这样做的好处是可以通过相同的函数名表示类似的操作,增强代码的可读性和一致性。有人可能会问,一个函数名表示一个功能不好吗?为什么要用同一个函数名表示多个功能呢?这样不会造成混淆吗?
回到刚才的例子,如果我们想要在程序中实现“加法”,可以定义一个 Add() 函数:
int Add(int a, int b) { return a + b; }这个 Add() 函数可以计算两个整数之和。然而,我们在程序中可能还需要计算其他类型数据的和,例如两个浮点数之和,而原来的 Add() 函数无法用于计算浮点之和。为此,我们不得不实现另一个 AddFloat() 函数来完成两个浮点数相加的计算:
float AddFloat(float a, float b) { return a + b; }AddFloat() 函数可以暂时解决问题,然而我们可能还会遇到更多种类型数据的相加,于是程序中就可能出现 AddDouble()、AddString()、AddHuman() 等一系列用于相加的函数。
同样是用于相加这个运算,却有着不同的函数名,这使得我们在调用这些函数时,不得不人为地根据具体情况选择合适的函数。这不仅非常麻烦,而且如果选择错误,还会导致结果出错。
C++ 中的函数重载机制就是用来解决这个问题的,该机制允许拥有相似功能的函数使用相同的函数名,而在实际调用时,编译器会根据实际参数的个数和数据类型进行匹配,最终确定调用哪个函数。这样就省去了选择函数的麻烦,避免了人为选择函数可能带来的错误,同时也使代码的形式更加统一。
准确来说,几个同名函数实现的功能相似但参数的个数或类型不同,就构成了函数重载,这些函数被称为重载函数。
C++重载函数的使用
接下来,我们将介绍如何使用函数重载来解决上述 Add() 函数所遇到的问题:// 定义第一个Add()函数,使其可以计算两个 int 类型的数据之和 int Add(int a, int b) { cout << "int Add( int a, int b )被调用!" << endl; return a + b; } // 重载Add()函数,对其重新定义 // 使其可以计算两个 double 类型的数据之和 double Add(double a, double b) { cout << " double Add( double a, double b )被调用!" << endl; return a + b; } int main() { // 因为参数是整数类型,其类型、个数与 int Add( int a, int b )匹配 // 所以 int Add( int a, int b )被调用 int nSum = Add(2, 3); cout << " 2 + 3 = " << nSum << endl; // 作为参数的小数会被表示成 double 类型 // 参数的类型、个数与 double Add(double a, double b )匹配 // 所以 double Add( double a, double b )被调用 double fSum = Add(2.5, 10.3); cout << " 2.5 + 10.3 = " << fSum << endl; return 0; }实现了函数重载,在实际调用函数时,编译器会根据不同的参数类型和参数个数调用与之匹配的重载函数。虽然在代码中调用的都是 Add() 函数,但实际调用的却是重载函数的不同实现版本,从而得到正确的结果:
int Add( int a, int b )被调用!
2 + 3 = 5
double Add( double a, double b )被调用!
2.5 + 10.3 = 12.8
在输出结果中,我们可以清楚地看到,第一次对 Add() 函数的调用,实际上执行的是 int 类型版本
int Add( int a, int b )
,而第二次调用执行的是 double 类型版本double Add( double a, double b)
。为什么同样是对 Add() 函数的调用,两次执行的却是不同的函数呢?
因为主函数在第一次调用 Add() 函数时,给出的实际参数 2 和 3 的类型为 int 类型,而在 Add() 函数的两个重载版本中,只有第一个 int 类型版本与之匹配,不仅参数类型相同,同时参数个数也相同,所以执行第一个 int 类型版本的 Add() 函数。
同样,在第二次用 2.5 和 10.3 作为实际参数调用 Add() 函数时,因为小数常数在程序中被认定为 double 类型,所以编译器会找到第二个 double 类型版本的 Add() 函数与它匹配,所以最终会执行第二个 Add() 函数。
函数重载允许我们以统一的代码形式调用功能相近的多个函数,这样可以使代码更加简洁,同时省去选择函数的麻烦。
那么,什么时候需要使用函数重载呢?只要发现程序中有多个功能相似的函数,只是处理的数据不同,就可以使用函数重载。换句话说,只要多个函数表达的操作相同而操作的对象不同,就可以使用相同的函数名来表示相同的操作,通过使用不同的参数来表示不同的操作对象。
需要注意的是,不能将不同功能的函数定义为重载函数,虽然这样做在语法上是正确的,但这样做违背了“函数名应准确表达函数功能”的原则,可能导致误用函数。
重载函数的解析
我们知道,编译器通过参数的类型和个数来区分重载函数的不同版本。因此,相同函数名的多个函数,只有在参数类型或个数上存在差异,才可以构成合法的重载函数的不同版本。例如:
// 参数类型不同构成合法的函数重载 int max(int a, int b); float max(float a, float b); double max(double a, double b);以上三个函数分别接受两个 int、float 和 double 类型的参数,具有不同的参数类型,因此可以构成合法的函数重载。
但需要特别注意的是,如果两个函数只是返回值类型不同,则不能构成函数重载,例如:
// 只是函数返回值不同,不能构成合法的函数重载 int max(int a, int b); float max(int a, int b);这是因为函数调用时,返回值并非总是必需的。如果只是单纯调用函数而没有返回值,编译器就无法确定应调用哪一个版本的重载函数。
定义了正确的重载函数后,在调用这些重载函数时,编译器将根据我们在调用时给出的实际参数找到与之匹配的重载函数的正确版本。
首先,编译器会进行严格的参数匹配,包括参数的数据类型和个数。如果编译器发现某个重载函数的参数类型和个数与函数调用时的实际参数相匹配,则优先调用这个重载函数。
例如:
// 实际参数是两个int类型的数据 // 与重载函数int max(int a,int b)的参数类型和个数相匹配 // 所以int max(int a,int b)被调用 int nMax = max(1, 2);这里的实际参数 1 和 2 都是 int 类型,参数个数是两个,这与
int max(int a, int b)
这个重载函数严格匹配,所以这里调用的是 int 版本的 max() 重载函数。如果编译器无法找到严格匹配的重载函数,它将尝试将参数类型转换为更高精度的类型进行匹配。例如,将 char 类型转换为 int 类型,将 float 类型转换为 double 类型等。如果转换类型后的参数能够找到与之匹配的重载函数,则调用此重载函数。
例如:
// 调用int max(int a,int b) char cMax = max('a','A');这里的实际参数 'a' 和 'A' 是 char 类型的,并没有与之匹配的重载函数。编译器会尝试将 char 类型的参数转换为更高精度的 int 类型,因此会找到
int max(int a, int b)
与之匹配。结果是实际调用的是 int 类型版本的 max() 重载函数。需要注意的是,参数可以从低精度的数据类型转换为高精度的数据类型进行匹配,但并不意味着高精度的数据类型也可以转换为低精度的数据类型进行匹配。由高精度到低精度的转换不仅会损失数据精度,更重要的是,由于转换过程中可能存在多种选择。
例如,一个 double 类型的参数既可以转换为 int 类型,也可以转换为 short 类型,这会导致编译器无法确定调用哪个版本的重载函数,从而无法完成重载。
除理解重载函数的匹配原则外,还需注意带有默认参数值的函数可能会给函数重载带来麻烦。例如,有如下两个重载函数:
// 比较两个整型数的大小 int max(int a, int b); // 比较三个整型数的大小,第三个数的默认值是0 int max(int a, int b , int c = 0);当我们以
int nMax = max(1, 2);
的形式尝试调用这个重载函数时,我们无法从函数调用表达式中确定它到底是只有两个参数,还是拥有三个参数(只不过第三个参数使用了默认参数),这就使得这个调用与两个重载函数都能严格匹配,这时编译器就不知道到底该调用哪个版本的重载函数。因此在重载函数中,应该尽量避免使用默认参数,以便编译器能够准确无误地找到匹配的重载函数。