C++基础整理(6)之函数重载、函数默认参数
注:整理一些突然学到的C++知识,随时mark一下
例如:忘记的关键字用法,新关键字,新数据结构
函数重载、函数默认参数的用法整理
提示:本文为 C++ 中常用的 重载 和 默认参数值 的用法和举例
一、函数重载(overload)
在C++中的**函数重载(Function Overloading)**是指可以定义多个同名的函数,但这些函数的参数列表(参数类型、参数个数或参数顺序,至少一方)必须不同。这样,编译器就可以根据提供的参数类型和数量来确定调用哪个函数。函数重载允许我们使用相同的函数名来表示不同的函数实现,这增强了代码的可读性和可维护性。
1、函数重载的使用示例——传参类型不同的重载
下面是一个简单的函数重载示例来实现基于不同数据类型的数据打印功能:
// 第一个版本,接受一个整数参数
void print(int n) {
std::cout << "Integer: " << n << std::endl;
}
// 第二个重载版本,接受一个浮点数参数
void print(double f) {
std::cout << "Double: " << f << std::endl;
}
// 第三个重载版本,接受一个字符串参数
void print(const char* str) {
std::cout << "String: " << str << std::endl;
}
int main() {
// 调用不同版本的print函数
print(10); // 调用接受整数的版本
print(3.14); // 调用接受浮点数的版本
print("Hello"); // 调用接受字符串的版本
return 0;
}
在这个例子中,我们定义了三个名为print的函数,每个函数接受不同类型的参数:一个接受int,一个接受double,还有一个接受const char*。当我们在main函数中调用print时,编译器会根据提供的参数类型自动选择正确的函数函数版本。
需要注意的是,函数重载只是基于参数列表的不同,而不是基于返回类型的不同。也就是说,不能仅仅通过改变返回类型来重载一个函数。此外,函数重载不考虑函数内部的实现或函数体,只考虑(函数名和参数列表)。
重载函数时,还应避免引起歧义的情况。例如,如果你有一个接受int的函数和一个接受double的函数,那么传递一个float类型的参数将会导致编译错误,因为编译器不能确定应该调用哪个版本。在这种情况下,你可能需要提供一个接受float参数的函数重载版本,或者显式地将float参数转换为int或double。
又例如:可以通过函数重载来实现一个max函数,该函数可以接受int、double和float类型的参数。每个重载版本都将返回相应类型的最大值。下面是一个简单的例子:
// 重载max函数,接受两个int参数
int max(int a, int b) {
return (a > b) ? a : b;
}
// 重载max函数,接受两个double参数
double max(double a, double b) {
return (a > b) ? a : b;
}
// 重载max函数,接受两个float参数
float max(float a, float b) {
return (a > b) ? a : b;
}
int main() {
// 测试int版本的max函数
int maxInt = max(10, 20);
std::cout << "Max int: " << maxInt << std::endl;
// 测试double版本的max函数
double maxDouble = max(3.14, 2.71);
std::cout << "Max double: " << maxDouble << std::endl;
// 测试float版本的max函数
float maxFloat = max(1.2f, 0.9f);
std::cout << "Max float: " << maxFloat << std::endl;
return 0;
}
在这个例子中,我们定义了三个max函数,每个函数都接受两个相同类型的参数(int、double或float),并返回该类型的最大值。在main函数中,我们分别调用了传参类型不同三个版本的max函数,并打印了结果。
如果想要一个更通用的解决方案,可以使用模板函数(template)来避免显式地重载每一个类型。但是,模板函数不能用于内建类型重载时的隐式类型转换,因此如果你需要隐式类型转换,你还是需要显式地重载这些函数。然而,对于自定义类型,模板函数通常是一个很好的选择,关于template的概念这里不深入。
2、函数重载的使用示例——传参数量不同的重载
下面是一个简单的函数重载示例来实现基于具有相同名称但接受不同数量参数的函数重载。其中sum函数被重载以接受不同数量的整数参数:
// 重载sum函数,接受两个int参数
int sum(int a, int b) {
return a + b;
}
// 重载sum函数,接受三个int参数
int sum(int a, int b, int c) {
return a + b + c;
}
// 重载sum函数,接受四个int参数
int sum(int a, int b, int c, int d) {
return a + b + c + d;
}
int main() {
// 调用接受两个参数的sum函数
int twoSum = sum(1, 2);
std::cout << "Sum of two numbers: " << twoSum << std::endl;
// 调用接受三个参数的sum函数
int threeSum = sum(1, 2, 3);
std::cout << "Sum of three numbers: " << threeSum << std::endl;
// 调用接受四个参数的sum函数
int fourSum = sum(1, 2, 3, 4);
std::cout << "Sum of four numbers: " << fourSum << std::endl;
return 0;
}
在这个例子中,我们定义了三个版本的sum函数,分别接受两个、三个和四个整数参数。每个版本的函数都计算并返回传入参数的总和。在main函数中,我们分别调用了这三个版本的sum函数,并打印了结果。
3、函数重载与函数重写的区别
重载(overload)指的是使用相同的函数名,但具有不同参数列表(包括参数的类型、数量或顺序)的函数实现方式。尽管这些函数的返回值可能不同,但返回值本身并不能作为区分不同重载函数的依据。
重写(overwrite)通常发生在子类继承父类的情境中,指的是子类使用与父类相同的函数名和参数列表,但实现不同的方法体。这种现象也称为方法的覆盖或重写。当子类对象调用这个被重写的方法时,将执行子类中的实现,而不是父类中的。
4、函数重载的实现机制:静态绑定
函数重载是编译器在编译时根据函数的参数列表对同名函数进行区分,会用“名称修饰”的机制,从而区分不同的函数实现。例如,如果有两个函数function func(int integer)
和function func(string str)
,编译器可能会为它们生成类似int_func
和str_func
的内部修饰后的名称。重载函数的调用在编译时就已经确定,这是一种静态绑定。因此重载与类的多态性无关。
函数重写则与类的多态性紧密相关。当子类重写父类的虚函数后,通过父类指针(或引用)调用该函数时,将根据实际指向的子类对象动态地调用相应的子类实现。这种调用在编译时无法确定,因为具体调用的子类虚函数地址只有在运行时才能确定。这种绑定方式称为动态绑定或晚绑定。
二、函数的默认参数
在C++中,函数的默认参数允许你在定义函数时为参数赋予 默认值。如果在调用函数时没有提供该具有默认值的参数的新值,那么该参数将使用其默认值。这提供了一种灵活的方式来调用函数。
1、默认参数的使用
下面是一个使用默认参数的函数示例:
// 函数定义,其中b和c有默认参数
void func(int a, int b = 10, int c = 20) {
std::cout << "a: " << a << ", b: " << b << ", c: " << c << std::endl;
}
int main() {
// 调用func,只提供a的值,b和c使用默认值
func(5);
// 输出: a: 5, b: 10, c: 20
// 调用func,提供a和b的值,c使用默认值
func(5, 15);
// 输出: a: 5, b: 15, c: 20
// 调用func,提供所有参数的值
func(5, 15, 25);
// 输出: a: 5, b: 15, c: 25
return 0;
}
在这个例子中,func函数有三个参数:a、b和c。b和c都有默认值,分别是10和20。在main函数中,展示了如何以不同方式调用func,既可以提供所有参数的值,也可以只提供部分参数的值,让未提供的参数使用默认值进行函数实现。
2、默认参数需要注意的点
(1)默认参数只能从最右向左依次定义,也就是说,你不能为一个参数指定默认值,而它右面的参数没有默认值。例如,下面的函数定义是错误的:
void wrongFunc(int a = 10, int b); // 错误:b没有默认值,但a有默认值
(2)如果一个函数有多个重载版本,并且其中一个重载版本使用了默认参数,那么编译器在解析函数调用时可能会遇到歧义(二义性)。必须避免这种情况,必须禁止使用函数重载和默认参数同时出现的组合。
(3)默认参数通常是在函数声明中定义的,而不写在函数定义中。这意味着通常会在头文件中声明带有默认参数的函数,并在源文件中提供函数定义。当函数定义给的默认值与声明时给的默认值不一致时,以声明的部分为准。
(4)默认参数是静态的,这意味着它们的默认值在编译时确定,并且在程序运行期间不会改变。
3、一个有重载的例子
定义一个calculate函数,该函数用于执行不同类型的数学运算,根据传入的参数数量和类型来确定:
// 基本重载:接受两个整数,执行加法
int calculate(int a, int b) {
return a + b;
}
// 重载:接受两个浮点数,执行加法
double calculate(double a, double b) {
return a + b;
}
// 重载:接受三个整数,执行加法与乘法组合
int calculate(int a, int b, int c) {
return (a + b) * c;
}
// 重载:接受字符串和两个整数,执行特定计算(例如,计算a的b次方)
double calculate(const std::string& operation, int a, int b) {
if (operation == "pow") {
return static_cast<double>(std::pow(a, b));
}
throw std::invalid_argument("Invalid operation");
}
// 重载:接受字符串、两个浮点数以及一个可选的默认布尔值,用于控制精度
double calculate(const std::string& operation, double a, double b, bool precise ) {
if (operation == "add") {
return a + b;
} else if (operation == "subtract") {
return a - b;
} else if (precise && operation == "multiply") {
// 使用高精度乘法(这里仅为示例,实际实现可能更复杂)
return a * b * 2;
} else {
// 使用默认精度乘法
return a * b;
}
}
int main() {
// 调用两个整数的加法重载
int sumInts = calculate(2, 3);
std::cout << "Sum of integers: " << sumInts << std::endl;
// 调用两个浮点数的加法重载
double sumDoubles = calculate(2.5, 3.5);
std::cout << "Sum of doubles: " << sumDoubles << std::endl;
// 调用三个整数的重载,执行加法与乘法组合
int combinedInts = calculate(2, 3, 4);
std::cout << "Combined integers: " << combinedInts << std::endl;
// 调用字符串和两个整数的重载,计算a的b次方
double powResult = calculate("pow", 2, 3);
std::cout << "2 to the power of 3: " << powResult << std::endl;
// 调用字符串和两个浮点数的重载,执行加法
double addDoubles = calculate("add", 5.5, 2.2);
std::cout << "Sum of doubles with string operation: " << addDoubles << std::endl;
// 调用字符串、两个浮点数以及一个布尔值的重载,执行乘法(带精度控制)
double preciseMultiply = calculate("multiply", 4.5, 3.14, true);
std::cout << "Precise multiplication: " << preciseMultiply << std::endl;
return 0;
}
在这个例子中,我们展示了多种形式的函数重载:
基于参数数量的重载:例如,calculate(int a, int b)
和calculate(int a, int b, int c)
。
基于参数类型的重载:例如,整数版本的calculate和浮点数版本的calculate。
使用字符串参数作为标识的重载:例如,calculate(const std::string& operation, int a, int b)
允许我们传入一个字符串来指定执行哪种运算。
使用bool变量的重载:calculate(const std::string& operation, double a, double b, bool precise )
展示了如何使用bool变量来控制函数的行为。