Effective C ++ 2e Item22

zhaozj2021-02-11  207

Terms 22: Try to use "pass reference" without "passing value"

In the C language, everything is implemented by passing, C inherits this tradition and uses it as a default. Unless explicitly specified, the function of the function is always initialized by "copy of the argument", and the caller of the function is also a copy of the function return value.

As I pointed out in the introduction of this book, the specific meaning of "passing an object" is defined by the copy constructor of the class of this object. This makes the pass value a very expensive operation. For example, look at this (just imaginary) class:

Class Person {public: Person (); // is simplified, omitted // ~ Person ();

...

Private: String name, address;

Class Student: Public Person {public: student (); // is simplified, omitted // ~ student ();

...

Private: String Schoolname, Schooladdress;

Now define a simple function returnstudent, which takes a Student parameter (via the value) and then return it immediately (also via the value). After the definition is finished, call this function:

Student ReturnStudent (student s) {return s;}

Student Plato; // Plato (Plato) Learning under // Socrates (Socrate)

ReturnStudent (Plato); // Call ReturnStudent

This seems unpaped to itching function calling process, what happened within it?

Simply put in: First, call the copy constructor of Student to initialize STUDENT to Plato; then call the copy constructor to initialize the function return value to S; then, the destructor S is called Finally, the destructor of the ReturnStudent returns the value object is called. So, the cost of this change is the cost of the two student's copy constructor plus two Student destructor.

But it's endless, there is! There are two String objects in the Student object, so two String objects must also be constructed each time you construct a Student object. The Student object is still inherited from the Person object, so a Person object must also be constructed each time constructed a Student object. There are two two String objects inside a Person object, so each Person constructed is also a consisting of the other two String. Therefore, through the value to pass a Student object, eventually causing a Student copy constructor, a Person copy constructor, four String copy constructor. When the Student object is destroyed, each constructor corresponds to the call of the destructor. Therefore, the final overhead of a STUDENT object is six constructor and six destructor functions. Because the returnStudent function uses two pass values ​​(once the parameters, the return value once), this function calls twelve constructor and twelve destructor! In the eyes of the C compiler, this is the worst situation. The compiler can be used to eliminate some calls for copy constructors (C standard - see clause 50 - describe which compilers can perform such optimization work, the clause M20 is given example). Some compilers have also done this. But in the case of all compilers, it must be vigilant to pass the values ​​to pass the values.

To avoid this potential expensive overhead, do not pass values ​​to transfer objects, but to pass reference:

Const Student & ReturnStudent (const student & s) {return

This will be very efficient: no constructor or destructor is called because there is no new object being created.

There is another advantage to pass the parameters by reference to the reference: it avoids the so-called "SLICING Problem". When a derived object is passed as a base class object, it (derived class object) is "cut" as a "cut", which has become a simple base class object. This is often not what you want. For example, assume that such a set of classes that implement graphics window systems:

Class window {public: string name () const; // Return window name Virtual Void Display () const; // Draw window content};

Class windowwithscrollbars: public window {public: Virtual void display () const;

Each Window object has a name, which can be obtained by the Name function; each window can be displayed, and it can be implemented by calling the display function. Display declares that Virtual means a simple Window base class object is displayed. The expensive WINDOWWITHSCROLLBARS object is displayed differently (see Terms 36, 37, M33).

Now suppose to write a function to print the name of the window and display this window. Below is a function written in an error:

// A function void printnameAndDisplay (Window W) {cout << w.display (); w.display ();

Imagine what will happen when calling this function with a WindowWithscrollBars object:

WINDOWWITHSCROLLBARS WWSB;

PrintNameAnddisplay (WWSB);

The parameter W will be created as a Windows object (it is transmitted by a value, remember?), All WWSB has the behavior characteristics of the WindowWithscrollbars object being "cut". PrintNameAndDisplay inside, W's behavior is like a class of Window objects (because it is a Window object), regardless of the object type that originally transmitted to the function. In particular, the call to Display is always Window :: Display instead of Windowwithscrollbars :: Display. The way to solve cutting problems is to pass W:

// A function void printnameAnddisplay (Const Window & W); w.display ();}

Now the behavior of W is consistent with the real type of the function. In order to emphasize that W Although the delivery is passed but cannot be modified inside the function, it is recommended to declare it as const.

Passing a reference is a good practice, but it will lead to its own complexity, the biggest problem is an alias issue, which is discussed in terms 17. In addition, more importantly, sometimes it is not possible to deliver an object, see Terms 23. Finally, the reference is almost all through the pointer, so the passing object is actually transmitting the pointer by reference. Therefore, if it is a small object, for example, INT - is actually more efficient than the pass reference.

转载请注明原文地址:https://www.9cbs.com/read-4460.html

New Post(0)