【问题标题】:Using operator new and placement new to create a dynamic array of non-default constructible objects使用 operator new 和placement new 创建非默认可构造对象的动态数组
【发布时间】:2021-06-03 23:12:51
【问题描述】:

我是placement new 的新手,所以我想将分配与初始化分开,使用它沿operator new 分配和构造我的用户定义类型class Foo 的数组。

这是我尝试过的:

struct Foo{
    Foo(int x) : x_(x){
        std::cout << "Foo(int)\n";
    }
    ~Foo(){
        std::cout << "~Foo()\n";
    }
    int x_ = 0;
};

int main(){

    Foo* pf = static_cast<Foo*>(operator new[](10 * sizeof(Foo) ) );

    for(int i  = 0; i != 10; ++i)
        new(pf + i)Foo(i * 7);

    for(int i = 0; i != 10; ++i)
        std::cout << pf[i].x_ << ", ";
    std::cout << '\n';

    for(int i = 0; i != 10; ++i)
        pf[i].~Foo();

    operator delete[](pf);

}
  • 代码看起来运行良好,所以如果我遗漏了什么,请指导我。这是否类似于class allocator 的工作方式? (将分配与初始化分开)。

【问题讨论】:

  • 这对我来说看起来很好,但是如果 Foo 过度对齐,这将不尊重 Foo 的对齐要求。如果 Foo 是模板类型,我会将其更改为 Foo* pf = static_cast&lt;Foo*&gt;(operator new[](10 * sizeof(Foo), std::align_val_t{alignof(Foo)} ) );
  • 我会完全摆脱operator new[]operator delete[],并使用std::aligned_storage作为数组,例如:using elemType = std::aligned_storage_t&lt;sizeof(Foo), alignof(Foo)&gt;; elemType data* = new elemType[10]; Foo* pf = reinterpret_cast&lt;Foo*&gt;(data); ... for(int i = 0; i &lt; 10; ++i) { new (pf+i) Foo(...); } ... for(int i = 0; i &lt; 10; ++i) pf[i].~Foo(); ... delete[] data;
  • 你可以使用std::vector,就像你可以使用emplace数据一样。更简单,更易于维护。
  • @RemyLebeau elemType *data = ...(错字)

标签: c++ new-operator placement-new


【解决方案1】:

如果我遗漏了什么,请指导我。

对于Foo 来说似乎很好。但更一般地说,这种方法不是异常安全的。如果其中一个类构造函数抛出,那么您的分配泄漏并且之前构造的对象不会被销毁。

这是否类似于类分配器的工作方式?

非常相似。一些不同之处:

  • std::allocator&lt;T&gt;::allocate 使用 ::operator new(std::size_t, std::align_val_t)std::allocator&lt;T&gt;::deallocate 调用 ::operator delete(void*, std::align_val_t),因为 C+++17。这是支持过度对齐类型所必需的。
  • 从技术上讲,您永远不会启动阵列的生命周期。这在实践中不是什么大问题。没有任何实用的方法可以在重用内存中创建动态数组。虽然,从 C++20 std::allocator&lt;T&gt;::allocate 实际上确实启动了 T 数组的生命周期(而不启动对象的生命周期)。从技术上讲,没有办法在标准 C++ 中为非平凡类型实现它。

【讨论】:

  • 如果使用std::allocator&lt;T&gt;::allocate 有没有办法用pf 来调用std::allocator<T>::deallocate 而不是data 或者你仅限于在data 上调用.deallocaate(),获得的实际指针是.allocate()?
  • @DavidC.Rankin 我对data 的意思感到困惑?
  • 抱歉造成混淆,我在godbolt source 上查看此内容,其中定义ALLOCA 触发了std::allocator 的使用。 (这可能应该是一个单独的问题——我稍后会删除 cmets,但我很好奇我是否看对了)
  • @DavidC.Rankin 是的,使用分配器与使用 std::aligned_storage 完全不同。您将operator ::new 的结果传递给operator ::delete,并将std::allocator::allocate 的结果传递给std::allocator::deallocate,就像您将new 的结果传递给deletedelete[]
  • 谢谢你——这就是我被挂断的地方。
猜你喜欢
  • 1970-01-01
  • 2021-08-31
  • 1970-01-01
  • 1970-01-01
  • 2013-08-16
  • 1970-01-01
  • 2017-10-12
  • 2015-10-17
  • 2014-08-07
相关资源
最近更新 更多