何时使用shared_ptr以及何时使用原始指针?

class B; class A { public: A () : m_b(new B()) { } shared_ptr<B> GimmeB () { return m_b; } private: shared_ptr<B> m_b; }; 

比方说,B是一个在语义上不应该存在于A的生命周期之外的类,也就是说,B本身是完全没有意义的。 GimmeB应该返回一个shared_ptr<B>B*吗?

一般来说,完全避免使用C ++代码中的原始指针代替智能指针是一种很好的做法吗?

我认为shared_ptr应该只在明确的所有权转移或共享时才使用,除了函数分配一些内存,填充一些数据并返回它的情况之外,我认为这是很less见的,主叫方和被叫方之间的理解是,前者现在对这些数据负责。

我认为你的分析是非常正确的。 在这种情况下,我也会返回一个空B* ,甚至是一个[const] B&如果这个对象保证永远不为null。

经过一段时间仔细阅读指针,我得到了一些指导,告诉我在很多情况下要做什么:

  • 如果您返回的对象的生命周期将由调用方pipe理,则返回std::unique_ptr 。 调用者可以将其分配给std::shared_ptr如果需要的话)。
  • 返回std::shared_ptr实际上是相当罕见的,而且当它有意义时,通常很明显:向调用者指出它将延长指向对象的生命期,超出原始维护资源的对象的生命周期。 从工厂返回共享指针也不例外:例如,你必须这样做。 当你使用std::enable_shared_from_this
  • 你很less需要std::weak_ptr ,除非你想了解lock方法。 这有一些用途,但它们很less见。 在你的例子中,如果A对象的生命周期从调用者的angular度来看并不是确定性的,那么这是可以考虑的。
  • 如果你返回一个对调用者无法控制其生命周期的现有对象的引用,则返回一个空指针或引用。 通过这样做,你告诉调用者一个对象存在,并且她不必关心它的生命周期。 如果不使用nullptr值,则应该返回一个引用。

“什么时候应该使用shared_ptr ,什么时候应该使用原始指针? 有一个非常简单的答案:

  • 如果您不想将任何所有权连接到指针,请使用原始指针。 这项工作通常也可以通过参考来完成。 原始指针也可以用在一些低级代码中(比如实现智能指针或实现容器)。
  • 当您想要对象的唯一所有权时,请使用unique_ptrscope_ptr 。 这是最有用的选项,应该在大多数情况下使用。 独特的所有权也可以通过直接创build一个对象来表示,而不是使用指针(如果可以的话,这比使用unique_ptr更好)。
  • 当你希望共享指针的所有权时,使用shared_ptrintrusive_ptr 。 这可能是混乱和低效率,往往不是一个好的select。 共享所有权在一些复杂的devise中可能是有用的,但一般应避免,因为这会导致代码难以理解。

shared_ptr从原始指针执行完全不同的任务, shared_ptr和raw指针都不是大多数代码的最佳select。

如果你不希望GimmeB()的被调用者能够通过在A的实例死后保留ptr的副本来延长指针的生命期,那么你肯定不应该返回一个shared_ptr。

如果被调用者不应该长时间保存返回的指针,也就是说,在指针之前没有A的生命期实例到期的风险,那么原始指针会更好。 但是,即使是更好的select,也只是使用引用,除非有充分的理由使用实际的原始指针。

最后在返回的指针可以在A实例的生命期到期之后存在的情况下,但是你不希望指针本身延长B的生命期,那么你可以返回一个weak_ptr,你可以用它来testing是否仍然存在。

底线是通常比使用原始指针更好的解决scheme。

以下是一个很好的经验法则:

  • 当没有传输或共享所有权引用或纯指针是不够的。 (普通指针比引用更灵活。)
  • 当拥有所有权但没有共享所有权时,则std::auto_ptr<>std::unique_ptr<>是一个不错的select。 通常情况下,工厂function。
  • 当共享所有权时, std::shared_ptr<>boost::intrusive_ptr<>是一个很好的用例。

我同意你的看法, shared_ptr是最好的时候显式共享资源发生,但也有其他types的智能指针可用。

在你的确切情况下:为什么不返回一个参考?

一个指针表明数据可能是空的,但是这里在你的A总会有一个B ,所以它永远不会为空。 引用声明了这种行为。

这就是说,我曾经看到人们提倡使用shared_ptr即使在非共享的环境中,并给予weak_ptr句柄,也就是“保护”应用程序并避免陈旧的指针。 不幸的是,由于你可以从weak_ptr恢复一个shared_ptr (这是实际操纵数据的唯一方法),即使它不是本意的,它仍然是共享所有权。

注意: shared_ptr有一个微妙的错误,除非你明确地写了一个拷贝构造函数和一个拷贝赋值操作符,否则默认情况下, A的拷贝将与原始拷贝共享相同的B 当然,你不会在A使用一个原始指针来保存一个B ,你会:)?


当然,另一个问题是你是否真的需要这样做。 良好的devise原则之一是封装 。 为了实现封装:

你不应该回到你的内部处理(见德米特法 )。

所以也许你的问题的真正答案是,而不是给B的引用或指针,它应该只能通过A的界面进行修改。

一般来说,我会尽可能地避免使用原始指针,因为它们的含义非常模糊 – 您可能不得不释放指针,但也许不会,只有人类阅读和书写的文档才会告诉您是什么情况。 而且文件总是不好,过时或被误解。

如果所有权是一个问题,请使用智能指针。 如果不是的话,我会尽可能使用一个参考。

  1. 您在构buildA时分配B.
  2. 你说B不应该一辈子坚持下去。
    这两个都指向B是A的成员,并且只是返回一个引用访问器。 你在过度工作吗?

避免使用原始指针是一个很好的做法,但是不能只用shared_ptrreplace所有的指针。 在这个例子中,你的类的用户会认为延长B的生命期超过了A的生命周期是可以的,并且可能由于自己的原因决定将返回的B对象保留一段时间。 您应该返回一个weak_ptr ,或者,如果B在A被销毁时绝对不能存在,则引用B或者仅仅是一个原始指针。

当你说:“让我们说B是一个在A的生命周期之外语义上不应该存在的类”

这告诉我B在逻辑上不应该存在没有A,但物理存在呢? 如果你能确定没有人会尝试使用a * B之后的A,否则可能原始指针将罚款。 否则,更聪明的指针可能是合适的。

当客户有一个直接指向A的指针时,你必须相信他们会适当地处理它; 不要试图等等

我发现C ++核心指南为这个问题提供了一些非常有用的提示:

使用原始指针(T *)或更智能的指针取决于谁拥有该对象(其释放obj的内存的责任)。

拥有 :

 smart pointer, own<T*> 

不是自己的:

 T*, T&, span<> 

自己的<>,span <>在Microsoft GSL库中定义

这里是经验法则:

1)从不使用原始指针(或不是自己的types)来传递所有权

2)智能指针只能在所有权语义被使用时使用

3)T *或所有者指定一个单独的对象(仅)

4)使用vector / array / span作为数组

5)对于我不熟悉的人来说,shared_ptr通常用在你不知道谁将释放obj的情况下,例如,一个obj被multithreading使用