Transfer: Temporary object and return value optimization in C + +

Source: Internet
Author: User

Http://www.cnblogs.com/xkfz007/articles/2506022.html

What is a temporary object?

C + + A real temporary object is an invisible anonymous object that does not appear in your source code, but the program does generate such an object at run time.

Typically occurs in the following two scenarios:

(1) in order to make the function call successful, the implicit type conversion .

Passing an object to a function, whose type is different from the formal parameter type of the function, can make the function call successful if it can be implicitly converted, then a temporary object is generated by the constructor and the temporary object is automatically destroyed when the function returns. The following example:





Call the function above
Countchar (buffer, c);

The first parameter we see is char[], and the function's parameter type is const STRING&, and the arguments are inconsistent to see if implicit conversions are possible, and the string class has a constructor that can act as an implicit conversion function (see 5). Then the compiler produces a temporary variable of string, constructed in buffer for the argument, then the str parameter in Countchar is bound to this temporary variable until the function returns.

Note that such conversions only occur in two cases: the function parameter is passed as a value (by value) or the object is passed to a reference-to-const parameter.

Pass-through Value method:


string buffer;
char c;

Countchar (buffer, c);

This method invokes the copy constructor of string to generate a temporary variable and binds the temporary variable to STR, which is destroyed when the function returns.

To pass a constant reference:

The starting instance is immediately in this case, but it is important to emphasize that a const type reference is passed, such as changing the prototype of the starting function to

int Countchar (string& str, char ch);

The following call is the same, the compiler will error! Why does C + + design require implicit type conversions when an object is passed to a reference-to-non-const parameter?

The following example may show you the purpose of this design:




ToUpper (buffer); error!! Non-const Reference pass parameters cannot complete implicit conversions

If the compiler allows the above pass to be completed, a temporary object is generated, and the ToUpper function converts the character of the temporary variable to uppercase, returning to destroying the object, but without affecting the contents of the buffer! The purpose of the program is to modify the "non-temporal object", and if the Reference-to-non-cosnt object is converted, the function will only modify the temporary variable. This is why the Non-const-reference parameter is forbidden in C + + to produce temporary variables.

(2) When the function returns an object .

When a function returns an object, the compiler generates a temporary object to return, such as declaring a function to merge two strings:

Const string Strmerge (const string s1, const string s2);
Most of the time it is impossible to avoid such temporary variables, but the modern compiler can optimize such temporary variables, such an optimization strategy, there is a so-called "return value Optimization", the next specific explanation.
Summary:
Temporary objects have the cost of construction and destruction, affect the efficiency of the program, and therefore eliminate them as much as possible. It is more important to find out quickly where a temporary object will be generated:
    • When we see a reference-to-const parameter, it is very likely that a temporary object is bound to that parameter;
    • When we see that the function returns an object, a temporary object is generated.

Reference: http://www.cnblogs.com/hazir/archive/2012/04/18/2456144.html

return value optimization in C + + (return value optimization) returns an optimization (return value optimization, referred to as Rvo), is an optimization mechanism: When a function needs to return an object, If you create a temporary object that the user returns, then this temporary object consumes the cost of a call to a constructor (Constructor), a call to a copy constructor (copy Constructor), and a destructor (destructor). And if you do a little bit of optimization, you can reduce the cost to a constructor, the following is a test in the debug mode of Visual Studio 2008: (At the time of testing under GCC, the compiler may have Rvo optimization, see the difference between the two types of code)C + + Return Value optimization
Code Maniac
Blog: http://www.programlife.net/
#include <iostream>
using namespace Std;
Class Rational
{
Public
Rational (int numerator = 0, int denominator = 1): N (Numerator), d (denominator) {
cout << "Constructor called ..." << Endl;
}
~rational () {
cout << "destructor called ..." << Endl;
}
Rational (const rational& RHS) {
This->d = RHS.D;
This->n = RHS.N;
cout << "Copy Constructor called ..." << Endl;
}
int numerator () const {return n;}
int denominator () const {return D;}
Private
int N, D;
};
Const Rational operator* (const rational& LHS, const rational& RHS) {
cout << "-----------Enter operator*-----------" << Endl;
Rational tmp (Lhs.numerator () * Rhs.numerator (),
Lhs.denominator () * Rhs.denominator ());
cout << "-----------Leave operator*-----------" << Endl;
return TMP;
}
int main (int argc, char **argv) {
Rational x (1, 5), Y (2, 9);
Rational z = x * y;
cout << "Calc Result:" << z.numerator ()
<< "/" << Z.denominator () << Endl;

return 0;
}

The function output is as follows:

You can see the cost of a call that consumes a constructor (Constructor), a call to a copy constructor (copy Constructor), and a destructor (destructor).

And if the operator* is replaced by another form:

Const Rational operator* (const rational& lhs,const rational& RHS)
{
Return Rational (Lhs.numerator () * Rhs.numerator (),
Lhs.denominator () * Rhs.denominator ());
}

It consumes only the cost of one constructor:

Reference: http://www.programlife.net/cpp-return-value-optimization.html return value optimization (RVO) with return value optimization (NRVO)

This is a compiler to do optimization, is already a very common optimization method, search can find a lot of information, on MSDN also have relevant instructions.

The return value optimization, as the name implies, is the optimization associated with the return value, which is optimized to avoid unnecessary temporary objects and copy of values when the function is returned by value (not as a reference, pointer).

First look at the following code:

typedef unsigned int UINT32;
Class Mycla
{
Public
MYCLA (UINT32 a_size = ten): Size (a_size) {
p = new Uint32[size];
}
MYCLA (Mycla const & a_right): Size (a_right.size) {
p = new Uint32[size];
memcpy (P, A_RIGHT.P, size*sizeof (UINT32));
}
MYCLA const& operator = (mycla const & a_right) {
size = A_right.size;
p = new Uint32[size];
memcpy (P, A_RIGHT.P, size*sizeof (UINT32));
return *this;
}
~mycla () {
delete [] p;
}
Private
UINT32 *p;
UINT32 size;
};
Mycla Testfun () {
return Mycla ();
}
int _tmain (int argc, _tchar* argv[])
{
Mycla a = Testfun ();
return 0;
}The Testfun () function returns a Mycla object, and is passed by value.

Before any optimization, this code might behave like this: return Mycla () in this line of code, constructs a temporary nameless object of the Mycla class (call it T1), and then copies T1 to another temporary object T2 (not on the stack). Then the function saves the address of the T2 (placed in the EAX register) and returns, the Testfun stack interval is "undone" (then T1 "No", T1 's survival domain in Testfun, so was destroyed), in Mycla a = Testfun (); In this sentence, a uses the address of the T2, can be found T2, followed by construction. So the construction process for a is complete. And then the T2 also "kill".

It can be seen that in this process, T1 and T2 the existence of the two temporary objects is very wasteful, occupy space does not say, the key is that they are only for the construction of a and the existence of a structure after the end of life. Since these two temporary objects are simply "invisible to the programmer" (anonymous objects), the compiler simply does something in it and does not generate them! How do you do it? Quite simply, the compiler "secretly" adds a parameter mycla& to the Testfun function we write, and then passes in the address of a (note that the memory space for a is already present, but the object has not been "constructed", that is, the constructor has not been called), and then inside the function body, Using a instead of the original "anonymous object", the structure of a is completed inside the function body. This saves the overhead of two temporary variables. This is called "Return value optimization"! In VC7, this is done by default when returning anonymous objects by value.

It says "return value optimization (RVO)" and a "named Return Value Optimization (NRVO)", which is for returning "named Object" by value (that is, a variable with a name!). When the optimization means, in fact, the same, but because the return value is a named variable, the situation is much more complex, so the conditions to perform the optimization is more stringent, in the following three cases (from MSDN), NRVO will certainly not work:

    1. Returns an object of different names on a different return path (for example, if XXX returns X,else when returning to Y)
    2. Multiple return paths to the EH state are introduced (even if all paths return the same named object)
    3. The returned object name is referenced in an inline ASM statement.

However, even if the NRVO can not be carried out, in the above description of the T2 this temporary variable will not be generated, for the VC C + + compiler, as long as you write the program is to return the object by value, it will have two ways to avoid the generation of T2. Take the following procedure to illustrate:

Mycla TestFun2 () {
Mycla x (3);
return x;
One approach is to use the variable A that gets the return value as an expression as a reference parameter in the function, and then copy construct a with the variable to be returned before returning the statement, and then rvo the variable, and the function returns to the original call point, A is constructed.

There is also a way, when the function returns, do not destructor X, and directly put the address of X into the Exa register, return to the TESTFUN2 call point, at this time, a can be used in the Exa address to construct, after a construction, then the original variable x! Yes, notice in fact at this time, X's survival domain has exceeded the TestFun2, but because here X is the TestFun2 stack, although it is invalid, but no one to erase the save, so X is still effective, of course, all in the compilation of the level, for the C + + language level is transparent.

Reference: http://www.cnblogs.com/liyiwen/archive/2009/12/02/1615711.html

typedef unsigned int UINT32;
Class Mycla
{
Public
MYCLA (UINT32 a_size = ten): Size (a_size) {
cout << "-----------Enter Defalut constructor mycla-----------" << Endl;
p = new Uint32[size];
}
MYCLA (Mycla const & a_right): Size (a_right.size) {
cout << "-----------Enter copy constructor mycla-----------" << Endl;
p = new Uint32[size];
memcpy (P, A_RIGHT.P, size*sizeof (UINT32));
}
MYCLA const& operator = (mycla const & a_right) {
cout << "-----------Enter operator = Mycla-----------" << Endl;
size = A_right.size;
p = new Uint32[size];
memcpy (P, A_RIGHT.P, size*sizeof (UINT32));
return *this;
}
~mycla () {
cout << "-----------Enter~mycla ()-----------" << Endl;
delete [] p;
}
Private
UINT32 *p;
UINT32 size;
};
Mycla Testfun () {
Mycla A;
cout << "-----------entertestfun ()-----------" << Endl;
return A;
};
int _tmain (int argc, _tchar* argv[])
{
Mycla A;
cout << "-----------before Entertestfun ()-----------" << Endl;
A = Testfun ();
cout << "-----------after Entertestfun ()-----------" << Endl;
return 0;
}

D:\source\c++\projects\sizeof1\debug>sizeof1.exe
-----------Enter Defalut Constructor Mycla-----------
-----------before Entertestfun ()-----------
-----------Enter Defalut Constructor Mycla-----------
-----------Entertestfun ()-----------
-----------Enter copy Constructor Mycla-----------
-----------Enter~mycla ()-----------
-----------Enter operator = Mycla-----------
-----------Enter~mycla ()-----------
-----------after Entertestfun ()-----------
-----------Enter~mycla ()-----------

typedef unsigned int UINT32;
Class Mycla
{
Public
MYCLA (UINT32 a_size = ten): Size (a_size) {
cout << "-----------Enter Defalut constructor mycla-----------" << Endl;
p = new Uint32[size];
}
MYCLA (Mycla const & a_right): Size (a_right.size) {
cout << "-----------Enter copy constructor mycla-----------" << Endl;
p = new Uint32[size];
memcpy (P, A_RIGHT.P, size*sizeof (UINT32));
}
MYCLA const& operator = (mycla const & a_right) {
cout << "-----------Enter operator = Mycla-----------" << Endl;
size = A_right.size;
p = new Uint32[size];
memcpy (P, A_RIGHT.P, size*sizeof (UINT32));
return *this;
}
~mycla () {
cout << "-----------Enter~mycla ()-----------" << Endl;
delete [] p;
}
Private
UINT32 *p;
UINT32 size;
};
Mycla Testfun () {
cout << "-----------entertestfun ()-----------" << Endl;
Mycla A;
return A;
};
int _tmain (int argc, _tchar* argv[])
{
Mycla a = Testfun ();
cout << "-----------before Entertestfun ()-----------" << Endl;
cout << "-----------after Entertestfun ()-----------" << Endl;
return 0;
}

D:\source\c++\projects\sizeof1\debug>sizeof1.exe
-----------Entertestfun ()-----------
-----------Enter Defalut Constructor MYCLA-----
-----------Enter copy Constructor Mycla--------
-----------Enter~mycla ()-----------
-----------before Entertestfun ()-----------
-----------after Entertestfun ()-----------
-----------Enter~mycla ()-----------

typedef unsigned int UINT32;
Class Mycla
{
Public
MYCLA (UINT32 a_size = ten): Size (a_size) {
cout << "-----------Enter Defalut constructor mycla-----------" << Endl;
p = new Uint32[size];
}
MYCLA (Mycla const & a_right): Size (a_right.size) {
cout << "-----------Enter copy constructor mycla-----------" << Endl;
p = new Uint32[size];
memcpy (P, A_RIGHT.P, size*sizeof (UINT32));
}
MYCLA const& operator = (mycla const & a_right) {
cout << "-----------Enter operator = Mycla-----------" << Endl;
size = A_right.size;
p = new Uint32[size];
memcpy (P, A_RIGHT.P, size*sizeof (UINT32));
return *this;
}
~mycla () {
cout << "-----------Enter~mycla ()-----------" << Endl;
delete [] p;
}
Private
UINT32 *p;
UINT32 size;
};
Mycla Testfun () {
cout << "-----------entertestfun ()-----------" << Endl;

return Mycla (30);
};
int _tmain (int argc, _tchar* argv[])
{
Mycla a = Testfun ();
cout << "-----------before Entertestfun ()-----------" << Endl;
cout << "-----------after Entertestfun ()-----------" << Endl;
return 0;
}

D:\source\c++\projects\sizeof1\debug>
D:\source\c++\projects\sizeof1\debug>
D:\source\c++\projects\sizeof1\debug>sizeof1.exe
-----------Entertestfun ()-----------
-----------Enter Defalut Constructor Mycla-----------
-----------before Entertestfun ()-----------
-----------after Entertestfun ()-----------
-----------Enter~mycla ()-----------

Transfer: Temporary object and return value optimization in C + +

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.