Item 20: it is better to pass a constant reference than to pass a value. Valid C ++ notes, itemvalid tive
Item 20: Prefer pass-by-reference-to-const to pass-by-value
The parameters and return values of C ++ functions are passed by default. This feature is inherited from the C language. If this parameter is not specified, the function parameter is initialized as a copy of the real parameter, and the caller obtains a copy of the returned value. These copies are completed by calling the object copy constructor, which makes the copy cost very high.
In general, passing constant references is better than passing values, while avoiding truncation problems. However, the built-in types, STL containers, and iterators are more suitable for passing values.
Here is an example.
The hierarchy of a typical class may be as follows:
class Person { string name, address;};class Student: public Person { string schoolName, schoolAddress;};
Assume that a function call is as follows:
bool validateStudent(Student s); // function taking a Student by valueStudent plato; // Plato studied under Socratesbool platoIsOK = validateStudent(plato); // call the function
Before callingvalidateStudent()
Six function calls are performed:
Person
Copy constructor, why?Student
Must be calledPerson
For more information about object copying, see: Item 12: Completely copying objects.
Student
Copy constructor
name
,address
,schoolName
,schoolAddress
Copy constructor
The solution is to pass the constant reference:
bool validateStudent(const Student& s);
First, it is passed as a reference and no new objects are constructed. This avoids the call of the six constructors in the preceding example. At the same timeconst
It is also required: The value passing method ensures that the function call will not change the originalStudent
To achieve the same effect, you must useconst
Declaration to declare this, let the compiler check it!
Truncation Problem
Changing a value to a reference can also effectively avoid truncation: Due to type restrictions, when a subclass object is passed, only the parent class is passed into the function.
For exampleWindow
The parent class derives from the subclass.WindowWithScrollBars
:
class Window {public: ... std::string name() const; // return name of window virtual void display() const; // draw window and contents};class WindowWithScrollBars: public Window {public: ... virtual void display() const;};
There is an accessWindow
Function of the API, which is obtained by passing values.Window
Instance:
// incorrect! parameter may be sliced!void printNameAndDisplay(Window w){ std::cout << w.name(); w.display();}WindowWithScrollBars wwsb;printNameAndDisplay(wwsb);
WhenprintNameAndDisplay
When the parameter type is fromWindowWithScrollBars
Implicitly convertedWindow
. This conversion process is calledWindow
. The result isw
Is actuallyWindow
Object.WindowWithScrollBars
Ofdisplay()
.
// fine, parameter won't be slicedvoid printNameAndDisplay(const Window& w){ std::cout << w.name(); w.display();}
This is good. If you have gone deep into the compiler, you will find that references are implemented through pointers.
Special Cases
In general, transferring constant references is a better choice than passing values. However, there are exceptions, such as built-in types, STL iterators, and function objects.
It is better to pass values of built-in types because they are small, while a reference usually requires 32-bit or 64-bit space. You may think that a small object should also be the first choice to pass the value, but a small object does not mean that the copy construction cost is not high! For example, STL containers are usually small and contain only some dynamic memory pointers. However, in its copy constructor, it is bound to allocate and copy those dynamic memory parts.
Even if the cost of copying the constructor is small, the data passing method still has performance problems. Some compilers treat built-in and user-defined types differently, even if they have the same underlying representation. For example, some Compilersdouble
Put it into the register, but refuse to include only onedouble
Put the object in the register.
One includes onlydouble
The object size is 8, it anddouble
It has the same size and underlying representation. For calculation of object size, refer to: Item 7: declare the destructor of the polymorphism base class as a virtual function.
In terms of object-oriented design, even if the object is small now, it may become larger as a user-defined type (if you change the internal implementation ). In the long term, we should also adopt a reference transfer method to design functions for use.
STL iterators and function objects should also be passed in values because they are indeed designed in STL and their copy constructors are not costly.
Unless indicated, this blog post is original, reprinted please in the form of a link to indicate the address: http://harttle.com/2015/08/13/effective-cpp-20.html
Copyright Disclaimer: This article is the original article of the blogger. For more information, see the original article link.