使用新放置作为复制分配运算符不好吗?
Is using a placement new as a copy assignment operator bad?
有时我想用const
成员制作类/结构。我意识到这是一个坏主意,原因有很多,但为了论证,让我们假装唯一的原因是它使格式良好的operator =
变得麻烦,至少可以说。但是,我设计了一个相当简单的解决方法,如以下结构所示:
struct S {
const int i;
S(int i) : i(i) {}
S(const S& other) : i(other.i) {}
S& operator =(const S& other) {
new (this) S(other);
return *this;
}
};
忽略析构函数和移动语义,有什么大理由不应该这样做吗?在我看来,它像一个更类型安全的版本
S& operator =(const S& other) {
const_cast<int&>(i) = other.i;
return *this;
}
因此,问题的总结是:不应该使用放置-new来实现复制分配以具有与复制构造相同的语义,这有什么主要原因吗?
我不认为放置新在这里是一个问题,而是产生未定义行为的const_cast
:
C++10.1.7.1-4除了可以修改任何声明为可变 (10.1.1) 的类成员之外,任何在其生存期 (6.6.3) 期间修改 const 对象的尝试都会导致未定义的行为。
在编译器开始优化之前,您可能会侥幸逃脱。
另一个问题是在活的(非销毁的)对象占用的片段内存上使用新的放置位置。但是你可能会侥幸逃脱,而有问题的对象有一个微不足道的析构函数。
真正重要的理由不应该这样做吗?
您必须绝对确定每个派生类都定义了自己的赋值运算符,即使它是微不足道的。因为派生类的隐式定义的复制赋值运算符会搞砸一切。它将调用
S::operator=
,这将在其位置重新创建错误类型的对象。这种销毁和构造赋值运算符不能由任何派生类重用。因此,您不仅强制派生类提供显式复制运算符,而且还强制它们在其赋值运算符中坚持相同的销毁和构造习惯用法。
您必须绝对确定在此类赋值运算符销毁和构造对象时没有其他线程正在访问该对象。
类可能具有一些数据成员,这些成员不得受赋值运算符的影响。例如,线程安全类可能有某种互斥锁或关键节成员,当当前线程要销毁和构造该互斥锁时,其他一些线程正在等待它们......
在性能方面,它与标准的复制和交换习惯用法相比几乎没有任何优势。那么,经历上述所有痛苦会有什么收获呢?
- 正在尝试重载二进制搜索树分配运算符
- 自定义先决条件对移动分配运算符有效吗
- 分配给转换运算符失败-C++
- 为什么我可以在不重载 "=" 运算符的情况下将一个对象分配给另一个对象?
- C++ - 没有自定义交换功能的移动分配运算符?
- 如何增加以前由新运算符分配的 C++ std::list 数组的大小?
- 为什么在通过引用返回运算符分配时取消引用'this'指针?
- 使用 'new[]' 运算符分配内存
- 操作后通过运算符分配对象
- 新运算符分配的大小大于声明的大小.为什么
- 如何控制运算符 [] 分配的值
- 字符** 使用 new 运算符分配内存
- 新的运算符分配函数顺序连续性和初始值
- 如何释放使用 C++ "new" 运算符分配的 C 中的内存
- 只能使用CUDA中的新运算符分配有限的内存
- 使用友元运算符分配和私有变量是不可访问的
- 释放由赋值运算符C++分配的内存
- 确定C++中新运算符分配的内存大小
- 用于新运算符分配的公共内存
- 如何在C++中检索由新运算符分配的对象的地址