shared_ptr
定义
一种 共享对象所有权 的智能指针,允许多个shared_ptr拥有相同的对象。
对象销毁时机:(引用计数归零)
1. 最后一个拥有该对象的shared_ptr 被销毁
2. 最后一个有用该对象的shared_ptr 转移对象所有权(通过operator= 或 reset()方法 )
对象的销毁方法:
1. delete-expression(包括delete ,delete[] )
2. 构造shared_ptr时 自定义的删除器
允许为空:
shared_ptr 允许为空,不拥有任何manage对象 。
转移所有权:
对象的所有权只能通过 copy构造和 copy 赋值来共享。 而使用 shared_ptr 的原始指针 来构造新的shared_ptr 会导致 未定义行为,不允许隐式构造。
具体实现:
以 shared_ptr< string > p = make_shared< string >(10,‘9’) 为例 。
在实现shared_ptr的经典方法中, 只有两个指针:
1. 指向数据内存空间的原始指针(内存中"9999999999"所保存的位置,可通过get()方法获取 地址),
2. 指向控制块的指针 。 控制块是一个动态分配的对象,维护在堆上,包含:1.指向manage对象的指针(指向string 对象) , 2.删除器 3.分配器 4.指向同一个manage对象的shared_ptr数量 5. 指向同一个manage对象的weak_ptr数量。
shared_ptr本身的线程安全性:
-
多线程中不同shared_ptr执行 copy构造和copy赋值等函数时 ,不需要额外的同步机制 。
-
如果其中一个访问 非const 成员函数,则会发生数据竞争。 shared_ptr重载了原子函数 来防止数据竞争。
1. 引用计数增减 线程安全
为了满足线程安全要求,引用计数器通常使用std::atomic::fetch_add和std::memory_order_relax的等原子操作 更新堆上的 控制块信息 。
2. 修改指向时
2.1 多线程操作同一个shared_ptr对象 - 不安全
如thread 执行func(shared_ptr< A > &sp): 其中有 sp = other_sp ;
一个线程中要修改shared_ptr 的指向, sp原来指向的引用计数需要减1 , other_sp指向的引用计数需要加1。即多个线程都在修改sp时可能出现问题,这些操作并不是原子操作。例如: 在引用计数在减1的时候,其内部的指向已经被其他线程修改了。引用计数的异常会导致某些托管对象被提前析构,后续在使用到该数据的时候触发core dump。
2.2 多线程操作不同shared_ptr对象 - 安全
如thread 执行func(shared_ptr< A > sp): 其中有 sp = other_sp ; 这里传值涉及到拷贝构造,已不是同一个shared_ptr对象。
这时候每个线程中的sp,管理的是同一份数据,使用同一个引用计数。当发生多线程修改sp指向时,原子操作的引用计数增减,保证了导致线程安全。
参考1:https://zhuanlan.zhihu.com/p/416289479
参考2:https://en.cppreference.com/w/cpp/memory/shared_ptr