智能指针其实就是资源管理权限的转移,自己不想手动释放,交给一个对象管理,对象什么时候被销毁,这块资源也就什么时候被释放。unique_ptr 、shared_ptr 和weak_ptr 之间的区别如下:
- unique_ptr:字面意思是独一无二的指针。这种智能指针只允许有一个管理者,其底层做了禁止拷贝的相关工作。
- shared_ptr:字面意思是共享的指针。这种智能指针允许多个管理者,底层通过引用计数实现
- weak_ptr:严格来说不是指针,一般出现在一些特殊场景,用于解决shared_ptr 循环拷贝的问题。
目录
1、unique_ptr
(1) 成员变量:_ptr、_refCount
(2) 构造函数:初始化引用计数
(3) 拷贝构造:引用计数自增
(4) 析构函数:引用计数自减
(5) 完整代码以及改进方向
1、unique_ptr
unique_ptr 只允许有一个管理者,既然管理权限交到了自己手里,就不允许其他智能指针插手。其核心在于防拷贝机制。
实现起来也比较简单,如果只是防止类外拷贝,我们只需要将拷贝构造函数放到 private修饰的作用域内即可;但是如果要同时防止类外和类内拷贝,我们可以使用delete关键字,具体使用如下:
template<class T>
class UniquePtr
{
public:
UniquePtr(T* ptr)
:_ptr(ptr) // 获取资源
{}
~UniquePtr() {
if (_ptr)
{
delete _ptr; // 释放资源
_ptr = nullptr;
}
}
UniquePtr(const UniquePtr<T>& up) = delete; // 禁止拷贝构造
UniquePtr<T>& operator=(const UniquePtr<T>& up) = delete; // 禁止赋值构造
private:
T* _ptr;
};
2、shared_ptr
shared_ptr 允许有多个管理者来同时管理这份资源。实现时为了避免资源被重复释放,实际采用的是引用计数来达到资源共享,每次析构只是让引用计数自减,当引用计数自减为 0 的时候,再释放资源.
(1) 成员变量:_ptr、_refCount
需要注意的是,_refCount 的类型是int*,因为如果存在多个管理者,不光是资源要共享,引用计数也要共享。如果_refCount的类型是int,那么不同对象会各自持一份_refCount的拷贝版本,计数时无法同步。
template<class T>
class SharedPtr
{
private:
T* _ptr; // 保存资源的地址
int* _refCount; // 引用计数
};
(2) 构造函数:初始化引用计数
除了获取资源的地址外,我们要将引用计数初始化为1,因为最初的资源管理者产生了。
SharedPtr(T* ptr)
:_ptr(ptr),
_refCount(new int(1))
{}
(3) 拷贝构造:引用计数自增
每发生一次拷贝构造,说明有新的管理者要参与资源管理,此时引用计数自增
SharedPtr(const SharedPtr<T>& sp)
:_ptr(sp._ptr),
_refCount(sp._refCount)
{
(*_refCount) ++;
}
(4) 析构函数:引用计数自减
每有一个管理者被销毁,引用计数就要自减;一旦引用计数为0,说明管理者不存在了,为了防止内存泄漏,此时需要释放资源。
~SharedPtr() {
if (--(*_refCount)==0 && _ptr)
{
delete _ptr;
_ptr = nullptr;
}
}
(5) 完整代码以及改进方向
这段代码仅用于了解 shared_ptr 的基本逻辑,实际上,如果要继续优化,引用计数是临界资源,引用计数自增或自减时需要加锁控制。
template<class T>
class SharedPtr
{
public:
SharedPtr(T* ptr)
:_ptr(ptr),
_refCount(new int(1))
{
cout << "引用计数: " << *_refCount << endl;
}
SharedPtr(const SharedPtr<T>& sp)
:_ptr(sp._ptr),
_refCount(sp._refCount)
{
(*_refCount) ++;
cout << "引用计数: " << *_refCount << endl;
}
~SharedPtr() {
if (--(*_refCount)==0 && _ptr)
{
delete _ptr;
_ptr = nullptr;
}
}
private:
T* _ptr;
int* _refCount;
};
简单测试的结果如下: