智能指针¶
智能指针定义在头文件<memory>
中。
1 shared_ptr¶
- 实现原理分析:
- shared_ptr 内部保存一个对象指针和一个引用计数类
_Ref_count
指针,通过_Ref_count
继承自_Ref_count_base
中引用计数变量来判断是否需要对保存的指针进行 delete,当引用计数变量==0
时,_Ref_count
会对保存的对象,以及_Ref_count
自身资源进行释放 - 引用计数类主要2个功能:管理内存、引用计数
- 所有对shared_ptr对象的拷贝赋值,都会触发引用计数+1。
- shares_Ptr对象的析构会触发引用计数-1。
1.1 类shared_ptr分析¶
shared_ptr
公有继承自_Ptr_base
,在shared_ptr
类中主要实现构造,赋值等操作,实际引用计数,对象指针数据都在_Ptr_base
中实现
- shared_ptr
部分源码
class shared_ptr : public _Ptr_base<_Ty> { // class for reference counted resource management
private:
using _Mybase = _Ptr_base<_Ty>;
public:
using typename _Mybase::element_type;
//其中一个构造函数
template <class _Ux,
enable_if_t<conjunction_v<conditional_t<is_array_v<_Ty>, _Can_array_delete<_Ux>, _Can_scalar_delete<_Ux>>,
_SP_convertible<_Ux, _Ty>>,
int> = 0>
explicit shared_ptr(_Ux* _Px) { // construct shared_ptr object that owns _Px
#if _HAS_IF_CONSTEXPR
if constexpr (is_array_v<_Ty>) {
_Setpd(_Px, default_delete<_Ux[]>{});
} else {
//_Owner临时保存_Px指针
_Temporary_owner<_Ux> _Owner(_Px);
//_Set_ptr_rep_and_enable_shared函数用来给_Ptr_base中的_Ptr和_Rep赋值,Ref_count中也保存_Px指针
_Set_ptr_rep_and_enable_shared(_Owner._Ptr, new _Ref_count<_Ux>(_Owner._Ptr));
_Owner._Ptr = nullptr;
}
//声明使用_Ptr_base中保护函数get,并将它转出公有函数get
using _Mybase::get;
//析构会调用引用计数-1,当引用计数==0时,会释放资源
~shared_ptr() noexcept { // release resource
this->_Decref();
}
}
1.2 类_Ptr_base分析¶
_Ptr_base
中有2个重要的指针成员变量,一个是保存的数据对象的指针,另一个控制类型指针_Ref_count_base
,用来实现引用计数及删除保存对象指针所占内存功能
- _Ptr_base
部分源码
template <class _Ty>
class _Ptr_base { // base class for shared_ptr and weak_ptr
public:
//如果是数组型,将会移除[]
using element_type = remove_extent_t<_Ty>;
//获取引用计数数
_NODISCARD long use_count() const noexcept {
return _Rep ? _Rep->_Use_count() : 0;
}
protected:
//返回保存的对象的指针
_NODISCARD element_type* get() const noexcept {
return _Ptr;
}
constexpr _Ptr_base() noexcept = default;
~_Ptr_base() = default;
//移动构造会用到的函数,只交换数据,引用计数并不会增加或减少,_Other不再使用
template <class _Ty2>
void _Move_construct_from(_Ptr_base<_Ty2>&& _Right) noexcept {
// implement shared_ptr's (converting) move ctor and weak_ptr's move ctor
_Ptr = _Right._Ptr;
_Rep = _Right._Rep;
_Right._Ptr = nullptr;
_Right._Rep = nullptr;
}
//拷贝构造会使用的函数,引用计数+1
template <class _Ty2>
void _Copy_construct_from(const shared_ptr<_Ty2>& _Other) noexcept {
// implement shared_ptr's (converting) copy ctor
_Other._Incref();
_Ptr = _Other._Ptr;
_Rep = _Other._Rep;
}
//使用新对象指针替换原指针,引用计数+1
template <class _Ty2>
void _Alias_construct_from(const shared_ptr<_Ty2>& _Other, element_type* _Px) noexcept {
// implement shared_ptr's aliasing ctor
_Other._Incref();
_Ptr = _Px;
_Rep = _Other._Rep;
}
//使用新对象指针替换原指针,移动构造引用计数不变,_Other不再使用
template <class _Ty2>
void _Alias_move_construct_from(shared_ptr<_Ty2>&& _Other, element_type* _Px) noexcept {
// implement shared_ptr's aliasing move ctor
_Ptr = _Px;
_Rep = _Other._Rep;
_Other._Ptr = nullptr;
_Other._Rep = nullptr;
}
template <class _Ty0>
friend class weak_ptr; // specifically, weak_ptr::lock()
template <class _Ty2>
bool _Construct_from_weak(const weak_ptr<_Ty2>& _Other) noexcept {
// implement shared_ptr's ctor from weak_ptr, and weak_ptr::lock()
if (_Other._Rep && _Other._Rep->_Incref_nz()) {
_Ptr = _Other._Ptr;
_Rep = _Other._Rep;
return true;
}
return false;
}
//引用计数+1
void _Incref() const noexcept {
if (_Rep) {
_Rep->_Incref();
}
}
//引用计数-1
void _Decref() noexcept { // decrement reference count
if (_Rep) {
_Rep->_Decref();
}
}
private:
//保存对象的指针
element_type* _Ptr{nullptr};
//用来控制引用计数及删除对象功能
_Ref_count_base* _Rep{nullptr};
#if _HAS_STATIC_RTTI
//用来获取删除器函数指针
template <class _Dx, class _Ty0>
friend _Dx* get_deleter(const shared_ptr<_Ty0>& _Sx) noexcept;
#endif // _HAS_STATIC_RTTI
};
1.3 类_Ref_count_base
分析¶
引用计数类_Ref_count_base
是一个抽象类,定义了一些基础方法,主要处理引用计数+-,以及何时delete保存的指针。它有一个子类_Ref_count
,在智能指针中使用的是这个子类_Ref_count
- _Ref_count_base
部分源码
// CLASS _Ref_count_base
class __declspec(novtable) _Ref_count_base { // common code for reference counting
private:
#ifdef _M_CEE_PURE
// permanent workaround to avoid mentioning _purecall in msvcurt.lib, ptrustu.lib, or other support libs
virtual void _Destroy() noexcept {
_STD terminate();
}
virtual void _Delete_this() noexcept {
_STD terminate();
}
#else // ^^^ _M_CEE_PURE / !_M_CEE_PURE vvv
virtual void _Destroy() noexcept = 0; // destroy managed resource
virtual void _Delete_this() noexcept = 0; // destroy self
#endif // _M_CEE_PURE
// 初始化引用计数值为1,这个值控制引用计数
_Atomic_counter_t _Uses = 1;
_Atomic_counter_t _Weaks = 1;
public:
// 引用计数+1,_MT_INCR(_Uses)是微软实现的具有原子操作的+1
void _Incref() noexcept { // increment use count
_MT_INCR(_Uses);
}
void _Incwref() noexcept { // increment weak reference count
_MT_INCR(_Weaks);
}
// 引用计数-1 当引用计数==0时会执行一系列delete操作,删除保存的指针对象以及自身
void _Decref() noexcept { // decrement use count
if (_MT_DECR(_Uses) == 0) {
_Destroy();
_Decwref();
}
}
void _Decwref() noexcept { // decrement weak reference count
if (_MT_DECR(_Weaks) == 0) {
_Delete_this();
}
}
//返回引用计数值
long _Use_count() const noexcept {
return static_cast<long>(_Uses);
}
};
1.4 类_Ref_count
分析¶
_Ref_count
是_Ref_count_base
的子类,它的作用是实现资源释放的具体操作。
- _Ref_count
部分源码
// CLASS TEMPLATE _Ref_count
template <class _Ty>
class _Ref_count : public _Ref_count_base { // handle reference counting for pointer without deleter
public:
//用待保存的对象构造实例对象
explicit _Ref_count(_Ty* _Px) : _Ref_count_base(), _Ptr(_Px) {}
private:
//删除保存的对象
virtual void _Destroy() noexcept override { // destroy managed resource
delete _Ptr;
}
//删除自身
virtual void _Delete_this() noexcept override { // destroy self
delete this;
}
//智能指针保存的对象指针,与_Ptr_base中的对象指针是同一个
_Ty* _Ptr;
};
2 weak_ptr¶
- 特点:
- 它只能接受
weak_ptr
或shared_ptr
,使用时需要先转成shared_ptr
再通过shared_ptr
去访问保存的指针对象 - 它是一种弱引用,没有引用计数,不负责管理内存,它的存在与否不影响原指针对象的释放
2.1 类weak_ptr
分析¶
weak_ptr
中有3个重要的函数,用来管理weak_ptr
,其中lock
函数用来获取shared_ptr
- weak_ptr
部分源码及分析
// CLASS TEMPLATE weak_ptr
template <class _Ty>
class weak_ptr : public _Ptr_base<_Ty> { // class for pointer to reference counted resource
public:
//这个会释放引用的shared_ptr
void reset() noexcept { // release resource, convert to null weak_ptr object
weak_ptr{}.swap(*this);
}
void swap(weak_ptr& _Other) noexcept {
this->_Swap(_Other);
}
//用来判断被管理对象是否被删除
_NODISCARD bool expired() const noexcept {
return this->use_count() == 0;
}
//用来获取引用的shared_ptr
_NODISCARD shared_ptr<_Ty> lock() const noexcept { // convert to shared_ptr
shared_ptr<_Ty> _Ret;
//通过weak_ptr获得shared_ptr,如果成功则返回引用的shared_ptr,否则得到的是空shared_ptr
(void) _Ret._Construct_from_weak(*this);
return _Ret;
}
};
2.2 类_Ptr_base
分析¶
_Ptr_base
中_Construct_from_weak
和_Construct_from_weak
这2个函数很重要,实现了shared_ptr
到weak_ptr
的转换
- _Ptr_base
部分源码
template <class _Ty>
class _Ptr_base { // base class for shared_ptr and weak_ptr
public:
using element_type = remove_extent_t<_Ty>;
protected:
_NODISCARD element_type* get() const noexcept {
return _Ptr;
}
constexpr _Ptr_base() noexcept = default;
~_Ptr_base() = default;
template <class _Ty0>
friend class weak_ptr; // specifically, weak_ptr::lock()
template <class _Ty2>
bool _Construct_from_weak(const weak_ptr<_Ty2>& _Other) noexcept {
// implement shared_ptr's ctor from weak_ptr, and weak_ptr::lock()
if (_Other._Rep && _Other._Rep->_Incref_nz()) {
_Ptr = _Other._Ptr;
_Rep = _Other._Rep;
return true;
}
return false;
}
//shared_ptr转weak_ptr,如果引用计数控制指针_Rep不为空,则表示shared_ptr保存着有效指针对象,指针对象和引用计数指针赋值,弱引用计数+1
template <class _Ty2>
void _Weakly_construct_from(const _Ptr_base<_Ty2>& _Other) noexcept { // implement weak_ptr's ctors
if (_Other._Rep) {
_Ptr = _Other._Ptr;
_Rep = _Other._Rep;
_Rep->_Incwref();
} else {
_STL_INTERNAL_CHECK(!_Ptr && !_Rep);
}
}
//弱引用计数+1
void _Incwref() const noexcept {
if (_Rep) {
_Rep->_Incwref();
}
}
//弱引用计数-1
void _Decwref() noexcept { // decrement weak reference count
if (_Rep) {
_Rep->_Decwref();
}
}
private:
element_type* _Ptr{nullptr};
_Ref_count_base* _Rep{nullptr};
};
3 unique_ptr¶
unique_ptr
独占指针,与shared_ptr
共享特性相反。uniqe_ptr
不能拷贝构造拷贝赋值,但支持移动构造和移动赋值。
title: uniqe_ptr怎么实现不能构造的?
`uniqe_ptr`类中将拷贝构造和移动赋值设置成delete状态,使赋值失效
```cpp
unique_ptr(const unique_ptr&) = delete;
unique_ptr& operator=(const unique_ptr&) = delete;
title: 怎么将一个uniqe_ptr赋给另一个uniqe_ptr?
因为`uniqe_ptr`支持移动语义,因此我们使用`move`来转移对象
```cpp
unique_ptr<A> uA1(new A);
//unique_ptr<A> uA2 = uA1; error
unique_ptr<A> uA2 = move(uA1);
//unique_ptr<A> uA3(uA2);error
unique_ptr<A> uA3(move(uA2));