1

I'm working with dynamic arrays and there is the problem I've got. Compiler calls destructor for the first array (with length 1) twice. So the program crashes. I'd be glad for any help. Thank you.

class MyClass{
public:
    int *a;
    MyClass(int i){
        a = new int[i];
    }
    ~MyClass(){
        if (a) delete[]a;
    }
};


int main(){ 
    MyClass c(1);
    c = MyClass(2);
}
qIUoF7
  • 49
  • 4

3 Answers3

3

The program crashes because copying MyClass only copies the pointer member and does not create a new array which then can be deleted. So you delete the same array twice which is Undefined Behavior.

The best fix for that is using std::vector<int> instead of raw arrays if every instance should have its own array.

If you (for some artificial restriction, there is no actual usecase for that) need to use raw arrays, define a suitable copy constructor and assignment operator. See "The rule of Three".

Community
  • 1
  • 1
Baum mit Augen
  • 49,044
  • 25
  • 144
  • 182
  • 1
    The best fix is either vector or smart pointer depending on the needs (those are not clear). One cannot always use vectors blindly. – luk32 Mar 18 '15 at 10:39
  • @luk32 Until he clarifies, I'll stick with the vector bc value semantics. – Baum mit Augen Mar 18 '15 at 10:46
  • @luk32 `std::vector` should be the default choice. It's the idiomatic way of spelling "dynamic array" in C++. Only if you know and verify that it's not appropriate for you should you turn to considering something else. – Angew is no longer proud of SO Mar 18 '15 at 10:47
1

Why you copy a MyClass instance, the default-generator copy constructor is called. This just copies the internal a pointer, and does not create a new int array.

So when the copy is destroyed, it deletes the array, and then when the original is destroyed, it also deletes the array.

There are 2 ways around this:

  1. Preferable: use something that wraps up all the allocation and copying for you, such as std::vector (rule of 0, we'll come to that in a minute), or if you are trying to create such a class, say for educational interest,
  2. Follow the "rule of 5" (used to be 3, before C++11 introduced move semantics). Whenever you have a data member that needs some special creation/destruction, make sure you deal with it appropriately in all constructors and destructors: copy/move construction/assignment, and destruction.

Note that at the moment, it is impossible to properly write a copy constructor/assignment operator, since you don't store the size of the array, so you don't know what size the copy should be.

For instance, if you store the array size in an int member named i, your copy constructor might look like:

MyClass(const MyClass& rhs) : a(0), i(rhs.i) {
    a = new int[i];
    std::copy(rhs.a, rhs.a+rhs.i, a);
}

Writing a copy assignment operator is a bit trickier, since you need to deal with deleteing the existing array, and even with restoring the original state if allocating the new array throws an exception:

Edit: sorry, my previous operator= example was total crap, this version is better:

MyClass& operator=(const MyClass& rhs) {
    int *newA = new int[rhs.i];
    std::copy(rhs.a, rhs.a+rhs.i, newA);
    i = rhs.i;

    delete[] a;
    a = newA;

    return *this;
}

You only replace the existing member after building the new one. This way, if allocating the new one throws an exception, the object is still in its old state.

BoBTFish
  • 19,167
  • 3
  • 49
  • 76
  • I added copy constructor, but It still doesn't work. – qIUoF7 Mar 18 '15 at 10:52
  • Follow the rule of 5. Maybe stick to 3 for now: copy construction, copy assignment, destruction. Worry about move semantics later. But the problem now is that you don't have a copy **assignment** operator. Basically the same problem, but a different function. – BoBTFish Mar 18 '15 at 10:58
  • @qIUoF7 Have added a sample for writing a copy assignment operator. – BoBTFish Mar 18 '15 at 11:09
  • @qIUoF7 Improved `operator=` example. – BoBTFish Mar 18 '15 at 11:32
  • No need to check if `a` is a nullptr. It's okay to call delete on 0, right? – JorenHeit Mar 18 '15 at 11:50
  • @JorenHeit Yes, of course. I just worked from what he already had and didn't think about that. Will correct it now. – BoBTFish Mar 18 '15 at 12:35
0

This occurs because the same "a" was deleted twice. What happened:

  1. MyClass c(1) // allocated a at address 1
  2. MyClass(2) // allocated a at address 2
  3. c = ... // now c.a points to address 2, because c just copies every members of the object on the right side
  4. object created at 2. was destroyed, thus address 2 is freed
  5. c was destroyed, thus address 2 is freed for the second time, causing crash

The solution is not to copy the address of "a", but create a copy constructor and copy the value of "a" instead.

James Bear
  • 434
  • 2
  • 4