放置new以避免复制构造函数
Placement new to avoid copy constructor
我有一个简单的类,它包含一个指向它自己成员之一的指针:
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)
的成员。
我意识到我可以写一个复制构造函数来解决这个问题。然而,比起在内存中正确的位置开始构造对象,首先执行复制似乎是浪费的。
在这里使用new操作符是否合理?或者这会给析构函数带来意想不到的后果吗?
void foo() {
doStuffWith(x);
new (&x) X(2); // completely discard the old value of X, and start again
doStuffWith(x);
}
实现此功能的最明显(可能也是最有效)的方法是提供复制赋值和复制构造操作符来"做正确的事情",大致顺序如下:
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
对象,而不跳过环,以防止损坏。
不
它不会破坏x
的旧值,但是您是对的,默认的复制赋值操作符也不会做您想要的。
对于我来说,如果X的析构函数在放置new之前被调用,这似乎是可以接受的解决方案。即使实际上没有为类指定析构函数,在语法上也是允许的。
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);
}
在这种形式下,为任何对象重用内存都是正确的方法(但前提是对象的函数不能抛出!)否则在程序关闭时可能发生UB,即析构函数的双重调用)。
实际上,标准保证了从new位置传递和返回的指针在非数组形式中的相等性:
18.6.1.3放置表格
…
void* operator new(std::size_t, void* ptr) noexcept;
返回:ptr。
备注:故意不执行其他操作。
(以及转换为void*然后返回的结果)指针类型也保证与源指针相同)
然而,为了避免类的不当使用,定义复制赋值和复制构造函数或将该类声明为不可复制的(带有已删除的)
会更安全。并且只有最后一个(不可复制的)情况才可以作为使用位置new的理由。
虽然我并不提倡将new放置用于一般用途,但它表达了直接重用对象内存的意图,并且不依赖于任何优化。复制构造函数和复制赋值当然更安全,但并不能完全表达这一意图:在实际需要时,应该构造新对象来代替旧对象。
- C++17复制构造函数,在std::unordereded_map上进行深度复制
- 为什么在C++中使用私有复制构造函数与删除复制构造函数
- 当从函数参数中的临时值调用复制构造函数时
- 如果有一个模板构造函数只有一个泛型参数,为什么我必须有一个复制构造函数
- 为什么需要复制构造函数,在哪些情况下它们非常有用
- 使用仅使用一次的变量调用的复制构造函数.这可能是通过调用move构造函数进行编译器优化的情况吗
- 为什么类中的ostringstream类型的成员会导致";调用隐含删除复制构造函数";错误
- 复制构造函数、赋值运算符C++
- std::ofstream 作为类成员删除复制构造函数?
- 复制构造函数C++无法正确复制指针
- 关于复制构造函数的一个棘手问题
- 为什么调用复制构造函数而不是移动构造函数?
- 填充上编译器生成的复制构造函数之间的不一致
- C++ 对象指针数组的复制构造函数
- C++ 基本 CTOR 说明 - 为什么不调用赋值/复制构造函数
- 防止在复制构造函数中隐式调用基构造函数
- 为用户定义的类正确调用复制构造函数/赋值运算符
- 具有已删除移动和复制构造函数的类的就地构造
- 复制构造函数隐式转换问题
- 复制构造函数中的递归调用