【问题标题】:Using placement new in a container在容器中使用新位置
【发布时间】:2016-05-09 20:13:01
【问题描述】:

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

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

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

【问题讨论】:

  • 这段代码有很多问题,但是这段代码中没有放置-new
  • 不,不是。我从没说过会。
  • 我明白了,我当时误解了这个问题。
  • 我想你想在Remove()中解构buffer[size-1],或者在解构之前先递减。按照您的示例中的编写方式,您遇到了一个错误。
  • @mindriot:你是对的。由于提取示例而导致的错误。修好了。

标签: c++ memory-management placement-new object-construction object-destruction


【解决方案1】:

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

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

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

最好选择std::allocator,它已经为您处理了所有内存问题。

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

  • 对齐
  • 分配和使用的大小
  • 破坏
  • 就位等施工问题
  • 可能的别名

这些都不是不可能克服的,它已经在标准库中完成了。如果您有兴趣使用自定义分配器,global allocation functions (void* operator new (std::size_t count);) 将是内存分配的合适起点。


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

【讨论】:

  • 我不知道std::allocator 提供了比简单的placement-new 更强大的保证。你能链接到某处的参考吗? (cppreference,或标准中的部分)
  • @YamMarcovic。它使事情变得更容易,因为它已经为您完成了所有艰苦的工作。
  • @Silicomancer。不,使用全局分配函数::new (sizeof(E)*100); 是正确的
  • @Niall 哦,我现在明白了。 ::allocate() 调用::new() 保证正确对齐,然后::construct() 将使用placement-new 在该对齐地址处构建。谢谢你的耐心。 :)
  • @YamMarcovic。没问题。在您的标准库实现中研究 vector 之类的实现也提供了一些关于使用 std::allocator 的指导。
【解决方案2】:

代码存在许多问题。 如果您在调用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;
}

【讨论】:

    猜你喜欢
    • 2013-03-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-23
    • 2022-11-14
    • 1970-01-01
    相关资源
    最近更新 更多