Pass by reference-to-
const is typically more efficient than pass by value and avoids the slicing problem.
Pass-by-value can be an expensive operation. For example, consider the following class hierarchy:
Now consider what will happen if we call a function
validateStudent, which takes a
Student argument (by value) and returns whether it is a real student:
Student object has two
string object within it, while its base class
Person contains two additional
string type data members. So the parameter-passing cost of this function is one call to the
Student copy constructor matched with two
string copy constructor, and one call to the base class
Person's copy constructor, which also entails two more
string construction. When the
Student object is destroyed, each constructor call is matched by a destructor call. Overall, the cost of passing a
Student by value is six constructors and six destructors.
It would be nice is there were a way to bypass all those constructions and destructions. The answer is: pass by reference-to-
Since no new objects are being created, there’s no constructor or destructor call. The
const in the revised parameter declaration is important, for it guarantees that the callers would not worry about
validateStudent making changes to the
Student they passed in (when passing by value,
validateStudent would be able to modify only a copy of the
Student they passed in, so the callers know they are shielded from any changes the function might make to the
Apart from efficiency, passing parameters by reference also avoids the
slicing problem: when a derived class object is passed (by value) as a base class object, the base class copy constructor is called, and the extra features in derived class object are “sliced” off. For example:
Below is a bad example if you want to write a function to print out a window’s name and then display the window:
When you call this functiona with a
Since it is passed by value, the parameter
w will be constructed as a
Window object. Regardless of the type of object passed to the function, inside
w will act like an object of class
Window (it is an object of class
Window after all), and all the specialized information that made
wwsb act like a
WindowWithScrollBars object will be sliced off.
However, if we revised the function declaration like this:
w will act like whatever kind of window is actually passed in.
In general, the only types for which we can reasonably assume that pass-by-value is inexpensive are:
- built-in types
- STL iterator
- function object types
Under the hood of the C++ compiler, a reference is implemented as pointers, so passing by reference usually means passing a pointer. This is why for built-in types (e.g., an
int), it’s more efficient to pass it by value than by reference. Also, iterators and function objects in the STL are more efficient to copy and are not subject to the slicing problem because they are designed to be passed by value (this is an example where rules change depending on which part of C++ we are using, see item 0).
On the other hand, we can not conclude that all small types are necessarily good pass-by-value candidate:
the user-defined types, despite being small in terms of size, may have expensive copy constructors, such as most STL containers that contain little more than a pointer but leading to copying everything they point to when applying copying operation.
even if the copy constructors are inexpensive, compilers may treat user-defined small types differently with buiilt-in types: for example, some compilters refuse to put objects consisting of only a
doubleinto a register, even though they will happily place naked
doubles there, so we can be better off passing such objects by reference, for compilers will certainly put pointers (the implementation of references) into registers.
a type that is small now may be bigger in a future release, for its internal implementation may change; things can even change when we switch to a different C++ implementation, for example, some implementations of the standard library’s
stringtype are seven times as big as others.
In summary, for everything else other than built-in types and STL iterator and function object types, follow the advice and prefer pass-by-reference-to-
const over pass-by-value.