【问题标题】:Should I use shared_ptr or unique_ptr我应该使用 shared_ptr 还是 unique_ptr
【发布时间】:2011-07-31 10:05:10
【问题描述】:

我一直在使用 pimpl 成语制作一些对象,但我不确定是使用 std::shared_ptr 还是 std::unique_ptr

我知道std::unique_ptr 效率更高,但这对我来说不是什么大问题,因为这些对象无论如何都相对重量级,所以std::shared_ptr 相对于std::unique_ptr 的成本相对较小。

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

但是,在某种程度上,这些对象实际上并没有被复制,因为更改会影响所有副本,所以我想知道也许使用 std::shared_ptr 并允许复制是某种反模式或坏事。

这对吗?

【问题讨论】:

  • 使用其中一种或另一种会深刻地改变您赋予对象的复制语义。两者都有用途。我想说的是 C++ 世界中更惯用的 unique_ptr,但是具有共享实现的对象有它们的用途,特别是如果您正在编写“外来”代码(例如 COM、C++/CLI),或者如果该类真的看起来像一个“引用类型”。
  • 在 C++11 中推荐的方式是使用 unique_ptr,毕竟你不需要复制或与任何人共享实现。 unique_ptr 在运行时也更快。

标签: c++ c++11 shared-ptr pimpl-idiom unique-ptr


【解决方案1】:

我一直在使用 pimpl 成语制作一些对象,但我不确定是使用 shared_ptr 还是 unique_ptr

绝对是unique_ptrscoped_ptr

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

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

但是,这些对象实际上并没有被复制,因为更改会影响所有副本,所以我想知道也许使用 shared_ptr 并允许复制是某种反模式或坏事。

它不是反模式,实际上是一种模式:别名。您已经在 C++ 中使用了它,并带有裸指针和引用。 shared_ptr 提供了一种额外的“安全”措施来避免死引用,但代价是额外的复杂性和新问题(当心造成内存泄漏的循环)。


与 Pimpl 无关

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

如果您可以排除某些状态,您可能需要查看Flyweight 模式。

【讨论】:

  • 那么你是说除了指针/智能指针之外,复制构造函数通常应该进行深度复制(或功能等效的东西,即写入时复制)?
  • @Clinton:从语义上讲,副本与其源是分离的,用户对实现细节不感兴趣 :) 在 C++ 中,指针和引用不是这种情况,但词汇表从来没有真的被改编了,因此在讨论这些方面时很尴尬。 ..在您的情况下,您更喜欢深拷贝还是浅拷贝应该只取决于您希望为类提供的语义,并且实现将遵循套件。
  • 但词汇从未真正被改编过”你是什么意思?
  • @curiousguy: :x 5 个月后?我认为我的观点是,做浅拷贝的“拷贝”构造函数有点奇怪,但真的......我不确定:p
  • @Frank:COW 是一个选项(我确实说过shared_ptr 已适应某些情况),但我们在这里谈论的是 Pimpl;这不是适应shared_ptr 的情况之一。
【解决方案2】:

如果你使用shared_ptr,它就不是经典的 pimpl 成语(除非你采取额外的步骤)。但真正的问题 这就是为什么要使用智能指针开始的原因;这是很 明确delete 应该出现在哪里,并且没有问题 异常安全或其他需要关注的。最多, 智能指针将为您节省一两行代码。和 只有一个语义正确的是boost::scoped_ptr, 而且我认为它在这种情况下不起作用。 (IIRC,它需要 一个完整的类型以便被实例化,但我可以 错了。)

pimpl 成语的一个重要方面是它的使用应该是 对客户透明;该类的行为应该完全像 它是经典实现的。这意味着要么抑制 复制和分配或实施深复制,除非类 是不可变的(没有非常量成员函数)。没有一般的 智能指针实现深拷贝;你可以实现一个, 当然,但它可能仍然需要一个完整的类型 每当复制发生时,这意味着您仍然必须 提供用户定义的复制构造函数和赋值运算符 (因为它们不能内联)。鉴于此,它可能不是 使用智能指针值得费心。

如果对象是不可变的,则例外。在这种情况下,它 文案深不深无所谓,shared_ptr 完全处理这种情况。

【讨论】:

    【解决方案3】:

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

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

    当传递shared_ptr(如 cmets 所说)时,最好通过 const 引用传递并在需要的地方复制(通过增加引用计数)。至于退货,视情况而定。

    【讨论】:

    • 这是个好建议,但我认为“总是按值传递和返回”有点强。首选是通过 const 引用或 rvalue 引用传递。通过简单的、可修改的引用传递有时仍然是合适的。
    • @Johann:啊,我在考虑值传递对具有成员(pimpl)shared_ptrunique_ptr 的对象的影响。在这种情况下,智能指针本身不应该被传递。
    • @Johann Gerell:不,您应该按值传递 shared_ptr。你可以,但这不是推荐的方式。推荐的方法是通过 const 引用传递它们。这比复制 shared_ptr 更有效,而且绝对安全,因为根据 shared_ptr 文档,并发读取同一个 shared_ptr 实例是可以的。
    【解决方案4】:

    是的,请使用它们。简单地说,shared_ptr 是智能指针的一种实现。 unique_ptr 是自动指针的一个实现:

    【讨论】:

      猜你喜欢
      • 2013-01-30
      • 1970-01-01
      • 2017-03-13
      • 1970-01-01
      • 2023-02-09
      • 2013-12-17
      • 2013-04-10
      • 2014-03-21
      • 2023-04-07
      相关资源
      最近更新 更多