I have a class that contains a dynamically allocated array, say
class A { int* myArray; A() { myArray = 0; } A(int size) { myArray = new int[size]; } ~A() { // Note that as per MikeB's helpful style critique, no need to check against 0. delete [] myArray; } }
But now I want to create a dynamically allocated array of these classes. Here’s my current code:
A* arrayOfAs = new A[5]; for (int i = 0; i < 5; ++i) { arrayOfAs[i] = A(3); }
But this blows up terribly. Because the new A object created (with the A(3) call) gets destructed when the for loop iteration finishes, and this means that the internal myArray of that A instance gets delete []-ed.
So I think my syntax must be terribly wrong? I guess there are a few fixes that seem like overkill, which I’m hoping to avoid:
- Creating a copy constructor for
A. - Using
vector<int>andvector<A>so I don’t have to worry about all this. - Instead of having
arrayOfAsbe an array ofAobjects, have it be an array ofA*pointers.
I would think this is just some beginners thing where there’s a syntax that actually works when attempting to dynamically allocate an array of things that have internal dynamic allocation.
(Also, style critiques appreciated, since it’s been a while since I did C++.)
Update for future viewers: All of the answers below are really helpful. Martin’s is accepted because of the example code and the useful ‘rule of 4,’ but I really suggest reading them all. Some are good, succinct statements of what’s wrong, and some point out correctly how and why vectors are a good way to go.
For building containers you obviously want to use one of the standard containers (such as a std::vector). But this is a perfect example of the things you need to consider when your object contains RAW pointers.
If your object has a RAW pointer then you need to remember the rule of 3 (now the rule of 5 in C++11).
This is because if not defined the compiler will generate its own version of these methods (see below). The compiler generated versions are not always useful when dealing with RAW pointers.
The copy constructor is the hard one to get correct (it’s non trivial if you want to provide the strong exception guarantee). The Assignment operator can be defined in terms of the Copy Constructor as you can use the copy and swap idiom internally.
See below for full details on the absolute minimum for a class containing a pointer to an array of integers.
Knowing that it is non trivial to get it correct you should consider using std::vector rather than a pointer to an array of integers. The vector is easy to use (and expand) and covers all the problems associated with exceptions. Compare the following class with the definition of A below.
Looking at your problem:
The compiler generated assignment operator is fine for nearly all situations, but when RAW pointers are in play you need to pay attention. In your case it is causing a problem because of the shallow copy problem. You have ended up with two objects that contain pointers to the same piece of memory. When the A(3) goes out of scope at the end of the loop it calls delete [] on its pointer. Thus the other object (in the array) now contains a pointer to memory that has been returned to the system.
The compiler generated copy constructor; copies each member variable by using that members copy constructor. For pointers this just means the pointer value is copied from the source object to the destination object (hence shallow copy).
The compiler generated assignment operator; copies each member variable by using that members assignment operator. For pointers this just means the pointer value is copied from the source object to the destination object (hence shallow copy).
So the minimum for a class that contains a pointer: