Using placement new in a container

旧街凉风 提交于 2021-02-11 05:03:38

问题


I just came across some container implementation in C++. That class uses an internal buffer to manage its objects. This is a simplified version without safety checks:

template <typename E> class Container
{
public:
   Container() : buffer(new E[100]), size(0) {}
   ~Container() { delete [] buffer; }

   void Add() { buffer[size] = E(); size++; }
   void Remove() { size--; buffer[size].~E(); }

private:
   E* buffer;
   int size;
};

AFAIK this will construct/destruct E objects redundantly in Container() and ~Container() if new/delete are not customized. This seems dangerous.

Is using placement new in Add() the best way to prevent dangerous redundant constructor / destructor calls (apart from binding the class to a fully featured pool)?

When using placement new, would new char[sizeof(E)*100] be the correct way for allocating the buffer?


回答1:


AFAIK this will construct/destruct E objects redundantly

It would appear so. The newed array already applies the default constructor and the delete[] would call destructor as well for all the elements. In effect, the Add() and Remove() methods add little other than maintain the size counter.

When using placement new, would new char[sizeof(E)*100] be the correct way for allocating the buffer?

The best would be to opt for the std::allocator that handles all of the memory issues for you already.

Using a placement new and managing the memory yourself requires you to be aware of a number of issues (including);

  • Alignment
  • Allocated and used size
  • Destruction
  • Construction issues such as emplacement
  • Possible aliasing

None of these are impossible to surmount, it has just already been done in the standard library. If you are interested in pursuing a custom allocator, the global allocation functions (void* operator new (std::size_t count);) would be the appropriate starting point for the memory allocations.


Without further explanation on the original purpose of the code - a std::vector or a std::array would be far better options for managing the elements in the container.




回答2:


There's a number of issues with the code. If you call Remove() before calling Add() you will perform assignment to a destructed object.

Otherwise the delete[] buffer will call the destructor of 100 objects in the array. Which may have been called before.

Here's a valid program:

#include <iostream>

int counter=0;

class Example {

    public:
        Example():ID(++counter){
           std::cout<<"constructing "<<ID<<std::endl;
        }
        ~Example(){
            std::cout<<"destructing "<<ID<<std::endl;
            ID=-1;
        }
    private:


      int ID;
};

template <typename E> class Container
{
public:
   Container() : buffer(new char [100*sizeof(E)]), size(0) {}
   ~Container() {
        for(size_t i=0;i<size;++i){
            reinterpret_cast<E*>(buffer)[i].~E();
        }
        delete []  buffer; 
    }

   void Add() { new (buffer+sizeof(E)*size) E(); size++; }
   void Remove() { reinterpret_cast<E*>(buffer)[--size].~E(); }

private:
   void* buffer;
   size_t size;
};


int main() {
    Container<Example> empty;

    Container<Example> single;
    Container<Example> more;

    single.Add();

    more.Add();

    more.Remove();

    more.Add();
    more.Add();
    more.Remove();

    return 0;
}


来源:https://stackoverflow.com/questions/35129485/using-placement-new-in-a-container

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!