什么是智能指针
- 正如你我都时刻担心的一件事情,我们的编程过程中,免不了要使用指针指向一块空间,那么就可能因为粗心大意而造成“内存泄露”的问题
- 当然,你肯定会呓呓自语:“我肯定没那么蠢”,然后默默地一直仔细的把关防止出现内存管理错误,但是忽然有一天,错误依旧发生了,好懊恼
- 那么与其自己一直的关系,可不可以有一种托管思路的工具诞生呢,有,就是“智能指针”
- 智能指针,是一种利用RAII思想的设计类,提供类似指针的用途,通过类对象的构造与析构,对自己管理的资源进行合理的释放与维护(依托于这个类对象的生命周期)
RAII思想
RAII,全称“Resource Acquisition Is Initialization”,是一种利用对象的生命周期来进行控制程序资源(如 内存, 句柄, 网络连接, 互斥量等等)的简单技术
智能指针的好处
在对象构造的时候获取资源,在对象生命周期内对 所控制资源的访问 保持有效,最后在对象析构时释放所保持的资源
因此,如果使用智能指针,我们可以不再需要显示的释放资源,这会带来很大的便捷智能指针的实现
简单介绍智能指针的发展
目前常见的智能指针三种
智能指针 | auto_ptr | unique_ptr | shared_ptr |
---|---|---|---|
优点 | 初步实现了指针的功能 | 初步实现了指针的功能,避免了auto_ptr拷贝与赋值的问题 | 较为成熟的一种智能指针,采用了“引用计数”的方法,根本上解决前面俩个拷贝与赋值的问题 |
缺点 | 原理是管理权转移,在拷贝与赋值行为中,造成赋值者失效 | 由于实现的原理,无法满足拷贝构造和赋值 | 计数回绕的问题 |
模拟实现auto_ptr
“`c++
template<class T> // 模板
class Auto_ptr
{
// 基本不被使用,存在指针转移的问题
public:
//构造
Auto_ptr(T ptr = nullptr)
:m_ptr(ptr)
{}
//析构
~Auto_ptr()
{
if (m_ptr) delete m_ptr;
}
// 解引用 返回ptr
T& operator() { return m_ptr; } //为什么拿引用来接受呢
// 指向 返回指针
T* operator->() { return m_ptr; }
// 指针的拷贝构造
Auto_ptr(Auto_ptr<T>& sp)
:m_ptr(sp.m_ptr)
{
sp.m_ptr = nullptr;
}
//赋值运算符
Auto_ptr<T>& operator=(Auto_ptr<T>& sp) //参数为引用类型, 返回为引用类型,为什么
{
//首先判断是否为本身
if (sp == *this) return;
//清空现有资源
if (m_ptr) delete m_ptr;
//转移指针指向,避免多指向引发野指针问题,sp.m_ptr = nulptr;
m_ptr = sp.m_ptr;
sp.m_ptr = nullptr;
}
private:
T* m_ptr;
};
### 模拟实现unique_ptr
```c++
template<class T>
class Unique_ptr
{
//避免转移,那就意味着不需要去考虑拷贝和赋值
public:
//构造
Unique_ptr(T* ptr = nullptr)
:m_ptr(ptr)
{}
//析构
~Unique_ptr()
{
if (m_ptr) delete m_ptr;
}
//*
T& operator*() { return *m_ptr; }
//->
T* operator->() { return m_ptr; }
private:
Unique_ptr(Unique_ptr<T>& Up) = delete; // or only decarler but not make it;
Unique_ptr<T>& operator=(Unique_ptr<T>& Up) = delete;
private:
T* m_ptr;
};
模拟实现shared_ptr
说到shared_ptr,一定需要考虑到,在它的实现原理中,因为存在“引用计数”,所以为了基本的线程安全,必须对“计数过程的++ –保证线程安全”,同时也保证析构时“计数为0采去彻底释放”
-
“`c++
template<class T>
class Shared_ptr
{
public:
//构造
Shared_ptr(T ptr = nullptr)
:m_ptr(ptr)
, pRefCount(new int(1)) // 构造本身默认count 为1;
, pMutex(new mutex)
{}
//析构 由于析构比较复杂,可以内置一个私有函数Release()进行析构
~Shared_ptr()
{
Release();
}
//
T& operator() { return m_ptr; }
//->
T* operator->() {
return m_ptr;
}//线程安全的计数+1 AddRefCount 私有
//拷贝
Shared_ptr(Shared_ptr<T>& Sp)
:m_ptr(Sp.m_ptr)
,pRefCount(Sp.pRefCount)
, pMutex(Sp.pMutex)
{
AddRefCount();
}
//赋值
Shared_ptr<T> operator=(Shared_ptr<T>& sp)
{
if (sp != *this)
{
//情况自己本身的属性
Release();
//赋值原属性, 再对资源计数+1
m_ptr = sp.m_ptr;
pRefCount = sp.pRefCount;
pMutex = sp.pMutex;AddRefCount(); }
}
private:
//+ – 计数都必须考虑线程安全问题
void Release()
{
//when recountf == 0, delete ref
bool isRefNull = false;
pMutex->lock();
if (–(pRefCount) == 0)
{
delete m_ptr;
delete pRefCount;
//现在当然还不可以删除锁资源,小脑袋瓜想什么着
isRefNull = true;
}
pMutex->unlock();
if (isRefNull) delete pMutex;
}
void AddRefCount()
{
//保证计数线程安全
pMutex->lock();
++(pRefCount);
pMutex->unlock();
}
private:
T m_ptr;
mutex pMutex;
int* pRefCount; //为什么作为成员变量呢, 这样应用计数的值就是对象的一个属性,每个对象都独立的拥有一份
};
#### 循环引用的问题,它的破解之刃——weak_ptr
* 当我们把话题引导到“引用计数”的问题上时,就避免不开的讨论“计数回绕”的问题了,简单来说:“计数回绕是一种采用引用计数,因为某种联系造成彼此引用(循环引用),恰好,双方shared_ptr管理资源的释放均依托于对方,造成“锁”状态,从而资源释放不了,造成内存泄露”
```C++
struct ListNode
{
int _data;
shared_ptr<ListNode> _prev;
shared_ptr<ListNode> _next;
~ListNode(){ cout << "~ListNode()" << endl; }
};
int main()
{
shared_ptr<ListNode> node1(new ListNode);
shared_ptr<ListNode> node2(new ListNode);
cout << node1.use_count() << endl;
cout << node2.use_count() << endl;
node1->_next = node2;
node2->_prev = node1;
cout << node1.use_count() << endl;
cout << node2.use_count() << endl;
return 0;
}
段落引用
解决循环引用问题的weak_ptr
“`C++
struct ListNode
{
int _data;
weak_ptr<ListNode> _prev;// 对比上面就把这里改为weak_ptr即可
weak_ptr<ListNode> _next; // 如上所示, 使用weak_ptr对存在循环引用,回绕的计数不进行+1;
~ListNode(){ cout << "~ListNode()" << endl; }
};
int main()
{
shared_ptr<ListNode> node1(new ListNode);
shared_ptr<ListNode> node2(new ListNode);
cout << node1.use_count() << endl;
cout << node2.use_count() << endl;
node1->_next = node2;
node2->_prev = node1;
cout << node1.use_count() << endl;
cout << node2.use_count() << endl;
return 0; }
原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/142732.html