给定一个拥有对象的生命周期与其所有者相关联的常见情况,我可以使用两种方式之一的唯一指针。.
可以赋值:
class owner
{
std::unique_ptr<someObject> owned;
public:
owner()
{
owned=std::unique_ptr<someObject>(new someObject());
}
};
可以使用重置方法:
class owner
{
std::unique_ptr<someObject> owned;
public:
owner()
{
owned.reset(new someObject());
}
};
从最佳实践的Angular 来看,我是否应该选择一种形式而不是另一种?
**对不起,伙计们。我把它简化了。堆分配发生在initialise方法中,而不是ctor中。因此,我不能使用初始化器列表。
3条答案
按热度按时间2j4z5cfb1#
the docs of
unique_ptr
'soperator=
:将r指向的对象的所有权转移到 *this,就像调用
reset(r.release())
,然后从std::forward<E>(r.get_deleter())
进行赋值一样。您所需要的只是
reset
调用,因此直接调用它会更简单wz8daaqr2#
正确的方法是使用
owned
的构造函数:除此之外,我更喜欢
reset
,因为在这种情况下你不会创建一个无用的中间示例(即使在机器级别上可能没有区别,因为优化器可以在那里做很多事情)。bkhjykvo3#
简答:两种解决方案是等效的。选择哪一个是风格的问题,或者你想表达什么。
详细内容:最好和最可取的解决方案是使用不同的设计:尽量避免任何生命周期状态。这可以通过使用新分配的堆示例立即 * 初始化 *
unique_ptr
来实现:为什么这是更可取的?因为它不是成功就是失败如果分配失败或者
someObject
的ctor爆炸,owner
的构造也会抛出。这意味着:你可以得到一个owner
示例,只有在完整的,有效的状态。作为额外的奖励,没有中介参与。但是-有些情况下您 * 无法在初始化时创建。* 当您的用例 * 显式调用生命周期状态时尤其如此:* 可能有时您需要将
owned
设置为空,然后仅将someObject
放入其中,由外部事件触发。在这种情况下,问题就出现了,我们应该从
make_unique
赋值,还是应该从reset()
赋值。两者在功能上是等同的。两者都将安全地清理另一个someObject
示例,该示例恰好位于unique_ptr
中。两者都将转移所有权,并将删除器一起移动。reset()
意味着你 * 改变 * 或 * 重新设置 * 已经存在的东西。在unique_ptr
之前为空的情况下,这可能会产生误导。此外,reset()
解决方案的优点是更短,更明确(您可以看到“新”)make_unique
在更抽象的层面上传达了含义:“创建一个唯一的someObject
示例并将其放置在此处”。有些人认为这更清晰、更精确,另一些人则认为它隐藏了实际的分配。此外,make_unique
创建了一个临时的,它被移动到目标中。优化者可以完全忽略这一点。异常安全
一般来说,
make_unique
在某些情况下可以改变异常安全性。出于这个原因,通常建议习惯使用make_unique
。在实践中,如果你创建了几个唯一管理的对象,这就变得相关了。如果一个的ctor失败了,另一个总是安全地管理。
然而,在这里讨论的情况下,我们只创建一个对象。那么,如果发生例外情况会发生什么呢?
reset(new someObject)
,则首先创建参数。如果这个失败了,很好,重置函数不会被调用。但是,如果reset()
必须清理一个现有的其他示例,并且该示例的 * 析构函数 * 抛出,那么我们就有问题了:reset()
调用将因异常而离开,并且新分配的堆参数泄漏。make_unique()
赋值,这种特殊情况可以安全地处理:如果旧示例的析构函数抛出,堆栈展开将调用make_unique()
返回的临时示例的析构函数。但这是一个“理论上的”论点。在实践中,有一种“社会契约”,目的是体面的人不要破坏。这是有道理的在dtor调用中抛出的对象可能会导致无尽的痛苦,并且基本上会危及处理任何类型的“情况”的能力。