在容器中使用新放置位置

Using placement new in a container

本文关键字:新放 置位 位置      更新时间:2023-10-16

我刚刚在C++遇到了一些容器实现。该类使用内部缓冲区来管理其对象。这是一个没有安全检查的简化版本

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 这将在Container()中冗余地构造/销毁E对象,如果未自定义new/delete,则~Container()。这似乎很危险。

Add()中使用放置new是防止危险的冗余构造函数/析构函数调用的最佳方法(除了将类绑定到功能齐全的池之外(?

使用放置new时,new char[sizeof(E)*100]是分配缓冲区的正确方法吗?

AFAIK 这将冗余地构造/销毁E对象

看起来是这样。new ed数组已经应用了默认构造函数,delete[]也会为所有元素调用析构函数。实际上,Add()Remove() 方法除了维护size计数器外,几乎没有其他作用。

使用放置 new 时,new char[sizeof(E)*100]是分配缓冲区的正确方法吗?

最好的方法是选择已经为您处理所有内存问题的std::allocator

使用放置new并自行管理内存需要您了解许多问题(包括(;

  • 对准
  • 分配和使用的大小
  • 破坏
  • 安置等建筑问题
  • 可能的别名

这些都不是无法克服的,它刚刚在标准库中完成。如果你有兴趣追求自定义分配器,全局分配函数(void* operator new (std::size_t count);(将是内存分配的适当起点。


如果没有进一步解释代码的原始用途 - std::vectorstd::array将是管理容器中元素的更好选择。

代码存在许多问题。如果在调用Add()之前调用Remove()则将已破坏的对象执行赋值。

否则,delete[] buffer将调用数组中 100 个对象的析构函数。以前可能叫过。

这是一个有效的程序:

#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;
}