函数重载
这绝对是我最喜欢的特性之一!
同一个函数名,可以有多个版本,只要参数列表不一样就行:
// 两个int相加
int add(int a, int b) {
return a + b;
}
// 三个int相加
int add(int a, int b, int c) {
return a + b + c;
}
// 两个double相加
double add(double a, double b) {
return a + b;
}
调用的时候,编译器会根据你传的参数自动选对应的版本:
add(1, 2); // 调用第一个
add(1, 2, 3); // 调用第二个
add(1.5, 2.5); // 调用第三个
C语言里怎么办? 得给每个函数起不同的名字:add_int、add_double、add_int3…又丑又难记。 重载的规则:
函数名必须相同
参数列表必须不同(个数不同、类型不同、顺序不同)
返回值不同不算重载!
int func(int a);
void func(int a); // 错误!返回值不同不算重载
默认参数
函数参数可以有默认值,调用的时候不传就用默认的:
// 声明默认参数
void print(int a, int b = 10, int c = 20) {
cout << a << " " << b << " " << c << endl;
}
print(1); // 输出:1 10 20
print(1, 2); // 输出:1 2 20
print(1, 2, 3); // 输出:1 2 3
默认参数的规则:
默认参数必须从右往左依次定义,不能中间跳一个
默认参数在声明里写,定义里别重复写(如果声明和定义分开的话)
默认参数的值必须是编译期能确定的(常量、全局变量、函数调用等)
// 错误!中间的b没有默认参数,c不能有
void func(int a, int b, int c = 10); // 对
void func(int a, int b = 10, int c); // 错!
使用心得:默认参数用好了能少写好多重载函数。比如一个打印函数,默认打印到屏幕,也可以指定打印到文件。
3. 内联函数
C语言里我们用宏定义来代替小函数,减少函数调用开销:
#define MAX(a, b) ((a) > (b) ? (a) : (b))
但宏有好多坑:没有类型检查、运算符优先级问题、副作用问题…
C++有了更好的选择——内联函数:
inline int max(int a, int b) {
return a > b ? a : b;
}
内联函数在编译的时候会把函数体直接展开到调用的地方,没有函数调用的开销,同时又有函数的类型检查和语法检查。
注意:inline 只是给编译器的一个建议,不是强制的。编译器觉得函数太复杂或者太大,就会忽略inline,当成普通函数处理。
还有,内联函数的定义要放在头文件里,因为编译器需要看到完整的定义才能展开。
4. 引用传参
这又是一个超级实用的特性!
C语言里,如果想在函数里修改实参的值,只能传指针:
void swap(int *a, int *b) {
int tmp = *a;
*a = *b;
*b = tmp;
}
swap(&x, &y); // 调用的时候还要取地址
C++里有了引用,传参更直观了:
void swap(int& a, int& b) {
int tmp = a;
a = b;
b = tmp;
}
swap(x, y); // 直接传变量,不用取地址
引用就像是变量的"别名",操作引用就是操作原变量。传引用和传指针底层其实差不多,但写起来更优雅,也不容易出空指针的问题。
什么时候用引用传参?
需要修改实参的值 → 用引用
不需要修改,但对象很大(比如结构体、类对象)→ 用const引用,避免拷贝开销
小的基本类型(int、double)→ 直接值传递就行,没必要用引用
引用 vs 指针:
引用必须初始化,不能为"空",更安全
引用一旦绑定就不能改指向,指针可以
引用用起来更直观,像普通变量一样
指针更灵活,但也更容易出错
三、其他值得注意的变化