1.非引用形参
普通的非引用类型的参数通过复制对应的实参实现初始化。当用实参副本初始化形参时,函数并没有访问调用所传递的实参本身,因此不会修改实参的值。
2.指针形参
函数的形参可以是指针,此时将复制实参指针。与其他非引用类型的形参一样,该类形参的任何改变也仅作用于局部副本。如果函数将新指针赋给形参,主调函数使用的实参指针的值没有改变。事实上被复制的指针只影响对指针的赋值。
如果保护指针指向的值,则形参需定义为指向 const 对象的指针:
void use_ptr(const int *p) { // use_ptr may read but not write to *p }
3.Limitations of Copying Arguments
复制实参并不是在所有的情况下都适合,不适宜复制实参的情况包括:
-
当需要在函数中修改实参的值时。
-
当需要以大型对象作为实参传递时。对实际的应用而言,复制对象所付出的时间和存储空间代价往往过大。
-
当没有办法实现对象的复制时。
4.引用形参
1: void swap(int &v1, int &v2)
2: {
3: int tmp = v2;
4: v2 = v1;
5: v1 = tmp;
6: }
5.利用 const 引用避免复制
编写一个比较两个 string 对象长度的函数作为例子。这个函数需要访问每个 string 对象的 size,但不必修改这些对象。由于 string 对象可能相当长,所以我们希望避免复制操作。使用 const 引用就可避免复制:
// compare the length of two strings bool isShorter(const string &s1, const string &s2) { return s1.size() < s2.size(); }
其每一个形参都是 const string 类型的引用。因为形参是引用,所以不复制实参。又因为形参是 const 引用,所以 isShorter 函数不能使用该引用来修改实参。
如果使用引用形参的唯一目的是避免复制实参,则应将形参定义为 const 引用。
应该将不需要修改的引用形参定义为 const 引用。普通的非 const 引用形参在使用时不太灵活。这样的形参既不能用 const 对象初始化,也不能用字面值或产生右值的表达式实参初始化。
6.vector 和其他容器类型的形参
通常,函数不应该有 vector 或其他标准库容器类型的形参。调用含有普通的非引用 vector 形参的函数将会复制 vector 的每一个元素。从避免复制 vector 的角度出发,应考虑将形参声明为引用类型。然而,事实上,C++ 程序员倾向于通过传递指向容器中需要处理的元素的迭代器来传递容器:
// pass iterators to the first and one past the last element to print void print(vector<int>::const_iterator beg, vector<int>::const_iterator end) { while (beg != end) { cout << *beg++; if (beg != end) cout << " "; // no space after last element } cout << endl; }
7.数组形参
(1)如果要编写一个函数,输出 int 型数组的内容,可用下面三种方式指定数组形参:
// three equivalent definitions of printValues void printValues(int*) { } void printValues(int[]) { } void printValues(int[10]) { }
注意:编译器忽略为任何数组形参指定的长度。
(2)通过引用传递数组
和其他类型一样,数组形参可声明为数组的引用。如果形参是数组的引用,编译器不会将数组实参转化为指针,而是传递数组的引用本身。在这种情况下,数组大小成为形参和实参类型的一部分。编译器检查数组的实参的大小与形参的大小是否匹配:
// ok: parameter is a reference to an array; size of array is fixed void printValues(int (&arr)[10]) { } int main() { int i = 0, j[2] = {0, 1}; int k[10] = {0,1,2,3,4,5,6,7,8,9}; printValues(&i); // error: argument is not an array of 10 ints printValues(j); // error: argument is not an array of 10 ints printValues(k); // ok: argument is an array of 10 ints return 0; }
// ok: parameter is a reference to an array; size of array is fixed void printValues(int (&arr)[10]) { for (size_t i = 0; i != 10; ++i) { cout << arr[i] << endl; } }
&arr 两边的圆括号是必需的,因为下标操作符具有更高的优先级:
f(int &arr[10]) // error: arr is an array of references f(int (&arr)[10]) // ok: arr is a reference to an array of 10 ints
(3)对数组标界的处理
- 第一种方法是在数组本身放置一个标记来检测数组的结束。
- 第二种方法是传递指向数组第一个和最后一个元素的下一个位置的指针。
- 第三种方法是将第二个形参定义为表示数组的大小,这种用法在 C 程序和标准化之前的 C++ 程序中十分普遍。