Reputation: 317
I am a bit new to Objects in C++, and I have the following simplified problem:
I want to create an array of objects that are already initialized by the constructer of the class.
Thus:
int main() {
Test A(1);
Test B(2);
Test C(3);
Test TestArray[3]={A,B,C};
/*
Code that both uses A,B,C directly and TestArray
*/
return 0;
}
Importantly, the class Test
dynamically allocates its value. And so the destructor should delete this allocated memory.
Thus:
class Test {
int *PointerToDynMem;
public:
Test(int);
~Test();
};
Test::Test(int a){
PointerToDynMem=new int(a);
}
Test::~Test(){
delete PointerToDynMem;
}
I think what happens is when the program ends A
,B
and C
go out of scope and call the destructor. But it seems that also when TestArray
goes out of scope, it also calls the destructor, but A
,B
,C
were already deallocated soo. ERROR.
I always coded like this with normal type objects like an integer, and here it never gave me any problem. It seems that I need to change something, but don't know exactly how, since I want to both us the objects separately and have an array of them.
The thing I am confused about is why the Array should call that destructor, since it is basically a pointer to the first element and so not really an object going out of scope.
Upvotes: 1
Views: 1383
Reputation: 180500
The thing I am confused about is why the Array should call that destructor, since it is basically a pointer to the first element and so not really an object going out of scope.
An array is not a pointer. If it was, we would just have pointers. An arrays name can and does decay to a pointer to the first element in the array (which can be really annoying) but it isn't a pointer. It is an object that stores N
objects in a contiguous piece of memory and that size information is part of it's type. That means a int[5]
is not the same type as a int[6]
.
What happens here is
Test TestArray[3]={A,B,C};
creates an array of three Test
and then copy initializes each Test
object in the array from the initializers {A,B,C}
. So when main
ends, TestArray
is destroyed call the destructor for each element. And then C
, B
and A
as destroyed in that order.
This is a problem for you though since you class just uses the default copy constructor. That means each object in the array has a copy of the point of the object it was initialized by and it points to the same memory. So when the array gets destroyed, delete
is called on all of the member pointers and then C
, B
and A
try to delete that same memory again, which is undefined behavior.
What you need to do is follow the rule rule of three and create the special member functions so your class is copied correctly, or you can use RAII in the form of smart pointers/std::vector
and let them handle all of that for you and you can use the rule of zero
Upvotes: 2
Reputation: 32586
For that code you need to add a copy constructor (and also operator= etc for a 'real' code)
Test::Test(const Test & t) {
PointerToDynMem=new int(*t.PointerToDynMem);
}
else your int*
is shared (TestArray[0].PointerToDynMem
is A.PointerToDynMem
and TestArray[1].PointerToDynMem
is B.PointerToDynMem
and TestArray[2].PointerToDynMem
is C.PointerToDynMem
) and deleted 2 times
Upvotes: 1
Reputation: 117298
TestArray[3]={A,B,C};
will copy the int*
held by A, B and C using the default copy constructor. To remedy this, create all constructors and operators that would otherwise be silently created for you. Read the rule of three/five/zero.
class Test {
int *PointerToDynMem;
public:
Test(int);
Test(const Test&); // implement this
Test(Test&&); // implement this
Test& operator=(const Test&); // implement this
Test& operator=(Test&&); // implement this
~Test();
};
Upvotes: 4