我有一个简单的类,其中包含指向其中一个成员的指针:
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);
}
答案 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 *然后回到相同的结果 指针类型也保证与源指针相同)
但是,为避免不正确使用该类,定义复制赋值和复制构造函数或将此类声明为不可复制(具有已删除的类)将更安全
并且只有最后一次(不可复制的)案例可能被视为使用新地点的原因。
虽然我远没有推广新的普通用途,但它表达了直接重用对象内存并且不依赖于任何优化的意图。当然,复制构造函数和复制赋值更安全,但是没有完全表达这个意图:实际上不需要“复制”,应该构造新对象来代替旧对象。