Can I use placement new(this) in operator=?
背景:我有一个包含许多变量的复杂类。我有一个健全的和经过测试的复制建设者:
1 2 3 4 5
| Applepie::Applepie( const Applepie ©) :
m_crust(copy.m_crust),
m_filling(copy.m_filling)
{
} |
在初始化器列表中调用的一些成员变量copy构造函数执行分配。
问题:我需要创建operator=。我可以简单地执行以下操作,而不是使用赋值而不是初始化列表来复制现有的构造,以及释放正在被替换的内存等等:
1 2 3 4 5 6 7 8 9
| Applepie& Applepie::operator=( const Applepie ©)
{
if( this != ©)
{
this->~Applepie(); // release own object
new(this) Applepie(copy); // placement new copy constructor
}
return *this;
} |
换句话说,destroy self后面跟着placement new copy构造函数在语义上与operator=相同吗?
这似乎有可能显著减少重复代码,并确认每个变量都已正确初始化,但在分配过程中可能会稍微降低效率。我是不是错过了一些更难懂的东西?
理论基础:我的实际班级大约有30个变量。我担心的是,我的复制构造函数和赋值运算符都必须复制全部30个,并且代码可能会出现分歧,导致这两个操作执行的方式不同。
- 如果copy ctor抛出,则表示您已经破坏了对象,因此您没有提供任何预期安全保证。
- @r martinho——在我看来,如果手动分配每个变量,复制ctor将抛出的任何情况都会导致我的operator=throw…所以…那看起来还是一样的?
- 问题不是operator=抛出,是operator=使对象处于无效状态!它被摧毁了。
- 苹果派真的需要30个成员变量吗?任何类都需要30个成员变量吗?可疑的这些成员中的一些可以组合成合理的类吗?很可能。
- jcWenger:复制和交换IDium使用copy-cnstructor,但如果复制构造函数抛出,则不会使当前对象处于无效状态。在您的版本中,无论发生什么情况,都会丢失对象,因此异常会导致系统中的对象无效(这是不可取的)。不应该有零代码提供担保。
- 销毁和重新创建所有成员可能会产生额外成本。通常,一些成员可以在分配给时重用现有资源。
- @jcwenger:如果您的成员对象设计得很好,那么您根本不需要手动编写复制构造函数,这很容易解决您的问题。
- 另请参考:stackoverflow.com/questions/1734628/…
正如萨特在"例外C++"中所说的,它也不例外。这意味着,如果在new或新对象的构造过程中发生任何错误,则赋值的左手操作数处于错误(未定义)状态,这将导致更多的麻烦。我强烈建议使用copy&swap习惯用法。
1 2 3 4 5 6
| Applepie& Applepie::operator=(Applepie copy)
{
swap(m_crust, copy.m_crust);
swap(m_filling, copy.m_filling);
return *this;
} |
当您的对象也使用PIMPL习惯用法(指向实现的指针)时,交换只通过更改两个指针来完成。
- +1.swap是一个很好的低估函数…其他优点:如果参数是右值,编译器可以执行复制省略。
- 所以…我的问题是我的实际类有大约30个变量。我的复制构造函数在初始值设定项列表中有30个变量。copy&swap仍然需要我进行30个swap()调用——我的目标是减少apple pie pie(原始)和applepie apple;apple=original;产生不同结果的可能性。
- @JCWENGER:是的,这是一个麻烦的召唤。因此,它可能是pimpl习惯用法的候选者,但在切换到该习惯用法之前,请考虑它是以一定的代价来实现的:然后每个成员访问都要经过一个指针。另一个想法是把你的Applepie类分成更小的部分。如果一个类中有30个数据成员,那么设计可能有问题…
- @JCWENGER:我想说您的真正目标是编写难以破解的代码。复制和交换习语就是这样做的。我认为你正在努力弥补其他问题(比如30名会员)。为了提高效率,对象应该已经有了swap()方法。因此,赋值运算符只是成为对复制构造函数的调用,然后调用swap()。
- 如果没有看到代码是很困难的,但是如果您的类正在管理资源(否则编译器生成的复制构造函数和赋值将起作用),并且您有30多个字段…这有点代码味道。考虑修改设计以划分责任。
除了Rene的答案之外,还有一个问题,即如果applepie是实际对象的基类,会发生什么情况:applepie将用错误类型的对象替换对象!
- 如果对象没有在句柄工作者习惯用法中实现,那么operator=无论如何都会进行对象切片。