我应该使用shared_ptr还是unique_ptr

我一直在使用pimpl习语做一些对象,但我不确定是否使用std::shared_ptrstd::unique_ptr

我明白, std::unique_ptr更有效率,但这对我来说并不是什么大问题,因为无论如何这些对象都是相对重量级的,所以std::shared_ptr超过std::unique_ptr的代价相对较小。

我目前正在与std::shared_ptr只是因为额外的灵活性。 例如,使用std::shared_ptr允许我将这些对象存储在散列表中以便快速访问,同时仍然可以将这些对象的副本返回给调用者(因为我相信任何迭代器或引用可能会很快变得无效)。

然而,这些对象实际上并没有被复制,因为变化会影响到所有的副本,所以我想知道,也许使用std::shared_ptr并允许副本是某种反模式或坏东西。

它是否正确?

我一直在使用pimpl习语做一些对象,但我不确定是否使用shared_ptrunique_ptr

绝对unique_ptrscoped_ptr

Pimpl不是一种模式,而是一种处理编译时间依赖性和二进制兼容性的习惯用法。 它不应该影响对象的语义,特别是在复制行为方面。

你可以使用任何一种你想要的智能指针,但是这两个保证你不会意外地在两个不同的对象之间共享实现,因为它们需要有意识地决定复制构造函数和赋值运算符的实现。

然而,这些对象实际上并没有被复制,因为变化会影响到所有的拷贝,所以我想知道使用shared_ptr和允许拷贝是某种反模式或坏事。

它不是反模式,实际上它是一种模式:别名。 您已经在C ++中用裸指针和引用来使用它。 shared_ptr提供额外的“安全”度量来避免死亡引用,代价是额外的复杂性和新的问题(小心产生内存泄漏的周期)。


与皮姆普无关

我知道unique_ptr更高效,但这对我来说并不是什么大问题,因为无论如何这些对象都是相对重量级的,所以shared_ptrunique_ptr的代价相对较小。

如果你能分解一些状态,你可能想看看Flyweight模式。

如果你使用shared_ptr ,这不是真正的古典pimpl习语(除非你采取额外的步骤)。 但真正的问题是为什么你要使用一个聪明的指针开始; 非常清楚delete应该在哪里发生,并且不存在exception安全或其他问题。 最多一个智能指针会为您节省一两行代码。 而唯一具有正确语义的是boost::scoped_ptr ,我不认为它适用于这种情况。 (IIRC,它需要一个完整的types才能被实例化,但我可能是错的。)

pimpl习语的一个重要方面是它的使用应该对客户是透明的; 这个类应该像传统的实现一样。 这意味着要么禁止复制和分配,要么实现深层复制,除非这个类是不可变的(没有非const成员函数)。 通常的智能指针都没有实现深度复制; 当然,你可以实现一个,但是当副本发生的时候,它可能仍然需要一个完整的types,这意味着你仍然必须提供一个用户定义的拷贝构造函数和赋值操作符(因为它们不能内联)。 鉴于此,使用智能指针可能不值得费神。

如果对象是不可变的,则是一个例外。 在这种情况下,复制是否很深并不重要, shared_ptr完全处理这种情况。

当你使用一个shared_ptr (例如在一个容器中,然后查看它并按返回),你不会导致它指向的对象的副本,只是一个带有引用计数的指针副本。

这意味着如果从多个点修改底层对象,则会影响同一实例上的更改。 这正是它所devise的,所以不是一些反模式

当传递一个shared_ptr (如注释所述)时,最好在需要的时候通过const引用和copy(通过递增引用计数)来传递。 至于回报,个案。

是的,请使用它们。 简而言之,shared_ptr是一个智能指针的实现。 unique_ptr是一个自动指针的实现: