Http://www.cnblogs.com/xkfz007/archive/2012/07/21/2602110.html
Before an article introduced the problem of the temporary object and the return value optimization Rvo. See here.
In C + +, returning objects is often criticized because this place is inefficient, requires a lot of operations, and generates some temporary objects, which can be time-consuming if the objects are larger. However, when the compiler implements it, it is often optimized to return the object, that is, the return value is optimized.
In g++, this is already optimized by default. I used to want to see how C + + did it, but I couldn't see it because g++ made the default return value optimization Rvo. Today found in the evening can have a method to prohibit this rvo, can refer to here.
Specifically, at compile time, add-fno-elide-constructors this option, namely:
g++-O rvo_test rvo_test.cc-fno-elide-constructors
The following is an example of an optimization that C + + does when returning an object.
The code is as follows:
#include <iostream>
#include <iomanip>
using namespace Std;
int num=1;
Class a{
Public
A () {
id=count++;
Pre_id=-1;
COUT<<SETW (2) <<num++<< ": A (): id=" <<id<< "pre_id=" <<pre_id<<endl;
}
A (const a& a) {
id=count++;
Pre_id=a.id;
COUT<<SETW (2) <<num++<< ": A (const a&): id=" <<id<< "pre_id=" <<pre_id<< Endl
}
~a () {
COUT<<SETW (2) <<num++<< ": ~a (): id=" <<id<< "pre_id=" <<pre_id<<endl;
}
a& operator= (const a& A) {
Pre_id=a.id;
COUT<<SETW (2) <<num++<< ": = (const a&): id=" <<id<< "pre_id=" <<pre_id<< Endl
}
Private
static int count;
int id;
int pre_id;
};
int a::count=0;
A F () {
A;
return A;
}
A G1 (a B) {
A a=b;
return A;
}
A G2 (a B) {
A;
A=b;
return A;
}
int main () {
A b1=f ();
A b2=g1 (B1);
A b3=g2 (B1);
A c1,c2,c3;
C1=f ();
C2=G1 (C1);
C3=G2 (C1);
return 0;
}
To make it easier to distinguish each object, use the variable ID to record the label of the object. The implementation method uses a static variable count to record the number of generated classes.
The following is the result of the operation. The left part is not adopted-fno-elide-constructors this option, that is, the case of Rvo optimization, the middle part of the test code, the right is the use of-fno-elide-constructors this option, that is, do not use the Rvo case, So right is what we need to see, the real execution process of the classes that need to be analyzed. At the same time, each line of the operation is labeled for ease of comparison:
The results of the operation on the right side are analyzed as follows:
(1). For A b1=f ();
First Call the function f (), in function f:
A F () {
A;
return A;
}
First call A's default constructor a () to generate a local object a:
1:a (): Id=0 pre_id=-1
At this point a id=0, and because the return value of F is an object of a, C + + uses A to invoke the copy constructor to generate a temporary object:
2: A (const a&): Id=1 pre_id=0
This temporary object is id=1, and you need to refactor the local object A because you want to leave the function f:
3: ~a (): Id=0 pre_id=-1
In main function main, the object B1 is initialized with the return value of function f:
A b1=f ();
So call the copy constructor to initialize the B1:
4:A (const a&): id=2 pre_id=1
In this case B1 the id=2, and the previous Id=1 temporary object because the task has already been completed, so C + + has been the destruction of it:
5: ~a (): Id=1 pre_id=0
So the B1 object is constructed. In this process, the most primitive approach to C + + for the return value of an object (non-reference or pointer) is to construct a temporary object to hold the return value and then use the temporary object to manipulate it. When the task of this temporary object is completed, it is destroyed.
(2). For a b2=g1 (B1);
Call the function G1 first
A G1 (a B) {
A a=b;
return A;
}
You can see that the G1 function is a parameter, which also involves the problem of the value-passing parameter. Because it is passed by value, replication is required and the copy constructor is called.
So, first call the copy constructor and use B1 to construct the form parameter B in the G1 function:
6:A (const a&): id=3 pre_id=2
At this time the form parameter is a local variable, and its id=3. Then run:
A a=b;
This is the use of form parameter B (id=3) to invoke the copy constructor to construct the local variable a:
7:A (const a&): id=4 pre_id=3
So the id=4 of a. Then running to the return statement, you need to return an object, similar to the one described above, with a call to the copy constructor to construct a temporary object:
8:A (const a&): id=5 pre_id=4
The id=5 of this temporary object. Because you have left the function g1, you need to destroy the local variable a:
9: ~a (): id=4 pre_id=3
Then use the temporary object id=5 to construct the object B2, namely:
10:A (const a&): Id=6 pre_id=5
So get the B2 id=6. At the same time, the temporary object task is completed and needs to be destroyed:
One: ~a (): id=5 pre_id=4
At the same time, the previous form parameter id=3 also to be destroyed (here you can see that this object is destroyed more late)
: ~a (): id=3 pre_id=2
So a B2=G1 (B1); This code is finished: The B2 construct is complete and all the temporary variables are destroyed.
(3). For a b3=g2 (B1);
Run the function G2:
A G2 (a B) {
A;
A=b;
return A;
}
The difference between G2 and G1 is that assignments are made in G2 instead of directly calling the copy constructor to generate local variables.
Similarly, because it is passed by value, the copy constructor is invoked with B1 to initialize form parameter B.
13:A (const a&): Id=7 pre_id=2
The id=7 of the local variables generated by the form parameter B.
Run A; Call the default constructor to generate a local variable a
14:a (): Id=8 pre_id=-1
The id=8 of the local variable A. then run a=b; the assignment operator needs to be called here:
: = (const a&): Id=8 pre_id=7
Assign B (id=7) to a. Then run the return statement and use A to invoke the copy constructor to construct a temporary object
16:A (const a&): Id=9 pre_id=8
The id=9 of the temporary object. Temporary object A (id=8) needs to be destroyed because it is leaving the function:
: ~a (): Id=8 pre_id=7
After destroying the temporary object, use the temporary variable id=9 to invoke the copy constructor to construct the B3:
18:A (const a&): id=10 pre_id=9
So get B3 (id=10). The temporary variables will be destroyed in the call to G2, and the temporary variable id=9 is destroyed first:
: ~a (): id=9 pre_id=8
Re-destroy the temporary variable generated by the form parameter B id=7 (also the latest destroy form parameter)
: ~a (): id=7 pre_id=2
In this case a B3=G2 (B1); This sentence is finished running, got the object B3 (id=10)
(4) The following sentence runs in the same way as the first 3 sentences, except that the default constructor is called to generate the object, and then the assignment operator is called to assign the value.
A c1,c2,c3;
C1=f ();
C2=G1 (C1);
C3=G2 (C1);
The following three sentences first call the default constructor to generate C1,C2,C3:
21:a (): id=11 pre_id=-1
22:a (): id=12 pre_id=-1
23:a (): id=13 pre_id=-1
This generates the C1 (id=11), C2 (id=12), C3 (Id=13).
The following five sentences are c1=f (); corresponding running results:
24:a (): id=14 pre_id=-1
25:A (const a&): Id=15 pre_id=14
: ~a (): id=14 pre_id=-1
: = (const a&): id=11 pre_id=15
: ~a (): id=15 pre_id=14
The following seven sentences are run C2=G1 (C1);
29:A (const a&): Id=16 pre_id=11
30:A (const a&): Id=17 pre_id=16
31:A (const a&): Id=18 pre_id=17
: ~a (): Id=17 pre_id=16
: = (const a&): id=12 pre_id=18
: ~a (): Id=18 pre_id=17
: ~a (): Id=16 pre_id=11
The following eight sentences are run C3=G2 (C1);
36:A (const a&): id=19 pre_id=11
37:a (): Id=20 pre_id=-1
a&: = (const): id=20 pre_id=19
39:A (const a&): id=21 pre_id=20
Max: ~a (): Id=20 pre_id=19
In: = (const a&): Id=13 pre_id=21
: ~a (): id=21 pre_id=20
: ~a (): id=19 pre_id=11
In this case, all the normal statements have been run.
The following is because the main function is going to be returned, so some variables are destroyed:
: ~a (): id=13 pre_id=21//Destroying Object C3 (Id=13)
: ~a (): id=12 pre_id=18//Destroying Object C2 (id=12)
: ~a (): id=11 pre_id=15//Destroying Object C2 (id=11)
~a (): id=10 pre_id=9//Destroying Object B3 (id=10)
: ~a (): id=6 pre_id=5//Destroying Object B2 (id=6)
~a (): id=2 pre_id=1////Destroying Object B3 (id=2)
At this point, the program ends normally.
A brief analysis of the operation results on the left, that is, the use of Rvo:
From the results of the operation, the use of Rvo can optimize a lot of steps compared to not using the Rvo situation:
(1) for a b1=f ();
A F () {
A;
return A;
}
The following sentence is the corresponding running result:
1:a (): Id=0 pre_id=-1
Normally it should be: Use the default constructor in function f to construct the local object A, and then directly initialize the B1 with a call to the copy constructor. This eliminates the case of generating temporary variables.
And here with a further optimization, it is found that the F only returns an object, so it is directly equivalent to using the default constructor to initialize the object B1, so that B1 (id=0) is obtained. Even the generation of temporary object a
(2) for a b2=g1 (B1);
A G1 (a B) {
A a=b;
return A;
}
The following three sentences are the result of the operation:
2: A (const a&): Id=1 pre_id=0
3:A (const a&): id=2 pre_id=1
4: ~a (): Id=1 pre_id=0
First Use B1 (id=0) to initial taxiing parameter B (id=1), and then because the discovery G1 is directly return local variable A, so omit the generation of a, directly using B (id=1) to initialize B2, get B2 (id=2).
(3) for a b3=g2 (B1);
A G2 (a B) {
A;
A=b;
return A;
The following three sentences are the result of the operation:
5:A (const a&): id=3 pre_id=0
6:a (): id=4 pre_id=-1
7: = (const a&): id=4 pre_id=3
8: ~a (): id=3 pre_id=0
Here is the first use of B1 (id=0) to copy the constructor parameter B (id=3), then the direct default construct to get B3 (id=4) (equivalent to directly take advantage of the generation of a), and then call the assignment operator, from B (id=3) to get the value. Finally, the B (id=3) is reconstructed.
(4) for a c1,c2,c3;
correspond to the following three sentences:
9:a (): id=5 pre_id=-1
10:a (): id=6 pre_id=-1
11:a (): id=7 pre_id=-1
There is no optimization, direct C1 (id=5), C2 (id=6), C3 (Id=7)
(5) for the following three sentences:
C1=f ();
C2=G1 (C1);
C3=G2 (C1);
The following sentence is the corresponding result:
12:a (): Id=8 pre_id=-1//because of the need to use the return value of F for assignment operations, calling the default constructor in F directly generates a temporary variable (equivalent to omitting a local variable a) id=8
: = (const a&): id=5 pre_id=8//uses a temporary variable id=8 to assign a value to C1 (id=5).
: ~a (): id=8 pre_id=-1//Temporary variable id=8 complete task, destroy
15:A (const a&): id=9 pre_id=5//using C1 (id=5) to copy the constructor parameter B (id=9)
16:A (const a&): id=10 pre_id=9//uses form parameter B (id=9) copy to construct a temporary variable id=10 (omit local variable A)
+: = (const a&): id=6 pre_id=10//Use temporary variable id=10 to assign operations to C2 (id=6)
: ~a (): id=10 pre_id=9//destroy temporary variable id=10
: ~a (): id=9 pre_id=5//Destroy form parameter B corresponds to temporary variable id=9
20:A (const a&): id=11 pre_id=5//using C1 (id=5) to copy the constructor parameter B (id=11)
21:a (): id=12 pre_id=-1//Because of the assignment operation in the G2 function and the need to return a local variable A, here is the construction of the local variable A, directly constructs a temporary variable id=12
: = (const a&): id=12 pre_id=11//uses form parameter B (id=11) to assign a temporary variable id=12.
Max: = (const a&): id=7 pre_id=12//uses temporary variable id=12 to assign C3 (Id=7).
: ~a (): id=12 pre_id=11//Temporary variable id=12 complete task, destroy
: ~a (): id=11 pre_id=5//Destroy form parameter B corresponds to temporary variable id=11
Here is the program that is about to run and destroy all local variables in the main function:
: ~a (): Id=7 pre_id=12//Destroy Object C3 (Id=7)
~a (): id=6 pre_id=10//Destroying Object C2 (id=6)
: ~a (): id=5 pre_id=8//Destroying Object C1 (id=5)
: ~a (): id=4 pre_id=3//Destroying Object B3 (id=4)
: ~a (): id=2 pre_id=1//Destroying Object B2 (id=2)
To: ~a (): Id=0 pre_id=-1//Destroy Object B1 (id=0)
As can be seen from the above analysis, the RVO is g++ compiler has done a lot of optimization.
In order to compare the output of the two cases and the corresponding relationship between the source code, the corresponding statements are marked with the same color:
Go The case and Rvo of returning objects in C + +