Eagle06
Eagle06

Reputation: 71

Destructor call in a class constructor with a vector

I have the following class which simply wraps an array and adds with the constructor some elements to it:

class myArray {
public:
    myArray();
    myArray(int a, int b);
    myArray(int a, int b, int c);
    myArray(myArray&& emplace);

    ~myArray();

    int& operator [](int id);

private:
    int *data;
};

myArray::myArray() {
    data = new int[1];
    data[0] = 0;
}

myArray::myArray(int a, int b) {
    data = new int[3];
    data[0] = 0;
    data[1] = a;
    data[2] = b;
}

myArray::myArray(int a, int b, int c) {
    data = new int[4];
    data[0] = 0;
    data[1] = a;
    data[2] = b;
    data[3] = c;
}

myArray::~myArray() {
    std::cout << "Destructor"<<std::endl;
    delete[] data;
}

int& myArray::operator [](int id) {
    return data[id];
}

myArray::myArray(myArray&& emplace) : data(std::move(emplace.data)) {}

Furthermore I have a second class which contains a vector of elements of the first class (myArray).

class Queue {
public:
    Queue();

private:
    std::vector<myArray> _queue;
};

Queue::Queue {
    _queue.reserve(1000);
    for(int a = 0; a < 10; a++)
        for(int b = 0; b < 10; b++)
            for(int c = 0; c < 10; c++)
                        _queue.emplace_back(a,b,c);
}

My question here is: Why is the destructor called for the myArray elements at the end of the Queue constructor? The Queue object is still alive in my main program but the destructor of myArray frees the allocated memory and I consequently get a segmentation fault.

Is there a way to avoid the call of the destructor or rather call it not until at the end of the Queue objects lifetime?

Upvotes: 0

Views: 79

Answers (1)

Alan Birtles
Alan Birtles

Reputation: 36379

Your move constructor doesn't set data to null on the moved from object so when the moved from object is destructed it will try to free data.

If you have c++14 you can use std::exchange to implement this:

myArray::myArray(myArray&& emplace)
 : data{std::exchange(emplace.data, nullptr)})
{}

Otherwise you need to do:

myArray::myArray(myArray&& emplace)
 : data{emplace.data)
{
  emplace.data = nullptr;
}

The move constructor will be invoked by std::vector as it reallocates to increase its capacity when you call emplace_back. Something like the following steps are performed:

  1. Allocate new memory to hold the elements
  2. Move construct using placement new elements in the new memory from the elements in the previous memory
  3. Destruct the elements in the previous memory
  4. Deallocate the previous memory

Upvotes: 3

Related Questions