1. 默认参数(缺省参数)
在 C++ 中,函数可以拥有默认参数(也称为缺省参数)。当调用函数时,如果未提供某个参数,则使用该参数的默认值。
1.1 默认参数的声明与定义
默认参数在函数声明中指定,而不是在定义中。如果函数的声明和定义分开,默认值应写在声明中。
// .h 头文件 extern void func(int a = 0); // .cpp 源文件 #include "yang.h" void func(int a = 10) // 错误:默认值应只在声明中指定 { printf(""); }正确做法:
// .h 头文件 extern void func(int a = 0); // .cpp 源文件 #include "yang.h" void func(int a) // 定义中不再写默认值 { printf("a = %d\n", a); }1.2 默认参数的使用规则
默认参数必须从右向左连续提供,调用时从左向右传参,不能跳过中间的默认参数。
void func(int a = 5, int b = 4, int c = 3, int d = 2, int e = 1) { printf("a = %d b = %d c = %d d = %d e = %d \n", a, b, c, d, e); } int main() { func(); // a=5,b=4,c=3,d=2,e=1 func(50, 40); // a=50,b=40,c=3,d=2,e=1 // func(50, , 30); // 错误:不能跳过 b return 0; }1.3 默认参数可以是表达式
默认参数可以是常量、全局变量,甚至是函数调用(但必须是编译时可确定的表达式)。
int my_rand() { srand(time(nullptr)); return rand() % 100; // 0~99 } void func(int a, int b = my_rand()) // 默认参数调用函数 { printf("a = %d b = %d \n", a, b); } int main() { func(1, 2); // a=1, b=2 func(12); // a=12, b=随机值 return 0; }2. 左值与右值
C++11 引入了值类别的概念,每个表达式都有类型和值类别。
- 左值 (lvalue):可以取地址的表达式,通常有持久的状态
- 右值 (rvalue):不能取地址的表达式,通常是临时对象或字面量
int main() { int a = 10; // a 是 int 类型的左值 int* p = nullptr; // p 是 int* 类型的左值 char ch = 'a'; // ch 是 char 类型的左值 const int b = 20; // b 是 const int 类型的左值(常性左值) &a // 合法:a 是左值 // &10; // 错误:10 是右值 &p // 合法:p 是左值 // &nullptr // 错误:nullptr 是右值 &b // 合法:b 是左值(常性左值) return 0; }3. 左值引用与右值引用
3.1 左值引用
传统的引用(&)是左值引用,只能绑定到左值。
int main() { int a = 10; int& ra = a; // 左值引用 const int& cra = a; // 左值常引用 const int b = 20; // int& rb = b; // 错误:不能将非 const 引用绑定到 const 对象 const int& crb = b; // 正确:const 引用可以绑定到 const 对象 return 0; }3.2 右值引用
C++11 引入了右值引用(&&),只能绑定到右值。
int main() { int a = 10; int& ra = a; // 左值引用 const int& rb = a; // 左值常引用 int&& rr = 10; // 右值引用,绑定到字面量 10 // int&& rr2 = a; // 错误:不能将右值引用绑定到左值 cout << rr << endl; // 输出: 10 rr += 100; // 可以修改 cout << rr << endl; // 输出: 110 const int& crx = 30; // const 左值引用可以绑定到右值 // 编译器会生成临时对象:int tmp = 30; const int& crx = tmp; return 0; }3.3 引用的特性
int main() { int a = 10; // 1. 引用必须初始化 int& ra = a; // 正确 // int& rb; // 错误:引用必须初始化 // 2. 不能引用引用(C++ 中没有引用的引用) int* p = &a int** pp = &p // 指针的指针 // int&& rra = ra; // 错误:不能有引用的引用 // 3. 引用不能为 NULL // int& rn = NULL; // 错误 return 0; }4. 数组的引用
数组的引用语法比较特殊,需要括号:int (&rar)[n] = ar;
void funa(int *br, int n) // 数组退化为指针 { cout << "funa: br[5] size: " << sizeof(br) << endl; // 输出指针大小 } void funb(int (&rar)[5]) // 数组的引用,保留数组类型信息 { cout << "funb (&rar)[5] size: " << sizeof(rar) << endl; // 输出数组大小 } int main() { const int n = 5; int ar[n] = { 12, 23, 34, 45, 56 }; // 1. 变量的别名 int& a = ar[0]; // 数组元素的引用 // 2. 数组的别名(数组的引用) int(&rar)[5] = ar; // 必须指定正确的大小 cout << "main ar: size " << sizeof(ar) << endl; // 20 (5 * 4) funa(ar, n); // 输出指针大小(通常 4 或 8) funb(ar); // 输出数组大小 20 // 3. 指向数组的指针 int(*p)[n] = &ar; // 指向整个数组的指针 // 4. 指针数组的引用 int* br[n]; // 指针数组 int* (&rbr)[n] = br; // 指针数组的引用 return 0; }5. 类类型的引用
引用不仅适用于内置类型,也适用于用户自定义的类类型。
class Int { // 类定义 }; int main() { Int obj; Int& robj = obj; // 类类型的左值引用 const Int& crobj = obj; // 类类型的 const 左值引用 const Int cobj; const Int& rcobj = cobj; // const 引用绑定 const 对象 // 对于类类型,const 引用绑定右值时不会生成临时对象(效率更高) const Int& rtemp = Int(); // 绑定到临时对象 return 0; }6. 总结
- 默认参数在函数声明中指定,从右向左连续提供
- 左值可取地址,右值不可取地址
- 左值引用(
&)绑定左值,右值引用(&&)绑定右值 - const 左值引用可以绑定左值和右值
- 引用必须初始化,不能为 NULL,不能有引用的引用
- 数组的引用需要特殊语法:
int (&rar)[n] = ar; - 引用适用于所有类型,包括内置类型和用户自定义类型