放置新的以避免复制构造函数

时间:2013-03-14 14:52:20

标签: c++ placement-new

我有一个简单的类,其中包含指向其中一个成员的指针:

struct X {
    int val;
    int* pVal;
    X(int v) : val(v), pVal(&val) {}
}

X x(1);

我有一些像这样的代码:

void foo() {
    doStuffWith(x);
    x = X(2); // completely discard the old value of X, and start again
    doStuffWith(x);
}

我担心当重新分配x时,如果没有返回值优化,x.pVal将无效地指向临时X(2)的成员。

我意识到我可以编写一个复制构造函数来解决这个问题。但是,首先进行复制似乎是浪费,而不是在内存中的正确位置构建对象。


在这里使用placement new运算符是否合理?或者这对析构函数有无意义的后果吗?

void foo() {
    doStuffWith(x);
    new (&x) X(2); // completely discard the old value of X, and start again
    doStuffWith(x);
}

3 个答案:

答案 0 :(得分:3)

使这项工作最明显(也可能是最有效)的方法是提供副本分配和复制构造操作符以“做正确的事”,这是一般顺序:

struct X {
    int val;
    int* pVal;

    X(int v) : val(v), pVal(&val) {}   
    X(X const &other) : val(other.val), pVal(&val) {} 

    // pVal was already set by ctor, so just ignore it:
    X &operator=(X const &other) { val = other.val; return *this; }

    // and allow assignment directly from an int:
    X &operator=(int n) { val = n; return *this; }
};

然后其余的代码可以只复制/分配X个对象,而不会跳过箍来防止损坏。

答案 1 :(得分:1)

它不会破坏x的旧值,但您是正确的,默认的副本分配运算符也不会执行您想要的操作。

答案 2 :(得分:-1)

对我来说,如果在放置new之前调用X的析构函数,似乎是完全可以接受的解决方案。在语法上允许甚至实际上没有为类指定析构函数。

struct X {
  int val;
  int* pVal;
  X(int v) : val(v), pVal(&val) {}
};

X x(1);

void foo() {
  doStuffWith(x);
  x.~X();
  new (&x) X(2); 
  doStuffWith(x);
}

在这种形式下,它是为任何对象重用内存的正确方法(但是只有当对象的ctor不能抛出时!否则UB可能在程序关闭时发生,即双重调用析构函数)。

实际上,标准保证了以非数组形式传递和返回new的指针的相等性:

  

18.6.1.3安置表格

     

...

     

void * operator new(std :: size_t size,void * ptr)noexcept;

     

返回:ptr。

     

备注:故意不执行任何其他操作。

(和转换为void *然后回到相同的结果 指针类型也保证与源指针相同)

但是,为避免不正确使用该类,定义复制赋值和复制构造函数或将此类声明为不可复制(具有已删除的类)将更安全

并且只有最后一次(不可复制的)案例可能被视为使用新地点的原因。

虽然我远没有推广新的普通用途,但它表达了直接重用对象内存并且不依赖于任何优化的意图。当然,复制构造函数和复制赋值更安全,但是没有完全表达这个意图:实际上不需要“复制”,应该构造新对象来代替旧对象。