@Author:CSU张扬
@Email:csuzhangyang@gmail.com or csuzhangyang@qq.com
@我的网站: https://www.faker.top
6.2 参数传递
如果形参是引用类型,它将绑定到对应的实参上;否则,将实参的值拷贝后赋给形参。
6.2.1 传值参数
函数对形参做的所有操作都不会影响实参。
【1】指针形参
执行指针拷贝操作时,拷贝的是指针的值;拷贝之后,两个指针是不同的指针。我们可以通过指针修改它所指对象的值。
void reset(int *p) {
*p = 0;
p = 0; // p是q的拷贝,并未改变q的值
}
int i = 1;
int *q = &i;
reset(q); // *q = i = 1
Best Practices:
C的程序员常常使用指针类型的形参,在C++中建议使用引用类型。
6.2.2 传引用参数
对引用的操作实际上就是对所引对象上的操作。
拷贝大的类类型或者容器对象比较低效,甚至有的类类型(IO类型)不支持拷贝操作,这时只能用引用。当无需修改引用的值是,使用常量引用。
6.2.3 const形参和实参
用实参初始化形参时,会忽略掉形参的顶层 const
。也就是形参有顶层 const
时,传给它常量对象或者非常量对象都可以。
形参初始化和变量初始化方式一样,记住通用的初始化规则即可。例如:我们可以使用非常量初始化一个底层 const
对象,反过来不行;普通引用必须引用相同类型的对象。
【1】尽量使用常量引用
- 把函数参数定义为普通引用会给人一种误导,认为该参数可以修改。
- 使用非常量引用限制了实参类型,例如不能把
const
对象、字面值或者需要类型转换的对象传递给非常量引用形参。
6.2.4 数组形参
不允许直接拷贝数组,使用数组时通常会(隐式的)转换为指针。所以我们无法以值传递的方式使用数组参数,当我们传递给函数一个数组时,其实是传递的数组首地址。
三种方式:
void print(const int*);
void print(const int[]);
void print(const int[10]); // 这里的维度10只是我们希望是10,实际可以传递任意维度的数组。
当我们不需要修改数组元素时,形参应该是指向 const
的指针。
【1】保证数组长度的三种技术
-
使用标记指定数组长度:常用于C风格字符串,字符数组以
\0
结束。 -
使用标准库规范:传递数组的首元素和尾后元素的指针。
void print(const int* beg, const int* end); // 函数定义 int a[3] = {1, 2, 3}; print(begin(a), end(a)); // 如何调用
-
显式传递数组的长度:
void print(const int ia[], size_t sz); int a[3] = {1, 2, 3}; print(a, 3);
【2】数组引用形参
void print(int (&arr)[10]);
int j[3] = {1, 2, 3};
int k[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
print(j); // 错误,维度必须为10
print(k); // 正确
与指针数组不同的是,这里的维度10指定了我们只能向函数传递大小为10的数组。
在后面,我们会介绍如何编写一个函数,可以给引用形参传递任意大小的数组。
注意区分:
f(int &arr[10])
,arr是引用的数组,不存在引用的数组,这是错误的。f(int (&arr)[10])
,arr是数组的引用,该数组包含10个整型。
【3】传递多维数组
两种形参的形式,注意 rowSize
是行数,3
就是列数。m
实际上是指向含有3个整数的数组的指针。
void print(int (*m)[3], int rowSize);
void print(int m[][3], int rowSize);
多维数组真正传递的是数组首元素的指针,例如:int a[2][3]
,调用形式:print(a, 2)
。
6.2.5 main:处理命令行选项
两种形参的形式:
int main(int argc, char *argv[]) {...}
int main(int argc, char **argv) {...}
argc
代表 argv
的大小,argv
中是我们命令行的字符串数组。
例如我们在命令行输入 ./a.out src.txt dest.txt
。
那么 argc = 3
,argv[0] = "./a.out"
,argv[1] = "src.txt"
,argv[2] = "dest.txt"
。
6.2.5 含有可变形参的函数
【1】initializer_list形参
如果函数的实参数量未知并且实参类型都相同,可以使用 initializer_list
类型的形参。
它是一种标准库模板类型,定义于同名的头文件内。
操作 | 作用 |
---|---|
initializer_list<T> lst; | 执行默认初始化;T类型的空列表 |
initializer_list<T> lst{a, b, c...} | lst 中元素是初始值的副本(拷贝),且为 const ;lst 的大小就是初始值的数量 |
lst2(lst) / lst2 = lst | 拷贝/赋值一个该对象,不会拷贝列表中的元素;拷贝后,二者共享元素 |
lst.size() | 元素数量 |
lst.begin() / lst.end() | lst 首元素指针/尾后元素指针 |
注意: initializer_list
中元素始终为常量。
使用举例:
void print(initializer_list<int> lst) {
for (auto i : lst) {
cout << i << " ";
}
}
int main() {
initializer_list<int> lst{1, 2, 3};
print(lst);
print({1, 2, 3}); // 直接传递大括号列表也是可行的
}
含有 initializer_list
形参的函数也可以拥有其他形参,其他形参的位置任意。
void print(string s1, initializer_list<int> lst, string s2) {
cout << s1 << endl;
for (auto i : lst) {
cout << i << " ";
}
cout << endl;
cout << s2 << endl;
}
int main() {
initializer_list<int> lst{1, 2, 3, 4};
string s = "abc";
print(s, lst, s);
print(s, {1, 2, 3, 4}, s);
}
【2】省略符形参
是为了便于C++程序访问特殊的C代码设置的,这些代码使用了 varargs
的C标准库功能。
注意:大多数类类型传递给省略符形参时都无法正确拷贝。
省略符形参只能出现在形参列表的 最后 一个位置。
void print(parm_list, ...);
void print(...);
略写,不常使用。