智能指针¶
智能指针定义在头文件 <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;
};
1.5 示例¶
1.5.1 shared_ptr 几种赋值方式¶
C++ 中 std::shared_ptr
提供了多种赋值方式,用来改变智能指针所指向的对象或者共享对象所有权。以下是几种常见的 shared_ptr
赋值操作:
-
普通赋值(copy assignment):
std::shared_ptr<T> ptr1(new T); std::shared_ptr<T> ptr2; ptr2 = ptr1; // 此时ptr1和ptr2共享同一个对象的所有权,引用计数加1
-
移动赋值(move assignment)(C++11 开始支持):
std::shared_ptr<T> ptr1(new T); std::shared_ptr<T> ptr2; ptr2 = std::move(ptr1); // 移动所有权,ptr1不再拥有资源,ptr2接管,ptr1变为空指针
-
拷贝构造函数赋值:
std::shared_ptr<T> ptr1(new T); std::shared_ptr<T> ptr2(ptr1); // 通过拷贝构造函数创建一个新的shared_ptr,ptr1和ptr2共享同一个对象
-
模板赋值运算符:
std::shared_ptr<Derived> derivedPtr(new Derived); std::shared_ptr<Base> basePtr = derivedPtr; // 如果Derived是Base的派生类,则可以将derivedPtr赋给basePtr
-
reset () 函数间接赋值:
std::shared_ptr<T> ptr; ptr.reset(new T); // 使用reset()分配一个新对象,如果ptr之前已有对象,则会释放旧对象并重新分配
-
swap () 函数交换两个 shared_ptr:
std::shared_ptr<T> ptr1(new T); std::shared_ptr<T> ptr2(new T); using std::swap; swap(ptr1, ptr2); // 交换两个 shared_ptr 指向的对象,不会增加引用计数
每种赋值操作都会维护内部的引用计数器,并在适当的时候自动管理内存,确保当所有指向同一对象的 shared_ptr
都失效时,该对象会被正确地销毁。同时,在多线程环境中,引用计数的操作通常是原子性的以保证线程安全。
1.5.2 使用 make_shared 和使用 shared_ptr 构造初始化有什么不同¶
std::make_shared<T>()
和直接使用 std::shared_ptr<T>
构造函数初始化智能指针在内存管理、性能和安全性上存在显著的不同:
- 内存分配效率:
- make_shared: 当使用
std::make_shared<T>(args...)
时,它会一次性为对象及其控制块(存储引用计数和其他元数据)分配连续的内存空间。这意味着只需要一次内存分配操作,从而提高了效率,减少了潜在的碎片,并且由于共享同一内存区域,访问控制块的速度也会更快。 -
shared_ptr 构造函数: 直接通过
new T(args...)
创建对象并传给std::shared_ptr<T>(new T(args...))
构造函数时,会分别对对象和控制块进行两次独立的内存分配。这通常会导致额外的开销。 -
异常安全性:
- make_shared: 如果
T
的构造过程中抛出了异常,那么通过make_shared
分配的内存会被正确地释放,不会导致资源泄露。 -
shared_ptr 构造函数: 当你先创建裸指针再将其传递给
shared_ptr
构造函数时,在两个步骤之间若发生异常,则可能导致对象已经构造但对应的shared_ptr
没有成功创建,进而引发资源泄露问题。 -
内存布局优化:
- make_shared: 内存布局更紧凑,有可能提升缓存局部性,从而提高程序性能。
-
shared_ptr 构造函数: 对象和控制块在内存中可能不相邻,对性能可能稍有影响。
-
代码简洁性与可读性:
- make_shared: 代码更加简洁,不需要显式删除原始指针。
- shared_ptr 构造函数: 需要显示地进行 new 操作以及处理可能出现的异常安全问题。
总结来说,std::make_shared<T>()
是推荐的实践方式,因为它提供了更好的性能、更高的内存利用率和更强的异常安全性。除非特定场景下无法使用 make_shared
或者需要更多灵活性,一般情况下都应优先考虑使用 make_shared
。
2 weak_ptr¶
- 特点:
- 用于解决
std::shared_ptr
的循环引用问题。 - 它只能接受
weak_ptr
或shared_ptr
,使用时需要先转成shared_ptr
再通过shared_ptr
去访问保存的指针对象 - 它是一种弱引用,没有引用计数,不负责管理内存,它的存在与否不影响原指针对象的释放
- 示例
#include <iostream> #include <memory> int main() { // 创建一个 std::shared_ptr std::shared_ptr<int> sharedPtr = std::make_shared<int>(42); // 从 std::shared_ptr 创建一个 std::weak_ptr std::weak_ptr<int> weakPtr = sharedPtr; // 检查 weak_ptr 是否仍然有效 if (std::shared_ptr<int> lockedPtr = weakPtr.lock()) { std::cout << "Value: " << *lockedPtr << std::endl; } else { std::cout << "Object has been deleted." << std::endl; } // 释放 sharedPtr,weakPtr 将失效 sharedPtr.reset(); // 再次检查 weak_ptr 是否仍然有效 if (std::shared_ptr<int> lockedPtr = weakPtr.lock()) { std::cout << "Value: " << *lockedPtr << std::endl; } else { std::cout << "Object has been deleted." << std::endl; } return 0; }
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));