一、原则
使用std::shared_ptr
管理具备共享所有权的资源
二、原理
std::shared_ptr
是通过引用计数的方式来管理自己的生命周期。
引用计数是通过动态分配一个控制块(参考三、关于控制块的几点说明)来实现的。
我们可以想象与std::shared_ptr<T>
对象相关的内存:如图所示。

从这张图中我们还可以看出:
-
std::shared_ptr
的尺寸是裸指针的两倍。 - 析构器的型别不是智能指针型别的一部分(区别于
std::unique_ptr
),这里特别注意,shared_ptr
析构器是随着新对象一起指定的,而不是像unique_ptr
那样,在定义的时候指定一次就好了。
三、关于控制块的几点说明
- 创建时机:
-
std::make_shared
总是创建一个控制块 - 从具备专属所有权的指针(即
std::unique_ptr
)出发构造一个std::shard_ptr
时,会创建一个控制块 - 当
std::shared_ptr
构造函数使用裸指针作为实参来调用时,它会创建一个控制块。
- 有一个坑:从同一个裸指针出发来构造不止一个
std::shared_ptr
的话,被指涉到的对象将会有多个控制块。多重控制块意味着多重的引用计数,而多重的引用计数意味着该对象会被析构多次!!,错误示例如下:
auto p = new Widget;
std::shared_ptr<Widget> sp1(p);
std::shared_ptr<Widget> sp2(p);
这段代码我们可以习得两个教训。首先,尽可能避免将裸指针传递给一个std::shared_ptr
的构造函数。常用的替代手法,是使用std::make_shared
。其次,如果必须将一个裸指针传递给std::shared_ptr
的构造函数(e.g. 需要自定义析构器),就直接传递new
运算符的结果,而非传递一个裸指针变量,即 std::shared_ptr<Widget> sp(new Widget, delFunc);
这种形式。
网友评论