使用运算符 new 和placement new 创建非默认可构造对象的动态数组

时间:2021-06-03 23:12:51

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

我是 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 的工作方式相似吗? (与初始化分开分配)。

1 个答案:

答案 0 :(得分:2)

<块引用>

如果我错过了什么,请指导我。

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

<块引用>

这与类分配器的工作方式相似吗?

非常相似。一些差异:

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