C ++: Is the destructor called the outer scope of the object?

Update 1: Added the print of "this" as suggested.

Update 2: Separate multiple files to try and stop gcc from optimizing.

Update 3: Recorded the copy constructor and introduced add functions.

Update 4: Added output from Clang and second cout basically.

I expect parameter destructors to be called as the last statements in the function. From now on, I expect the following output from the code below.

default constructor: 008DFCF8 other constructor: 008DFCEC copy constructor: 008DFBC0 in member add destroying: 008DFBC0 copy constructor: 008DFBB8 copy constructor: 008DFBB4 in function add destroying: 008DFBB4 destroying: 008DFBB8 3 == 3 end of main destroying: 008DFCEC destroying: 008DFCF8 

When using MSVC (Visual Studio), the output is as expected. But GCC (4.8.2-19ubuntu1) displays the following, which shows that destructors for function parameters are called after the first cout statement in main (), but until the last.

 default constructor: 0x7fff2fcea510 other constructor: 0x7fff2fcea520 copy constructor: 0x7fff2fcea550 in member add copy constructor: 0x7fff2fcea540 copy constructor: 0x7fff2fcea530 in function add 3 == 3 destroying: 0x7fff2fcea530 destroying: 0x7fff2fcea540 destroying: 0x7fff2fcea550 end of main destroying: 0x7fff2fcea520 destroying: 0x7fff2fcea510 

For those who are interested in what clang ++ (3.4-1ubuntu3) produces.

 default constructor: 0x7fff52cf9878 other constructor: 0x7fff52cf9870 copy constructor: 0x7fff52cf9860 copy constructor: 0x7fff52cf9858 in function add 3 == copy constructor: 0x7fff52cf9850 in member add 3 destroying: 0x7fff52cf9850 destroying: 0x7fff52cf9858 destroying: 0x7fff52cf9860 end of main destroying: 0x7fff52cf9870 destroying: 0x7fff52cf9878 

Questions:

  • My initial suspicion is that GCC is inserting functions? If true, is there a way to disable this optimization?
  • Which section in the C ++ specification will allow destructors to be called after the main output of cout? Nesting rules are especially interesting if necessary, and when destructors are planned.

 // Test.h #ifndef __TEST_H__ #include <iostream> using namespace std; class Test { public: int val; Test(Test const &a) : val(a.val) { cout << "copy constructor: " << this << endl; } Test() : val(1) { cout << "default constructor: " << this << endl; } Test(int val) : val(val) { cout << "other constructor: " << this << endl; } ~Test() { cout << "destroying: " << this << endl; } int add(Test b); }; #endif 

 // Add.cpp #include "Test.h" int Test::add(Test b) { cout << "in member add" << endl; return val + b.val; } int add(Test a, Test b) { cout << "in function add" << endl; return a.val + b.val; } 

 // Main.cpp #include "Test.h" int add(Test a, Test b); int main() { Test one, two(2); cout << add(one, two) << " == " << one.add(two) << endl; cout << "end of main" << endl; return 0; } 

Compiled for GCC using:

 g++ -c Add.cpp -o Add.o ; g++ -c Main.cpp -o Main.o ; g++ Add.o Main.o -o test 
+5
source share
3 answers

It seems that the C ++ standard may be a bit ambiguous as to when to call function parameter destructors. C ++ 03 and C ++ 11 say in 5.2.2 / 4 “Function Call” (emphasis added):

The parameter's lifetime expires when the function in which it is found returns certain. The initialization and destruction of each parameter occurs in the context of the call function.

Thus, the parameter destructor does not occur conceptually when closing the function. This is what I did not know.

The standard provides a note explaining how this means that if a destructor for a parameter generates, then only exception handlers for the calling function or “higher” are considered (in particular, even if the called function has the function “block-function”, this is not considered).

Although I think that the intention is for the behavior of MSVC, I could see how someone could interpret the reading, which allows the behavior of GCC.

And again, maybe this is a mistake in GCC?

+2
source

You create temporary objects when you call add (a, b) and when you call the add (b) member. I think what you see in the case of gcc is that local variables in add () functions (parameters) are destroyed when these functions are returned. The last two lines are done for the variables one and two.

VC is different - but it’s not, it just shows that the two compilers optimize the code differently.

Instead of just typing done, try also printing the value of this. Print "this" in the constructor, and then you will see how paired calls and destructors are connected.

Unfortunately, I messed up a bit between VC and GCC. VC prints "done" three times at first - presumably since the add () parameters are destroyed, and GCC does all this last, probably because it has nested add functions.

+2
source

Consider this line:

 cout << add(one, two) << " == " << one.add(two) << endl; 

It is written as:

 cout << add(one, two); cout << " == " << one.add(two) << endl; 

Will this change the printout for GCC?

Or so:

 auto i = add(one, two); cout << i << " == "; auto j = one.add(two) cout << j << endl; 

I think this is about side effects (not inlining). It seems that VC plans side effects (destruction of temporary objects) earlier, whereas GCC plans it at the end of the instruction - ;


Quote Added:

The temporary lifetime of the object

Temporary objects are created in various situations: binding a reference to prvalue, returning a function value from a function, throwing it to prvalue, throwing an exception, introducing an exception handler and some initializations. In each case, all temporary files are destroyed as the last step in evaluating the full expression , which (lexically) contains the point at which they were created, and if several temporary files were created, they are destroyed in the opposite order of creation. This is true even if this estimate ends up throwing an exception.

In my opinion, this is talking about GCC and against VC (especially about printing "3 == 3" after destruction, this is strange for me).

+2
source

Source: https://habr.com/ru/post/1204232/


All Articles