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:
PersonCopy constructor, why?StudentMust be calledPersonFor more information about object copying, see: Item 12: Completely copying objects.
StudentCopy constructor
name,address,schoolName,schoolAddressCopy 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 timeconstIt is also required: The value passing method ensures that the function call will not change the originalStudentTo achieve the same effect, you must useconstDeclaration 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 exampleWindowThe 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 accessWindowFunction of the API, which is obtained by passing values.WindowInstance:
// incorrect! parameter may be sliced!void printNameAndDisplay(Window w){ std::cout << w.name(); w.display();}WindowWithScrollBars wwsb;printNameAndDisplay(wwsb);
WhenprintNameAndDisplayWhen the parameter type is fromWindowWithScrollBarsImplicitly convertedWindow. This conversion process is calledWindow. The result iswIs actuallyWindowObject.WindowWithScrollBarsOfdisplay().
// 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 CompilersdoublePut it into the register, but refuse to include only onedoublePut the object in the register.
One includes onlydoubleThe object size is 8, it anddoubleIt 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.