C ++ object model: How does a compiler process a function to return an object?

Source: Internet
Author: User

C ++ object model: How does a compiler process a function to return an object?
1. output that is inconsistent with experience we know that when one of the following three situations occurs, the copy constructor of the class corresponding to the object will be called: 1) during initialization of an object display
2) When the object is passed to a function as a parameter 3) when the function returns an object of the class
Therefore, when designing a function (common or member function), experience tells us that for efficiency, we should try to return a pointer or reference to an object, rather than directly returning an object. If an object is directly returned, it may lead to the copying and construction process of the object. This means that a certain amount of memory replication and object creation actions will occur, reducing the program efficiency. This design idea is correct, but in fact, when a function returns an object, will the above replication construction process certainly happen?
For example, for the following code:

class X{    public:        X()        {            mData = 100;            cout << "X::X()" << endl;        }        X(const X& rhs)        {            mData = rhs.mData;            cout << "X::X(const X&rhs)" << endl;        }        void setData(int n)        {            mData = n;        }        void print()        {            cout << "X::mData == " << mData << endl;        }    private:        int mData;};X func(){    X xx;    xx.setData(101);    return xx;}int main(){    X xx = func();    return 0;}

After reading the code snippet above, what do you think the program should output? If the analysis is based on the statement in the book, a local object xx of Class X is defined in the func function, so the constructor of Class X will be called; if the return value returned by the func function is an object, the function returns a copy of object xx, so the copy constructor of Class X will be called. In the main function, A local object xx of Class X is also defined, and the object is constructed by using the object returned by function func as the initial value. Therefore, copying Class X is called to construct the copy. That is to say, according to this analysis, the input result should be: X ()
X: X (const X & rhs)
X: X (const X & rhs)

I also thought that the output should be the above three rows, but the actual running result is shown in, which is totally unexpected:

From the running results, only one line of "X: X ()" is output, that is, it only constructs an X-class object without any replication construction process. Our analysis basis is correct, the program code is very simple, and the analysis process is also correct, but why is the program behavior inconsistent with our analysis? In fact, the compiler secretly modifies the code of our program for optimization considerations. Next we will first introduce a method for the compiler to process returned objects, and then introduce how the optimization of the compiler has moved our code.
2. A method used by the compiler to process returned objects. After a function is called, its local objects will be destroyed. If the function returns a local object, how does the compiler copy this local object? The method is as follows: 1) First add an additional parameter to the function. The type is the reference of the Class Object. This parameter is used to store the returned values obtained by "replication Construction". 2) Insert a copy construction call operation before the return command, so that the content of the object to be returned is treated as the initial value of the new parameter.
The two operations of this method will rewrite the function so that it does not return any value. According to this method, the operation of the func function may be converted to the following pseudo code:
// C ++ pseudocode, simulating the call of constructor and copying constructor void func (X & __result) {X xx; xx. x: X (); // call the default constructor xx of class X. setData (101); _ result. x: X (xx); // call the copy constructor return of Class X ;}

Now the compiler must convert every func () call operation to conform to its new definition, that is, X xx = func ();
Will be converted into the following statement: X xx; // The constructor func (xx) of Class X is not called );
Therefore, the main function is converted to the following pseudo code:
// C ++ pseudo code, simulating the call of constructor and copying constructor int main () {X xx; // it does not call the constructor func (xx) of Class X ); return 0 ;}

According to the above compiler operations, we can get the following output: X ()
X: X (const X & rhs) The first behavior is the construction of the local object xx in the func function, and the second behavior is the replication construction operation to achieve the returned goal. Although this result is different from the analysis in section 1st, using this method by compilation can reduce the call of a copy constructor and improve the efficiency, after all, it is no good for copying the same object twice. This program will be further optimized by compilation, which will be detailed in section 3rd.
Another use case of function func is to directly use the return value of function func without assigning it to a variable. Modify the main function as follows:
int main(){    func().print();    return 0;}

When the above problem occurs, the compiler may perform the following conversions to make the code run correctly:
// C ++ pseudo code, simulating the compiler's related processing operations int main () {X _ temp; func (_ temp); _ temp. print ();}}

In the book "Deep Exploration of C ++ object models", for this case, the converted code is not placed in curly brackets, but I personally think this is more reasonable. According to the definition of C ++, the func function returns a temporary object, so when the statement func (). print ();
After running, the temporary variable should be destroyed. Put the converted statement in a pair of curly brackets. After the converted statement is run, the _ temp temporary variable will be destroyed due to the scope. But I don't know if the compiler does this.
Similarly, if the program defines a function pointer variable and points to the function, the compiler also needs to rewrite the definition of the function pointer.
3. In section 2nd, NRV optimization has analyzed how the compiler processes the function that returns an object, but the result is still different from the output of our program, this is because the compiler further optimizes the program. The method is to replace the return value name (named return value) with the parameter referenced by the added Class Object (result parameter ).
Using this policy, the func function is converted to the following pseudo code:
// C ++ pseudocode, simulating constructor and copying constructor optimization void func (X & __ result) {_ result. x: X (); // call the default constructor of Class X _ result. setData (101); return ;}

The pseudo code after main Function Conversion remains unchanged, as shown below:
Int main () {X xx; // The constructor func (xx) of Class X is not called; return 0 ;}

Through this optimization, we can see that during the calling of the main function, only one constructor will be called and no replication constructor will be called. This compilation optimization operation is called Named Return Value (NRV) optimization. NRV optimization is now regarded as an unshirkable optimization operation of the Standard C ++ compiler. The unexpected output produced in section 1st is the result of NRV optimization.
Although NRV optimization provides an important improvement in efficiency, it is not clear whether the optimization is completed by the compiler silently. Once the function becomes more complex, optimization is also difficult to implement.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.