Home > front end >  Creating a dynamic array to hold a custom struct without accidentally deconstructing the items
Creating a dynamic array to hold a custom struct without accidentally deconstructing the items

Time:10-28

My question is summed up pretty well with this code snippet:

struct T {
    int* heapValue;
    T(){
        this->heapValue = new int[3]; // any dynamic size
    }
    ~T(){
        delete[] this->heapValue;
    }
}

int main() {
    int N = 5; // some value determined at runtime
    T* array = new T[N];

    for (int i = 0; i < N; i  )
    {
      T t = T(123, 456, args);
      t.do_something();

      array[i] = t;
    } // uh oh, t is deconstructed! and array[i].heapValue is invalidated

    int x = array[0].heapValue[0]; // segfault
}

I was able to solve this by creating T** array instead and making each t with new T(args), but then when it comes time to deallocate array I need to loop through each element and delete it first before deleting the array itself. This is slow and inconvenient. Also, T** looks like a 2-dimensional array which is potentially misleading.

The same issue occurs with std::vector<T> and can be solved with the equally slow and inconvenient std::vector<T*>.

Is there any way to create a dynamic array T* such that the elements are not deconstructed right after being assigned?

CodePudding user response:

The comments under your questions already summed up all the possibilities to handle this problem. Anyway, here is quick example how to use move semantics, with minimal changes to your code:

struct T {
    int* heapValue;
    T(){
        std::cout << "CTOR: " << this << std::endl;
        this->heapValue = new int[3]; // any dynamic size
    }
    ~T(){
        std::cout << "DTOR: " << this << std::endl;
        delete[] this->heapValue;
    }
    T(const T& other) = delete;
    T& operator=(const T& other) = delete;
    T& operator=(T&& other)
    {
        this->heapValue = other.heapValue;
        other.heapValue = nullptr;
        return *this;
    }
    T(T&& other)
    {
        *this = std::move(other);  // to avoid duplicate code
    }
    void do_something(){
        std::cout << "do_something: " << this << std::endl;
        heapValue[0]= 123;  // just for demonstration
        heapValue[1]= 456;
        heapValue[2]= 789;
    }
};

int main() {
    int N = 5; // some value determined at runtime
    T* array = new T[N];

    for (int i = 0; i < N; i  )
    {
      T t = T();
      t.do_something();
      array[i] = std::move(t);
    }
    std::cout << array[0].heapValue[0] << std::endl;
    std::cout << array[0].heapValue[1] << std::endl;
    std::cout << array[0].heapValue[2] << std::endl;
}

By deleting copy constructor and assignment we make sure there will be no invalid memory access. By providing move assignment we have a cheap way of storing your objects in the array.

CodePudding user response:

In this line

T t = T(123, 456, args);

you are creating a local variable, which quickly goes out of scope, so the pointer you are trying to use is invalidated, try allocating in loop with new

  •  Tags:  
  • c
  • Related