C ++ stack based constructor / destructor not working properly

It’s hard for me to understand why the following code does not create or destroy the two objects that I create, as I expected:

#include <iostream> class MyClass { int myVar; public: MyClass(int x) { myVar = x; std::cout << "constructing " << myVar << ", " << (long)this << std::endl; } ~MyClass() { std::cout << "destructing " << myVar << ", " << (long)this << std::endl; } }; int main(int argc, const char * argv[]) { MyClass a = MyClass(1); a = MyClass(2); return 0; } 

I would think that inside main, I first create an object with a value of 1, and then create a new one with a value of 2. And each of the objects will be created and destroyed, so I expect to see the following output

 constructing 1, 3456 constructing 2, 6789 destructing 1, 3456 destructing 2, 6789 

However, I get the following:

 constructing 1, 3456 constructing 2, 6789 destructing 2, 6789 <- note the "2" destructing 2, 3456 

Update: I added the output of the object address (this) to better see which object is doing.

When I use the "new MyClass" instead, I do not come across this strange effect.

What causes this, and, understanding my goal, how to avoid such mistakes in the future?

While this example looks harmless, I ran into crashes of my code because I selected other objects in the constructor and freed them in the destructor. This led to the release of objects when the object was still in use.

Conclusion

Now that all my questions are answered, let me summarize:

  • In the above example, I use "myVar", which does not even show the problem that made me address this issue. My apologies for this.
  • The actual problem with the code was that I did not use a simple int var, but the array that I created with "new" in the destructor and freed up with deletion in the destructor. And with this, the array will be deleted twice, which will lead to incorrect data in my program.
  • The fix is ​​not to use a simple pointer to an array, but a reference count pointer, so that when it is copied by an assignment operator, it increases the refcount, thereby preventing it from being released prematurely.
  • In general, the effect that I showed here is nothing dangerous - it does not cause anything, because I got the impression. The dangerous part was that I did not use ref counting ptrs.
+4
source share
8 answers

The question was answered, but I think I should explain why the author’s real project crashes.

We have some class containing some object, this object is created in the constructor and deleted in the destructor:

 class SomeClass { public: SomeClass(int param) { mObject = new SomeObj(param); } ~SomeClass() { delete mObject; } private: SomeObj * mObject; } 

When we do something like

 int main(int argc, const char * argv[]) { SomeClass a = SomeClass(1);//1 a = SomeClass(2);//2 return 0;//3 } 

We call the SomeObj constructor on line 1, and then on line2. After that we call
SomeClass::operator=(SomeClass& rhs) ,
which is auto-generated for us, his body is just
{ mObject = rhs.mObject; }
So what do we see?

 object1.mObject = object2.mObject; //old object1.mObject is leaked now, we have no pointer to it. delete object2; // it was temporary, its lifetime is just one line of code //it calls delete object2.mObject; // it equals to delete object1.mObject, because both pointers point to same object delete object1;//after end of main() //it calls delete object1.mObject; // ERROR! object was deleted 

So it costs nothing with C ++;)

+1
source

String a = MyClass(2); it will not call the destructor, it will call the assignment operator ( MyClass::operator= ), which you did not implement, so the compiler provides it for you - it does not "print" anything, so you don’t see this.

The reason you get destrucing 2 twice is because right after the line a = MyClass(2); temporary object MyClass(2) destroyed. Then at the end of main variable a destroyed, and since myVar now 2, it prints 2 again.

+11
source
 a = MyClass(2); 

Uses the copy assignment operator= provided by the compiler. That is why you see destructing 2 .

So, during copying a.myVar gets the value 2 instead of 1 .

The temporary object is destroyed after the semicolon of the line:

 a = MyClass(2); // ^- Here 

And at the end of block a also collapses.


The whole process is here:

 int main(int argc, const char * argv[]) { MyClass a = MyClass(1); // Create an object a = MyClass(2); // Create a temporary object and use the operator= to proceed to the copy, now a.intVar = 2 // ^- Here the temporary object is destructed return 0; } // a is now destructed 
+3
source

The cout instructions you created should be considered middle-level debugging tools that are useful for understanding what is happening under the hood of a C ++ program (without actually diving into the low-level build code). I changed your published code a bit, replacing the default constructor created by the compiler and the assignment operator with those that work just as well as those created by the compiler (which alone would be sufficient if you did not add cout statements to see what happens under the hood) ....

 #include <iostream> class MyClass { int myVar; public: MyClass(int x) { myVar = x; std::cout << " constructing " << myVar << ", " << this << std::endl; } ~MyClass() { std::cout << " destructing " << myVar << ", at " << this << std::endl; } MyClass() { myVar = 999; std::cout << " constructing " << myVar << ", at " << this << std::endl; } MyClass& operator=(const MyClass& rhs) { std::cout << " object " << myVar << " (at " << this << ") = object " << rhs.myVar << " (at " << &rhs << ")\n"; myVar = rhs.myVar; return *this; } friend std::ostream& operator<<(std::ostream& s, const MyClass& m); }; std::ostream& operator<<(std::ostream& s, const MyClass& m) { s << m.myVar; } int main(int argc, const char * argv[]) { MyClass a = MyClass(1); // <---- the way you initialize 'a' // MyClass a(1); // // <---- another way to initialize 'a' std::cout << "Variable 'a' is now: " << a << "\n"; std::cout << "Now setting 'a' to 2...\n"; a = MyClass(2); std::cout << "Variable 'a' is now: " << a << "\n"; return 0; } 

Coded in this way, I stepped back from your middle-level cout debugging instructions to the right and added cout instructions (which are not indented) to show what the programmer usually looks for if they don't perform mid-level debugging. When I run this, I get following:

  constructing 1, 0xbfcbfb48 Variable 'a' is now: 1 Now setting 'a' to 2... constructing 2, 0xbfcbfb4c object 1 (at 0xbfcbfb48) = object 2 (at 0xbfcbfb4c) destructing 2, at 0xbfcbfb4c Variable 'a' is now: 2 destructing 2, at 0xbfcbfb48 

Usually the programmer takes care of what is on the left, which is what the C ++ program offers. Note that MyClass stores the value, not the pointer. Your example is encoded just fine, and it has no errors if your class data is simple values. If your class contains pointers, then indeed, the default constructor and assignment operator (or custom ones that work as defaults, such as those shown above) are no longer sufficient because they provide small copies of pointed to data. Your class should either include some form of smart pointer, or manually handle copying of the allocated resource, possibly using reference counting to increase efficiency. Some form of smart pointer is probably a safer bet.

+3
source

The compiler optimizes the first call:

 MyClass a = MyClass(1); 

Only one constructor call instead of a construct, and then a copy constructor call. However, on the second line:

 a = MyClass(2); 

First, a temporary object is created, and then it is defined by a. Then what happens is that the temporary object is destroyed (thus, the first destructing 2 ), and then a destroyed (thus, the second destructing 2 ).

The reason destructing 2 is printed upon destruction is because a default assignment operator is created for your class, since you did not define it, and this assignment operator will copy the value myVar .

+2
source
 MyClass a = MyClass(1); 

This creates an object with a value of 1, so you see

 constructing 1 

then

 a = MyClass(2); 

creates a temporary object with a value of 2, so you see

 constructing 2 

a temporary object is assigned a , giving a the same value, 2, then the temporary one goes out of scope and is destroyed, so you see

 destructing 2 

Then at the end of main variable a destroyed, and since it was reassigned to the new value that you see

 destructing 2 

This is C ++, not Java or C #, so a is an object that is not a reference. String a = MyClass(2); does not make a reference to another object; it changes the object a as a copy of another object.

+2
source

When your program reaches the end of main, it destroys a , the value of the myVar variable at this point is 2. If you write instead:

  MyClass a = MyClass(1); MyClass b = MyClass(2); 

You will see the expected result.

+1
source

When you say a = MyClass (2); you apply the default assignment operator to object a. In this case, the value of a.mvar will change to 2.

Try instead:

 int main(int argc, const char * argv[]) { MyClass a( 1 ); MyClass b( 2 ); return 0; } 
+1
source

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


All Articles