Dov
Dov

Reputation: 8542

how to call destructor on some of the objects in a Dynamic Array

I finally got around to trying placement new to create an efficient dynamic array. the purpose is to understand how it works, not to replace class vector. The constructor works. A block is allocated but uninitialized. As each element is added, it is initialized. But I don't see how to use placement delete to call the destructor on only those elements that exist. Can anyone explain that one? This code works for allocating the elements one by one as the array grows, but the delete is not right.

template<typename T>
class DynArray {
private:
  uint32_t capacity;
  uint32_t size;
  T* data;
  void* operator new(size_t sz, T* place) {
    return place;
  }
  void operator delete(void* p, DynArray* place) {
  }

public:
  DynArray(uint32_t capacity) :
     capacity(capacity), size(0), data((T*)new char[capacity*sizeof(T)]) {}
  void add(const T& v) {
        new(data+size++) T(v);
  }
  ~DynArray() {
     for (int i = 0; i < size; i++)
       delete (this) &data[i];
     delete [] (char*)data;
  }
};

Upvotes: 3

Views: 2046

Answers (2)

jfMR
jfMR

Reputation: 24738

A placement delete doesn't make much sense since the destructor already does what a placement delete is supposed to do.

An ordinary delete calls the destructor and then releases the memory that was allocated for the object with new. However, unlike an ordinary new, a placement new does not allocate memory, it only initialises it. Therefore, a placement delete would only have to call the destructor of the object to be "deleted".

All you need is to call the destructor of each object of the array directly:

~DynArray() {
   for (int i = 0; i < size; i++)
      data[i].~T();
}

Since C++17 you can also use the function template std::destroy instead of directly calling the destructor:

~DynArray() {
   auto first = std::addressof(data[0]);
   auto last  = std::next(first, size);
   std::destroy(first, last);
}

Upvotes: 3

Stephen Newell
Stephen Newell

Reputation: 7838

You actually found the only case (at least that I'm aware of) where you want to invoke the destructor manually:

  ~DynArray() {
     for (int i = 0; i < size; i++)
       data[i].~T();
     delete [] (char*)data;
  }

Combined with a trivial class and main, you should get the expected results:

struct S {
    ~S() { std::cout << __PRETTY_FUNCTION__ << '\n'; }
};

int main() {
    DynArray<S> da{10};
    da.add(S{});
    return 0;
}

Note that you see the destructor called twice since DynArray takes objects by const reference, thus it has a temporary.

$./a.out 
S::~S()
S::~S()

Upvotes: 1

Related Questions