2008年1月24日星期四

Passing By Reference-to-const 通过对常量的引用来传递参数

Passing By Reference-to-const

By Dan Saks
Embedded Systems Design
(10/01/01, 09:13:58 H EDT)


The rules for initializing references make passing by reference-to-const an efficient and attractive alternative to passing by value.
引用的初始化机制使得通过对常量的引用来传递参数比按值传递更高效。

As in C, function calls in C++ normally pass arguments by value. For example, given:
和在C语言中一样,函数通常通过值来传递参数。如:

int abs(int i);
int n;


calling abs(n) passes argument n to function abs by value. That is, the program copies the value of n to abs's parameter i each time it makes this call.
C++ uses pass-by-value even for objects of class types. (In C++, class types include struct and union types.) For example, given some struct type:
C++还能够通过按值传递的方式传递类类型的数据(包括结构和共同体)。
struct gadget
{
// a whole bunch of fields
};


and:


int test(gadget g);
gadget x;


calling test(x) passes argument x by value. Much as before, the program copies x's value to parameter g each time it makes this call.
程序每次调用都需要拷贝x的值。
For large struct objects, passing by value can be very slow because it copies each byte of the argument to the corresponding byte of the parameter, as if by calling the Standard C memcpy function. The program does not necessarily call memcpy to do the job, but the argument passing mechanism behaves as if it did. Strictly speaking, passing a class object by value in C++ conceptually calls a special function associated with the class called the copy constructor. However, when the class is just a struct as in C, the copy constructor behaves essentially like memcpy.
对于较大的结构对象,按值传递方式速度非常慢。通过值传递的方式传递一个类对象,在C++中调用了类的拷贝构造函数。对于结构对象,拷贝构造函数的表现与memcpy一样(非常耗时)。

You can avoid the cost of copying a large argument by passing it by address rather than by value. You simply change the function declaration to:
可以通过使用按地址传递的方式来避免这种消耗大量资源的方式,如:

int test(gadget *g);


and then change the function call to test(&x). Passing &x (the address of x) is often a lot less work than passing an entire copy of x.
函数调用的时将地址传递给函数,因而可以节省参数传递的时间。

Unfortunately, when you change the parameter type from gadget to "pointer to gadget," you introduce a change in the function's behavior, or at least the possibility of a change. When passing by value, the function only sees a copy of the original gadget, so it cannot change that gadget. In my example, when passing by value, calling test(x) cannot change x. When passing by address, the function can dereference its pointer parameter and store new values into the gadget argument. That is, calling test(&x) might change x. Of course, you can prevent the function from tampering with its argument by using the const qualifier, as in:
通过值传递时,函数只得到了参数的一个拷贝,函数无法改变参数的值。
通过引用传递值时,可以放弃指针指向的参数而存入新的参数,也就是说 test(&x)可以改变x的值。当然,你可以通过添加const 修饰符来避免参数被修改。

int test(gadget const *g); //注意const的位置,这和const gadget是否一样呢?


For very large objects, passing by address (with const) is almost always faster than passing by value. For objects of modest size (on the order of eight to 16 bytes), it isn't always clear at the outset whether passing by address will actually be faster than passing by value. It depends on the target machine's architecture and what the function does with the parameter. Sometimes you just have to make your best guess and wait to measure the performance of the running program. If it turns out that you guessed wrong, you may have to rewrite the function.
当传递的对象较大时,按值传递参数肯定速度较慢。而对象速度较小时,速度则由计算机的结构决定,需要测试来确定。

Unfortunately, rewriting a function call so that it uses pass-by-address instead of pass-by-value, or vice versa, can be a bona fide maintenance headache. Changing the function declaration from:


int test(gadget g);


to:


int test(gadget const *g);


isn't much fuss, nor is changing the corresponding function definition. But numerous function calls might be scattered throughout the code, and you have to rewrite all those calls. In some cases, a call such as text(x) becomes test(&x). In others, test(*p) becomes test(p) (where p is a "pointer to gadget").
如果需要吧某个函数由按值传递改为按引用传递,对散布在代码中的众多的函数调用来说,非常麻烦。

In C++, passing by reference offers an alternative to passing by address. You can declare the test function as:
在C++中,有一种替代的方式:按地址传递,可以如下声明函数:

int test(gadget const &g);


In this case, parameter g has type "reference to const gadget." This lets you write the call as test(x), as if it were passing by value, but it yields the exact same performance as if it were passing by address. Again, the const qualifier prevents the function from changing the value of its actual gadget argument.
这样参数g的类型为“指向常量gadget的引用”。这样你就可以像传递值那样将函数调用写成test(x).

Changing a function parameter from passing by value to passing by reference-to-const is a fairly minor change. You must change the function's definition and corresponding declaration, if any, appearing in a header. Although you must recompile any code that calls the function, you need not rewrite the calls.


In short, passing by reference-to-const is a potentially efficient alternative to passing by value. To the calling function, an argument passed by reference-to-const looks and acts just like one passed by value. The argument expression in the call looks the same either way-you need not add or remove *s or &s when switching from one form to the other.
两种方式并不需要修改函数调用时的变量为*s或者&s;

When passing by value, the called function can't store into the actual argument because it only has access to a copy. When passing by reference-to-const, the called function can't store into the actual argument because it's const.
对于通过指向常量的引用和值传递两种方式,参数都不会被改变。

Dan Saks is the president of Saks & Associates, a C/C++ training and consulting company. He is also a consulting editor for the C/C++ Users Journal. You can write to him at dsaks@wittenberg.edu.

没有评论: