[C++学习]函数之参数传递

一、传值参数

当初始化一个非引用类型的变量时,初始值被拷贝给变量。此时,对变量的改动不会影响初始值。函数对传值参数的所有操作都不会影响实参。

指针形参

指针的行为和其他非引用类型一样,当执行指针拷贝操作时,拷贝的时指针的值。拷贝之后的两个指针是不同的指针。但因为指针使我们可以间接访问它所指的对象,所以通过指针可以修改它所指的对象的值。

在C++语言中,建议使用引用类型的形参替代指针。

二、传引用参数

使用引用参数,允许函数改变一个或多个实参的值。和其他引用一样,引用形参绑定初始化它的对象。

使用引用避免拷贝

拷贝大的类类型的对象或者容器对象比较抵消,甚至一些类类型根本不支持拷贝操作。所以,如果函数无须改变引用形参的值,最好将其声明为常量引用。

举个例子,比较两个string对象的长度。因为string对象可能会很长,所以应该避免直接拷贝它们,这时使用引用形参比较好。又因为比较长度无需改变其内容,所以把形参第一位常量的引用。

bool isShorter(const string &s1, const string &s2)
{
    return s1.size() < s2.size();
}

使用引用形参返回额外信息

一个函数只能返回一个值,然而有时候函数需要同时返回多个值,引用形参就为我们一个返回多个结果提供了有效的途径。

三、const形参和实参

当实参初始化形参时会忽略掉顶层const,传给它常量对象或非常量对象都是可以的。

void function(const int i) { /* function能够读取i,但是不能向i写值 */}

C++语言中允许定义若干具有相同名字的函数,不过前提是不同的函数的形参列表应该有明显的区别。因为顶层const被忽略,不能这样定义第二个函数:

void function(const int i) {}
void function(int i) {}

指针或引用形参与const

形参的初始化方式与变量的初始化方式是一样的。我们可以使用非常量初始化一个底层const对象,但是反过来不行,同时一个普通的引用必须用同类型的对象初始化。

尽量使用常量引用

把函数不会改变的形参定义成引用是一种常见的错误,这么做带给函数的调用者一种误导,即函数可以修改它的实参的值。此外,使用引用而非常量引用也会极大地限制函数所能接受的实参类型。

四、数组形参

数组有两个特殊性质:不允许拷贝数组以及使用数组时通常会将其转换成指针。因为数组会被转换成指针,所以当我们为函数传递一个数组时,实际上传递的是指向数组首元素的指针。

void function(const int*);
void function(const int[]);
void function(const int[10]); // 表示期望的元素个数,实际不一定

因为数组是以指针的形式传递给函数的,所以一开始并不知道数组的大小,调用者应该为此提供一些额外的信息。

使用标记指定数组长度

此方法的典型示例是C语言风格的字符串,其字符串最后一个字符后面跟着一个空字符串。在处理C语言风格字符串时遇到空字符停止。

void print(const char *cp)
{
    if (cp)
        while (*cp)
            cout << *cp++;
}

使用标准库规范

管理数组实参的第二种方法是传递指向数组首元素和尾元素的指针。

void print(const int *beg, const int *end)
{
    while (beg != end)
        cout << *beg++ << endl;
}

调用方法:

int number[] = {1, 2, 3, 4, 5};
print(begin(number), end(number));

显式传递一个表示数组大小的形参

void print(const int ia[], size_t size)
{
    for (size_t i = 0; i != size; ++i)
    {
        cout << ia[i] << endl;
    }
}

调用方法:

int number[] = {1, 2, 3, 4, 5};
print(number, end(number) - begin(number));

数组形参和const

当函数不需要对数组元素执行写操作时,数组形参应该是指向const的指针,只有当函数确实需要改变元素值的时候,才把形参定义为指向非常量的函数。

数组引用形参

C++语言允许将变量定义成数组的引用,基于同样的道理,形参也可以是数组的引用。

void print(int (&arr)[10])
{
    for (auto elem : arr)
    {
        cout << elem << endl;
    }
}

传递多维数组

所谓的多维数组其实是数组的数组。

和所有数组一样,当多维数组中传递给函数时,指针传递的时指向数组首元素的指针。

void print(int (*matrix)[10], int rowSize) { /*matrix是指向含有10个整数的数组的指针*/ }
void print(int matrix[][10], int rowSize) { /*等价于上面的写法*/ }

五、处理命令行选项

命令行选项通过两个形参传递给main函数:

int main(int argc, char *argv[]) { /* */ }
int main(int argc, char **argv) { /* */ }

可选实参从argv[1]开始,argv[0]保存着程序的名称。

六、含有可变形参的函数

有时候我们无法预知应该像函数传递几个实参。为了能处理不同数量实参的函数,C++11新标准提供了两种的方法:

initializer_list形参

如果函数的是参数量未知但是全部实参的类型相同,我们可以使用initializer_list类型的形参。initializer_list是一种标准库类型,由于表示某种特定类型的值的数组。

下面是initializer_list提供的操作:

操作 说明
initializer_list lst; 默认初始化T类型元素的空列表
initializer_list lst{a, b, c...}; lst初始化是对应的初始化副本,列表中的元素是const
lst2(lst) 拷贝一个initializer_list对象不会拷贝列表中的元素,拷贝后,原始列表和副本共享元素
lst2 = lst 赋值一个initializer_list对象
lst.size() 列表中的元素数量
lst.begin() 返回指向lst首元素的指针
lst.end() 返回指向lst中尾元素下一位置的指针

与vector不同的是,initializer_list对象中的元素医院是常量值,无法修改。

void error_msg(initializer_list<string> il)
{
    for (auto beg = il.begin(); beg != il.end(); ++beg)
    {
        cout << *beg << endl;
    }
}

省略符形参

省略符形参是为了便于C++程序访问某些特殊的C代码设置的。这些代码使用了名为varargs的C标准库功能。通常,省略符形参不应用于其他目的。

省略符形参应该仅仅用于C和C++的通用的类型。特别应该注意的是,大多数类型的对象在传递给省略符形参时都无法正确的拷贝。

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

posted @ 2019-09-28 15:26  Dumbledore  阅读(493)  评论(0编辑  收藏  举报