这里引用了cppreference的实现说明部分std::shared_ptr
,其中提到有两个不同的指针(如粗体所示):可以返回的指针get()
,以及控制块中保存实际数据的指针.
在典型的实现中,
std::shared_ptr
只保留两个指针:
存储的指针(返回一个
get()
)指向控制块的指针
控制块是一个动态分配的对象,它包含:
指向托管对象的指针或托管对象本身
删除(类型擦除)
分配器(类型擦除)
数量
shared_ptrs
是自己的管理对象的数目
weak_ptrs
引用该管理对象
shared_ptr
直接保持的指针是由返回get()
的指针,而控制块保持的指针或对象是当共享所有者的数量达到零时将被删除的指针或对象.这些指针不一定相等.
我的问题是,为什么托管对象需要两个不同的指针(两个粗体)(除了指向控制块的指针)?那个回来的人get()
不够吗?为什么这些指针不一定相等?
这样做的原因是你可以拥有一个shared_ptr
指向别的东西而不是它拥有的东西,这是设计的.这是使用列为nr的构造函数实现的.8关于cppreference:
template< class Y > shared_ptr( const shared_ptr& r, T *ptr );
一个shared_ptr
与此构造函数创建共享所有权与r
不过分ptr
.考虑这个(人为的,但是说明性的)代码:
std::shared_ptrcreator() { using Pair = std::pair ; std::shared_ptr p(new Pair(42, 3.14)); std::shared_ptr q(p, &(p->first)); return q; }
一旦此函数退出,只有指向该int
对子对象的指针可用于客户端代码.但由于之间的共享所有权q
和p
,指针q
"保持活着"整个Pair
对象.
一旦发生了交易,就Pair
必须将整个对象的指针传递给删除器.因此,指向Pair
对象的指针必须存储在删除器旁边的某个地方 - 换句话说,在控制块中.
对于一个不那么人为的例子(可能甚至更接近该特征的原始动机),考虑指向基类的情况.像这样的东西:
struct Base1 { // ::: }; struct Base2 { // ::: }; struct Derived : Base1, Base2 { // ::: }; std::shared_ptrcreator() { std::shared_ptr p(new Derived()); std::shared_ptr q(p, static_cast (p.get())); return q; }
当然,真正落实std::shared_ptr
已制定的所有隐式转换,这样的p
-和- q
舞蹈creator
是没有必要的,但我已经把它有类似于第一个例子.