你真的了解智能指针shared_ptr吗?

通常情况下的回答:

shared_ptr是一个类模板,它采用引用计数器,允许多个shared_ptr指向同一个对象,所以也称它为可以共享所有权的智能指针。当我们调用构造函数时,引用计数器会加一;调用析构函数时,引用计数器会减一;当我们使用operator = 时,若不是自赋值情况,所管理的原对象引用计数减一,新对象引用计数加一...当引用计数为零时,所管理的对象会被真正释放。

你可能不了解的底层实现:

我们知道 shared_ptr 是通过引用计数器来实现的,但你知道它的底层是如何实现的吗?

实际上,shared_ptr持有两个指针!,一个是它所【存储的指针】;另一个是指向【控制块】的指针,那么...控制块有包含哪些东西呢?

控制块

控制块是一个动态分配对象,它包含【指向管理对象的指针】,删除器,分配器,拥有所管理对象的shared_ptr的数目(也就是引用计数)以及引用所管理对象的weak_ptr数目

当一个shared_ptr与另一个shared_ptr共享一份对象时,实际上是通过第一个指针指向【所管理的对象】,第二个指针指向【控制块】以实现共享对象所有权的目的

你真的了解智能指针shared_ptr吗?_第1张图片

 为什么控制块也要持有【指向管理对象的指针】?

实际上,shared_ptr直接持有的指针就是我们通过 get( ) 方法返回的指针;而控制块所持有的指针,是指向我们所管理的对象的。通常情况下,两者是一致的,但也有特殊情况。这不是胡诌,在cppreference中提到:

“The pointer held by the shared_ptr directly is the one returned by get(), while the pointer or object held by the control block is the one that will be deleted when the number of shared owners reaches zero. These pointers are not necessarily equal.”

于是我有两个疑问:

  1. 为什么一个shared_ptr需要两个指针,一个由它自己直接持有,另一个由控制块持有?
  2. 为什么这两个指针会不相等

怀着疑问,我在shared_ptr的构造函数中找到了答案:

template< class Y >
shared_ptr( const shared_ptr& r, T *ptr );

这是在shared_ptr构造函数中出现的,我们可以看见第一个参数为管理对象类型为Y 智能指针,第二个参数指向T类型的指针。

这个构造函数的设计是为了,让我们可以有一个shared_ptr指向它所拥有其他东西

看似有些拗口,我们从例子来看:

#include
#include

using std::cout;
using std::endl;

std::shared_ptr creator2()
{
    using Pair = std::pair;

    std::shared_ptr p(new Pair(3.14, 42));
    std::shared_ptr q(p, &(p->second));
    cout << "q.get() " << q.get() << endl; 
    cout << "p.get() " << p.get() << endl; 
    cout << "q owns  value in " << &(p->second) << endl;
    return q;
}

int main() {
    // shared_ptr ptr1 = creator1();
    shared_ptr ptr2 = creator2();
    cout << *ptr2 << endl;
    return 0;
}

这是一个有些“矫揉造作”的例子,但是很清晰。在 creator2 中,我们有一个类型为 pair的对象,并且有一个shared指针 p 管理它

正常情况下,一旦函数结束,这个pair对象就会被释放,但是由于q和p共享它的所有权,所以指针q保持了pair对象的活性。实际上,我们采用这种方式构造的目的是:我们想让shared指针q指向它【所拥有的】(pair对象)的【其他东西】(int部分)那么当shared指针 q 析构后,它所拥有的 【pair对象】也必须被释放,那么【指向pair对象的指针】应该被【传递给删除器】,也就是说我们需要把它存储在控制块中!

这回答了第一个疑问 “为什么一个shared_ptr需要两个指针,一个由它自己直接持有,另一个由控制块持有?”,实际上,第二个疑问 “为什么这两个指针会不相等” 的答案也呼之欲出了!

上述程序的输出结果我贴在下方:

​
// 输出!
q.get() 0x623f88
p.get() 0x623f80
q owns  value in 0x623f88
42

​

这印证了 “有些时候,shared_ptr【直接持有】的指针 和 【控制块持有】的指针不同 ”。

总结

总得来说,shared_ptr 【调用get方法】返回的指针就是它【直接持有】的指针;

而当引用计数为0时,真正【传递给删除器】的指针是【控制块持有】的指针!

最后,找到的一些关于上述所提到构造函数的历史渊源。(英翻中未必准确)

Peter Dimov、Beman Dawes和Greg Colvin通过第一份库技术报告(称为TR1)提议将shared_ptr和weak_ptr纳入标准库。该建议被接受,并最终在2011年的迭代中成为C++标准的一部分。

在这个提案中,作者指出了 "共享指针异化 "的用法。

高级用户经常要求能够创建一个shared_ptr实例q,与另一个(主)shared_ptr p共享所有权,但指向一个不是【*p】的基类对象。例如,p 可能是 q 的【一个成员或一个元素】。本节提出了一个额外的构造函数,可以用于这个目的。

所以他们在控制块中增加了一个额外的指针。

你可能感兴趣的:(c++,开发语言)