C++Primer(5th) Notes - 6.2 参数传递

@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】尽量使用常量引用

  1. 把函数参数定义为普通引用会给人一种误导,认为该参数可以修改。
  2. 使用非常量引用限制了实参类型,例如不能把 const 对象、字面值或者需要类型转换的对象传递给非常量引用形参。

6.2.4 数组形参

不允许直接拷贝数组,使用数组时通常会(隐式的)转换为指针。所以我们无法以值传递的方式使用数组参数,当我们传递给函数一个数组时,其实是传递的数组首地址。

三种方式:

void print(const int*);
void print(const int[]);
void print(const int[10]);  // 这里的维度10只是我们希望是10,实际可以传递任意维度的数组。

当我们不需要修改数组元素时,形参应该是指向 const 的指针。

【1】保证数组长度的三种技术

  1. 使用标记指定数组长度:常用于C风格字符串,字符数组以 \0 结束。

  2. 使用标准库规范:传递数组的首元素和尾后元素的指针。

    void print(const int* beg, const int* end);  // 函数定义
    int a[3] = {1, 2, 3};
    print(begin(a), end(a));  // 如何调用
    
  3. 显式传递数组的长度:

    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的数组。
在后面,我们会介绍如何编写一个函数,可以给引用形参传递任意大小的数组。

注意区分:

  1. f(int &arr[10]),arr是引用的数组,不存在引用的数组,这是错误的。
  2. f(int (&arr)[10]),arr是数组的引用,该数组包含10个整型。

【3】传递多维数组

两种形参的形式,注意 rowSize 是行数,3 就是列数。m 实际上是指向含有3个整数的数组的指针。

  1. void print(int (*m)[3], int rowSize);
  2. void print(int m[][3], int rowSize);

多维数组真正传递的是数组首元素的指针,例如:int a[2][3] ,调用形式:print(a, 2)

6.2.5 main:处理命令行选项

两种形参的形式:

  1. int main(int argc, char *argv[]) {...}
  2. int main(int argc, char **argv) {...}

argc 代表 argv 的大小,argv 中是我们命令行的字符串数组。

例如我们在命令行输入 ./a.out src.txt dest.txt
那么 argc = 3argv[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 中元素是初始值的副本(拷贝),且为 constlst 的大小就是初始值的数量
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标准库功能。

注意:大多数类类型传递给省略符形参时都无法正确拷贝。

省略符形参只能出现在形参列表的 最后 一个位置。

  1. void print(parm_list, ...);
  2. void print(...);

略写,不常使用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

神奇小海螺

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值