c++ - Destructor called on assignment - c++

I'm still learning the basics of c++ so I may not have had the correct vocabulary to find the answer to my question but I couldn't find this mentioned anywhere.
If I have a class with a constructor and destructor why does the destructor get called on the new data when I am assigning to the class?
For example:
#include <iostream>
class TestClass {
public:
int* some_data;
TestClass() {
std::cout << "Creating" << std::endl;
some_data = (int*)malloc(10*sizeof(int));
}
~TestClass() {
std::cout << "Deconstructing" << std::endl;
free(some_data);
}
TestClass(const TestClass& t) : some_data{t.some_data} {
std::cout << "Copy" << std::endl;
}
};
int main() {
TestClass foo;
std::cout << "Created once" << std::endl;
foo = TestClass();
std::cout << "Created twice" << std::endl;
}
which prints:
Creating
Created once
Creating
Deconstructing
Created twice
Deconstructing
free(): double free detected in tcache 2
Aborted (core dumped)
So after following this in the debugger it appears the deconstructor is called on the newly created data which is confusing to me. Shouldn't the original data be freed once and then at the end of execution the new data should be freed? It seems like the original data is never freed like this.

Your object owns a raw pointer to allocated memory, but does not implement a proper copy constructor that makes an allocation and copies the data behind the pointer. As written, when you copy an object, the pointer is copied, such that now two objects point to the same address (and the old one that the just-assigned-to object is leaked.)
When the temporary goes out of scope, it deletes its pointer but the copy (foo) still points to it. When foo goes out of scope, it deletes the same pointer again, causing this double free error you're seeing.
If you need to write a destructor to clean up, you almost always need to also provide copy and assignment operations, or disable them.
SUGGESTIONS:
hold the pointer in a std::unique_ptr which will fail to compile if you try to copy it. This forces you to deal with the issue. Also, malloc and free are mainly for C or low-level C++ memory management. Consider using new and delete for allocations instead. (unique_ptr uses delete by default, not free, and you must not mix them.)
alternately, delete the copy constructor and assignment operator
also, consider when you want to move from an xvalue (temporary or moved lvalue), you can pilfer the allocation from the right-hand-side. So this class is a good candidate for move constructor and move assignment.

Most of the comments and a few details more in code:
#include <iostream>
#include <array>
#include <memory>
class TestClass
{
// members of a class should not be public
private:
// TestClass owns the data, this is best modeled
// with a unique_ptr. std::array is a nicer way of
// working with arrays as objects (with size!)
std::unique_ptr<std::array<int, 10>> some_data;
public:
TestClass() :
some_data{ std::make_unique<std::array<int,10>>() }
{
std::cout << "Creating" << std::endl;
// set everything in the array to 0
std::fill(some_data->begin(), some_data->end(), 0);
}
~TestClass()
{
std::cout << "Destructing" << std::endl;
// no need to manually delete a std::unique_ptr
// its destructor will free the memory
// and that will be called as part of this destructor
}
TestClass(const TestClass& t) :
// when you copy a class the copy should have its
// own copy of the data (to avoid deleting some data twice)
// or you must chose shared ownership (lookup std::shared_ptr)
some_data{ std::make_unique<std::array<int,10>>() }
{
std::cout << "Copy" << std::endl;
// copy data from t to this instances array
// (note this would not have been necessary
// it your array was just a non-pointer member,
// try that for yourself too.)
std::copy(some_data->begin(), some_data->end(), t.some_data->begin());
}
TestClass(TestClass&& rhs) :
some_data{ std::move(rhs.some_data) } // transfer ownership of the unique_pointer to this copy
{
std::cout << "Move" << std::endl;
}
// Important this assignement operator is used in your original code too
// but you couldn't see it!
TestClass& operator=(const TestClass& t)
{
some_data = std::make_unique<std::array<int, 10>>();
std::copy(some_data->begin(), some_data->end(), t.some_data->begin());
std::cout << "Assignment" << std::endl;
return *this;
}
};
int main()
{
TestClass foo;
std::cout << "Created once" << std::endl;
foo = TestClass();
std::cout << "Created twice" << std::endl;
TestClass bar{ std::move(foo) };
std::cout << "Moved" << std::endl;
}

Related

Why std::move doesn't avoid a second destruction?

I know ordinary std::vector::push_back() will copy the object. I hope this code would only destruct a only once, using std::move() and A(A&&) noexcept to avoid copying. But it doesn't seem to work.
Is there any way that I can construct an object before push_back() and move it into a vector perfectly?
#include <bits/stdc++.h>
using namespace std;
class A {
public:
A() { std::cout << "construct" << this << '\n'; }
A(A&&) noexcept { std::cout << "move" << this << "\n"; }
A(const A&) = delete;
~A() { std::cout << "destruct" << this << '\n'; }
};
std::vector<A> as;
void add(A&& a) {
std::cout << "add 1\n";
as.push_back(std::move(a));
std::cout << "add 2\n";
}
int main() {
add(A());
std::cout << "main2\n";
return 0;
}
Output:
construct0x16d20b1fb
add 1
move0x151e068b0
add 2
destruct0x16d20b1fb
main2
destruct0x151e068b0
I hope this code would only destruct a only once, using std::move() and A(A&&) noexcept to avoid copying.
Using Move constructor perverts copying but doesn't prevent creating new objects, you are creating an inline object with default constructor "cout : construct0x16d20b1fb" and then from that object's data your going to create a 'NEW' object and calling move constructor will
transform the ownership of the object's data/resources to the new object that is being 'Constructed' so that explain the "cout : move0x151e068b0" then the line is finished so your inline object is destroyed "cout " destruct0x16d20b1fb" then your program finishes "cout : destruct0x151e068b0" your object that made via move constructor is destroyed.
you are expecting the behavior of pointers from your vector of "Objects" which is supposed to hold actual objects not pointer to other objects so it needs to containt objects and each of objects that are in that vector has a different address, unless you create your object and use a vector pointers to objects.
By the way std::move is but a cast, it casts to rvalue references so when you are capturing with rvalue reference there's no need to cast it again, you should be using it like this:
A a_object1;
vector.push_back(std::move(a_object1));
and it will use the move constructor. although in this case it will cast it implicitly because you are capturing only by rvalue reference in add function unless you add an overload to this function that also takes reference there's no need.

how to disable move construct base class from derived class?

In the following code, I want to disable the move construction of base class Vector from derived class VectorMap, and call the copy constructor.
#include <iostream>
#include<algorithm>
struct Vector{
int* _ptr=nullptr;
int _size=0;
Vector(int n){
_ptr = new int[n];
_size = n;
std::cout<< " Construct "<<this<<std::endl;
}
Vector(void) { std::cout <<" Construct " << this << std::endl; }
virtual ~Vector(void) {
if (_ptr != nullptr) {
std::cout << "Deconstruct " << this << " -> delete " << _ptr << std::endl;
delete _ptr;
return;
}
std::cout << "Deconstruct " << this << std::endl;
}
Vector(Vector&& v2) noexcept {
int* p2=v2._ptr; int s2=v2._size;
v2._ptr=_ptr;
v2._size=_size;
_ptr=p2; _size=s2;
std::cout << "Move construct " << this << std::endl;
}
Vector(const Vector& v3){
_ptr=new int[v3._size];
_size=v3._size;
memcpy(_ptr,v3._ptr,sizeof(int) * _size);
}
};
struct VectorMap
: public Vector {
VectorMap(int* p,int size){
_ptr=p;
_size=size;
}
~VectorMap(void) override {
_ptr=nullptr; _size=0;
}
};
int main(void) {
Vector v1(10);
Vector v2=VectorMap(v1._ptr,5); // v1._ptr will be deleted twice
return sizeof(v2);
}
As you can see, if move constructor is called in the line Vector v2=VectorMap(v1._ptr,5);, the data pointer in v1 will be deleted twice, one is by v2, and another is by v1. Since they share the same pointer. Is there any way to modify VectorMap to call copy constructor rather than move constructor in such case?
The primary problem is muddy ownership semantics.
Vector creates a resource (dynamic array), and is apparently intended to have unique ownership over it.
VectorMap on the other hand takes a pointer in its constructor, and gives it to its base Vector that takes ownership. But right before destruction, VectorMap rescinds the ownership by setting the base Vector pointer to null. So, VectorMap sort of pretends to own the resource until it's time for the responsibility of the ownership at which point it backs down.
This inheritance also causes other problem situations. Consider for example making a copy of VectorMap. Look at what the copy constructor of the base does. It allocates memory for the copy. But the desturctor of the VectorMap copy sets the pointer to null, so in this case the pointer is deleted zero times. It leaks memory.
As an analogy, disabling slicing would be a bandage for the wound, but what you really should do is to not stick your hand inside a running blender. If VectorMap is supposed to not have ownership, then it shouldn't inherit a base that takes ownership. It's unclear to me what the point of VectorMap class even is.
Furthermore, a class that has unique ownership of a resource such as Vector really should encapsulate that bare pointer with private access specifier. Sure, such classes sometimes still provide a way to copy or throw away that value (like std::vector::data and std::unique_ptr::release), but it's important that only happens through a specific function to reduce chance of accidental violation of ownership semantics.
Another serious bug:
_ptr=new int[v3._size];
// ...
delete _ptr;
You must not delete a pointer to a dynamic array. You must use delete[]. Using delete results in undefined behaviour.
Another bug: You forgot to include the header that declares memcpy. Also, I would recommend using std::copy instead.

Why is there no memory leak here

I was trying to answer this question, so I decided to create the following simple test case so that the OP could see by himself the memory leak.
#include<iostream>
class MyObject
{
public:
MyObject(){std::cout << "creation of my object" << std::endl;}
virtual ~MyObject(){std::cout << "destruction of my object" << std::endl;}
};
void processMyObject(MyObject foo)
{
}
int main()
{
processMyObject(*new MyObject());
return 0;
}
I compiled it :
g++ test.cpp -o test
And then, I saw an unexpected output :
creation of my object
destruction of my object
I have absolutly no idea of what is happening here. Could anyone explain to me ?
PS: I used g++ 4.6.3
Since you pass an object by value to the function, you incurr a copy or move-copy construction. But you are not keeping track of that with your primitive memory leak checker. You could provide your own copy constructor, and then you will see two objects are being created, and only one is being destroyed:
#include<iostream>
class MyObject
{
public:
MyObject() {std::cout << "creation of my object" << std::endl;}
MyObject(const MyObject&) {std::cout << "copy creation of my object" << std::endl;}
~MyObject() {std::cout << "destruction of my object" << std::endl;}
};
void processMyObject(MyObject foo) {}
int main()
{
processMyObject(*new MyObject());
}
Output:
creation of my object
copy creation of my object
destruction of my object
Because you're taking the MyObject by value.
Thus there is a destruction. But it is the destruction of the foo argument at the end of processMyObject.
The *new does actually still leak in this case.
EDIT: As pointed out by juanchopanza, you need to also print a statement in the copy constructor and in the move constructor as well.
What happens in your code
You construct an object and get information about it (creation of my object)
You pass it to function - copy constructor fires, but does not report anything
The copy is destroyed - you get information about it (destruction of my object)
The original instance leaks despite fact, that you don't have any information about it.
How to see it?
Simply report pointers to this during construction and destruction (quick'n'dirty, please don't complain):
class MyObject
{
public:
MyObject(){std::cout << "creation of my object (" << (int)this << ")" << std::endl;}
virtual ~MyObject(){std::cout << "destruction of my object (" << (int)this << ")" << std::endl;}
};
Result:
creation of my object (165437448)
destruction of my object (-1076708692)
As you see, destroyed object is different than created one.
How to "fix" it to show the leak?
The simplest way to "fix" your code is to pass object by pointer:
#include<iostream>
class MyObject
{
public:
MyObject(){std::cout << "creation of my object" << std::endl;}
virtual ~MyObject(){std::cout << "destruction of my object" << std::endl;}
};
void processMyObject(MyObject * foo)
{
}
int main()
{
processMyObject(new MyObject());
return 0;
}
Another option is to report copy ctors and move ctors as well:
class MyObject
{
public:
MyObject(){std::cout << "creation of my object" << std::endl;}
MyObject(const MyObject & obj) { std::cout << "copy-ctor" << std::endl; }
MyObject(MyObject && obj) { std::cout << "move-ctor" << std::endl; }
virtual ~MyObject(){std::cout << "destruction of my object" << std::endl;}
};
There actually is a memory leak. Just because your object was destroyed, doesn't mean that you deleted the resources you acquired with new. You have to explicitly use delete.
EDIT
So here's what's happening:
You are calling the default constructor on the fly, and passing it to the function call. This prints the first message.
As part of the function call, the object is passing to processMyObject(), creating a new object in that scope using the copy constructor, which has been implicitly defined by the compiler.
When that object (in processMyObject()) goes out of scope, its destructor is called, printing the second message.
So, the instance printing the first message is different from that printing the second one.
Hope that clears things up.

c++ destroy an object on stack frame

I try to understand what happen when an object destroy on stack.
here is my sample code:
#include <stdio.h>
struct B {
~B() {puts("BBBB");}
};
int main()
{
B b;
b.~B();
}
output is
BBBB
BBBB
based on the output, I can tell the object is destroy twice.one is ~B(), another one is after "}". how and why can a object get destroy twice?
update:
after i review replies, I think the destructor doesnt destroy this object. it there a way to destroy an object before it reach out of scope "}".
Thanks
You are not supposed to invoke the destructor by hand. What's happening is you are invoking the destructor and then when the object gets popped off the stack the destructor is called again automatically by the compiler.
~B() is called before the destruction
Destructors are usually used to deallocate memory and do other cleanup
for a class object and its class members when the object is destroyed.
A destructor is called for a class object when that object passes out
of scope or is explicitly deleted.
Source
So you are just calling a function twice.
There's minimal-to-no garbage collection in C++, objects are simply destroyed when they go out of scope. So you could replace your test to this:
#include <iostream>
struct B {
B() { std::cout << "B()" << std::endl; }
~B() { std::cout << "~B()" << std::endl; }
};
int main()
{
std::cout << "start main" << std::endl;
{ // scope
std::cout << "start scope" << std::endl;
B b;
std::cout << "end scope" << std::endl;
} // <-- b gets destroyed here.
std::cout << "end main" << std::endl;
}
If you want an object on the stack over which you have control over the lifetime of, you can do something like this:
#include <iostream>
#include <memory.h>
struct B {
B() { std::cout << "B()" << std::endl; }
~B() { std::cout << "~B()" << std::endl; }
};
int main()
{
std::cout << "start main" << std::endl;
{ // scope
std::cout << "start scope" << std::endl;
void* stackStorage = alloca(sizeof(B));
std::cout << "alloca'd" << std::endl;
// use "in-place new" to construct an instance of B
// at the address pointed to by stackStorage.
B* b = new (stackStorage) B();
std::cout << "ctord" << std::endl;
b->~B(); // <-- we're responsible for dtoring this object.
std::cout << "end scope" << std::endl;
} // <-- b gets destroyed here, but it's just a pointer.
std::cout << "end main" << std::endl;
}
Live demo: http://ideone.com/ziNjkd
Remember, though, it's the stack. When it goes out of scope, it goes away - if you don't destroy it, it just dissapears.
{
void* stackStorage = alloca(sizeof(B));
B* b = new (stackStorage) B(); // "in-place new"
} // (*b)s memory is released but (*b) was not dtord.
The only time you would manually call a destructor is when you've got grounds to use placement new
Keep in mind that the destructor is like any other function. The only difference with other function is that it is automatically called when the object is deallocated. You cann see this like an event. You get a chance to clean everything before the object get annihilated. Calling the destructor by hand does not deallocate the object.
Object construction/destruction in C++ follows this simple rule:
Anything automatically allocated (and constructed) is automatically destructed.
Anything explicitely allocated with new is explicitely destructed via delete.
Anything explicitely constructed with new() must be explicitely destructed by calling the destructor.
The destructor has to be called in all three cases, the difference is in how the memory for the object is allocated:
The object is on the stack, its allocation is managed by the compiler.
The object is on the heap, its allocation is managed by the programmer.
The object is anywhere, construction and destruction is independent of allocation.
In the first two cases, we have a combination of allocation and construction, and consequently a combination of destruction and deallocation. The third case is entirely different, but fully supported by the language, and the reason that you are allowed to explicitely call a destructor; because in this case an object is constructed without allocating memory for it. Consequently, it must be destructible without deallocating memory as well. This case can be used like this:
void* buffer = (void*)new char[sizeof(Foo)]; //allocation
Foo* myFoo = new(buffer) Foo(); //construction
myFoo->~Foo(); //destruction
Foo* anotherFoo = new(buffer) Foo(); //reuse the buffer to construct another object in it
anotherFoo->~Foo(); //destruction of the second object
delete buffer; //deallocation
Note, that this actually constructs two objects, one after the other in the same place, destructing them explicitely before the memory is reused. The buffer could also be a large slap of memory to store many objects, std::vector<> works like this.
So, yes, destruction does destroy your object, you must not use it after destruction. But since you used an automatically allocated and constructed object in your question, the compiler also took care of destructing it, leading to the double destruction. This is always a bug, objects must never be destructed twice, but the language allows you to do it anyway.
What you are doing is a pretty good example of a variable running out of scope.
int main()
{
//Main is started
B b;
/* What happens is that `b` is constructed as a local
variable and put on the runtime stack.*/
b.~B();
/*You then invoke the destructor manually which is bad practice.
But it is possible and it will execute the code.
However, the state of the resulting instance is undefined.*/
}
/*Main ends and local variables defined in the
current scope get destroyed. Which also means that B's destructor
is called the second time */
FYI - the only time you are supposed to do manual destruction of an object is when it is put on the heap like this:
// create an instance of B on the heap
B* b = new B();
// remove instance and implicitly call b's destructor.
delete b;
i would like to answer my own question.
after a lot of reading, Here is my summary.
1. destructor doesnt destroy it's object. the object stays at stack
until out of scope.
2. nothing can destroy a stack object.
3. the destructor did destroy RESOURCE inside the object.
sample code:
struct B {
B() {happy="today is a good day"; hour = 7;}
~B() {puts("BBBB");}
std::string happy;
int hour;
};
int main()
{
B b;
std::cout << b.happy << b.hour <<std::endl;
b.~B();
std::cout << b.happy << b.hour <<std::endl;
}
output:
today is a good day7
BBBB
7
BBBB
we can see a resource b.happy is gone after b.~B() is called. this is a proof of my point 3.
you see the b.hour(int type is not a resource) is still here. this is a proof of my point 1.

STL vector allocations

I was wondering why the vector templates perform two allocations, when only one seems to be
necessary.
For example this:
#include <vector>
#include <iostream>
class A {
public:
A(const A &a) {
std::cout << "Calling copy constructor " << this << " " << &a << "\n";
}
A() {
std::cout << "Calling default constructor " << this << "\n";
}
~A() {
std::cout << "Calling destructor " << this << "\n";
}
};
int main(int argc, char **argv)
{
std::vector <A> Avec;
std::cout << "resize start\n";
Avec.resize(1);
std::cout << "resize end\n";
return 0;
}
Outputs:
resize start
Calling default constructor 0x7fff9a34191f
Calling copy constructor 0x1569010 0x7fff9a34191f
Calling destructor 0x7fff9a34191f
resize end
It isn't performing two allocations, it is creating an object by the default constructor to pass into resize, then copying that object into the new position, then destructing the argument.
If you look at the arguments to resize:
void resize(n, t = T())
It has as a defaulted argument a default constructed object of type T (this is the default constructor being called in your output). Then, within the function, it copies this into the correct position (this is the copy constructor). After the resize function ends, destroys the argument (the destructor call in the output).
Here is a guess:
The compiler re-ordered the initial allocation of Avec until after the "resize start"
The vector is initially allocated with 0 elements
The resize gets the new element filled with a default A (which was achieved by createing a default A, copying it into the vector, and deleting the temporary.
If you initialize objects that way the vector template creates objects by making a copy.
If you don't want to call copy constructor you should make :
vector<A*> Avec;
avec.push_back(new A());
http://www.cplusplus.com/reference/stl/vector/vector/