Key points of this article:
1, try to replace Pass-by-value with Pass-by-reference-to-const. The former is more efficient and can avoid cutting problems.
2. This rule does not apply to both the built-in type and the iterator and function object types in the STL. For them, Pass-by-value is usually more appropriate.
By default, C + + passes an object to a function by-value, or gets the object return value of a function. Unless you specify otherwise, the function arguments are initialized with the copy of the actual argument, and the caller gets a copy of the function return value. These replicas are obtained by the copy constructor with the object. This may make the pass-by-value an expensive operation, but it can also lead to object cutting problems.
The efficiency considerations
See the following class's inheritance system for pass-by-reference efficiency issues:
Class Person{public:person (); virtual ~person (); private:string name;string address;}; Class Student:public person{public:student (); ~student ();.. private:string schoolname;string schooladdress;};
Now consider the following code, which calls the function validatestudent, which requires a student argument (Pass-by-value) and returns whether it is valid:
BOOL Validatestudent (Student s);//Declare a function, function to accept the student Student Plato by the value; The object of the Plato class
BOOL Stuisok=validatestudent (Plato);
What happens when the above function is called?
Obviously, the copy constructor of student is called, and the parameter S is initialized with Plato. It is also obvious that when Validatestudent returns, S will be destroyed. So the cost of the parameter passing of this function is the call of a student copy constructor and the invocation of a student destructor at a time.
But that's not all. The Student object contains two string objects, and the student object inherits from a person object and contains two extra string objects inside the person object. Ultimately, the consequence of passing a student object as a value is to cause a call to a student copy constructor, a call to the copy constructor of a person, and a copy constructor call of four strings. When a copy of a student object is destroyed, every call to a constructor corresponds to a destructor call, so the full cost of passing a student in a pass-through is six constructors and six destructors !
This is the right and worthwhile behavior. After all, you want all objects to be reliably initialized and destroyed. However, the pass by Reference-to-const Way is better:
BOOL Validatestudent (const student& s);
This is very effective: no constructors and destructors are called, because no new objects are constructed .
The const in the modified parameter declaration is very important, originally validatestudent to accept a student parameter in By-value way, so the caller knows that the function will never make any changes to their incoming student. The validatestudent can only change its copy. It is now necessary to student to be passed as a reference and declare it as const, otherwise the caller must be concerned that validatestudent changed the student they passed in.
Object Cutting issues
Passing parameters in a pass-by-reference way also avoids cutting problems (slicing problem). When a derived class object is passed as a base class object (by value), the copy constructor of the base class is called, and those that make the object behave like a derived class object are "cut off", leaving only a purely base class object For example, suppose you work on a group of classes that implement a graphics window system:
Class Window {public: ... std::string name () const; Returns the window name virtual void display () const;//Display windows and their contents};class windowwithscrollbars:public window {public: ... virtual void display () const;};
All window objects have a name (the name function), and all Windows can be displayed (the display function). The fact that display is virtual tells you clearly that the method of displaying the window object of the base class may be different from the display method of the specialized Windowwithscrollbars object. Now, suppose you want to write a function that prints out the name of a window and then displays the window. The following is a demonstration of the error:
void Printnameanddisplay (Window w)//incorrect! Parameters may be cut { std::cout << w.name (); W.display ();}
Consider what happens when you call this function with a Windowwithscrollbars object:
Windowwithscrollbars Wwsb;printnameanddisplay (WWSB);
The parameter w will be constructed as a Window object-it is passed value, and the WWSB behaves like a Windowwithscrollbars object with special information being cut. In Printnameanddisplay, regardless of the type of object passed to the function, W will always behave like an object of the window class (because its type is window). So calling display in Printnameanddisplay will always call Window::d isplay, it will never be windowwithscrollbars::d isplay. in fact, the derived class object is cast to the base class object .
The way to bypass the cutting problem is to pass W in Passby Reference-to-const way:
void Printnameanddisplay (const window& W) { //parameter will not be cut std::cout << w.name (); W.display ();}
Now what kind of window is coming in, W shows that type
Pass-by-value and Pass-by-reference-to-const
Small Object The Pass-by-value is still pass-by-reference-to-const:
(1) A small object does not mean that the copy constructor that calls it is inexpensive. Many objects (including most STL containers) contain more than one pointer, but copying such objects must copy everything they point at the same time, which is very expensive. Even when small objects have a cheap copy constructor, there is a performance problem. Some compilers do not discriminate between built-in types and user-defined types, even if they have the same underlying representation. For example, some compilers refuse to put an object consisting only of a double into a register (reg), even if they are usually very willing to put a pure double there. When this happens, it is better for you to pass such an object in a quoted way, because the compiler would take a pointer (the referenced implementation) into the register for granted.
(2) A small user-defined type is not necessarily the best candidate for a value, another reason is that, as a user-defined type, its size often changes because it can become very large in the future.
The conclusion is: the small object also tries to Pass-by-reference-to-const
Using pointers to implement references is a very typical practice, so pass by reference actually usually means passing a pointer .
It can be concluded that if you have a built-in type object (an int), passing it by value is often more efficient than quoting, and the same recommendation applies to iterators and function objects in STL.
Because the parameters of the built-in data type do not exist for construction, destruction, and replication is very fast, the efficiency of "value passing" and "reference passing" is almost equal.
Normally, you can reasonably assume that the value of a cheap type has only built-in types and iterators and function objects in the STL. For any other type, try replacing pass-by-value with Pass-by-reference-to-const.
Clause 20: Prefer to replace pass-by-value with Pass-by-reference-to-const