目录
1. 什么是RAII?
RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内
存、文件句柄、网络连接、互斥量等等)的简单技术。
在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在
对象析构的时候释放资源。借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做
法有两大好处:
- 不需要显式地释放资源。
- 采用这种方式,对象所需的资源在其生命期内始终保持有效
// 利用RAII思想写出的智能指针模板
template <class T>
class SmartPtr
{
SmartPtr(T* ptr)
:_ptr(ptr);
{}
~SmartPtr()
{
delete _ptr;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
T* _ptr;
};
2. auto_ptr
在C++98中就提供了一种类似于智能指针的指针。
auto_ptr的原理是:将管理权转移的思想
具体实现如下:
#pragma once
namespace bit
{
template<class T>
class auto_ptr
{
//构造函数
auto_ptr(T* ptr)
:_ptr(ptr)
{}
//转移资源
auto_ptr(auto_ptr<T>& ap)
:_ptr(ap._ptr)
{
ap._ptr = nullptr;
}
//赋值
auto_ptr<T>& operator=(auto_ptr<T>& ap)
{
if (this != &ap)
{
if (_ptr)
{
delete _ptr;
}
_ptr = ap._ptr;
ap._ptr = nullptr;
}
return *this;
}
//析构函数
~auto_ptr()
{
if (_ptr)
{
delete _ptr;
}
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
T* _ptr;
};
}
这样有一种缺陷就是赋值的指针将会变成空指针,这种效果也遭受了很多学习C++语法人的吐槽,以至于很多公司禁止使用该指针。
这种“智能指针”不能满足我们的要求,因此在C++11出现之前,广泛的C++使用者就聚齐在一起,形成了一个社区:boost
3. boost
什么是boost?

而我们的智能指针就出自于此,较为著名的智能指针包括:scoped_ptr 、shared_ptr 、week_ptr
而C++出台的unique_ptr、shared_ptr、week_ptr都出自于此。
4. unique_ptr
什么是unique_ptr?
通过名字我们可以发现它被称为独一无二的指针,也就是说明它是无法被复制的,而它的底层也确实如此。

通过它的拷贝构造,我们可以发现它没有对象构造和赋值构造,这也就保证了该指针指向的空间只有它一个指向。
那么我们来观察它的底层是如何实现的:
using namespace std;
namespace bit
{
template<class T>
struct default_delete
{
void operator()(T* ptr)
{
delete ptr;
ptr = nullptr;
}
};
template<class T,class D = default_delete<T>>
class unique_ptr
{
public:
//构造函数
unique_ptr(T* ptr)
:_ptr(ptr)
{}
unique_ptr(unique_ptr<T>& ap) = delete;
//赋值
unique_ptr<T>& operator=(unique_ptr<T>& ap) = delete;
//析构函数
~unique_ptr()
{
if (_ptr)
{
cout << "delete ptr" << endl;
D d;
d(_ptr);
}
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
T* _ptr;
};
}
在它的底层中将其中两个构造函数加上了delete,在C++11中新增加了delete和default关键字,如果一个函数后加上delete,表示无法默认生成。那么就表明如果不显式写这两个构造函数,那么就能够实现unique这个特点。
至于这里还存在一个叫D的类型,它表示我们存储的指针以什么形式释放
这里我们就称为定制删除器
5. 定制删除器
如果我们传入的值是通过new实现出来的,那么我们在释放的时候就需要通过delete来进行释放
如果是new [],那么我们就需要delete[]来进行释放,所有我们的关键点就是如何在析构的时候释放空间,这里我们就涉及到了定制删除器。
定制删除器顾名思义就是对不同的步骤定制了不同的解决方案,
比如如果传入的值是通过new来实现的,那么就需要使用delete
如果是通过malloc来实现的,那么就需要使用free
![]()
比如unique_ptr,我们就可以发现它不仅仅是传入一个T参数,还需要传入一个D参数,这个D参数就是我们的定制删除器,就可以在析构中调用D所形成的对象来释放我们的指针。
6. shared_ptr
有一种情况:多个指针管理同一块区域,那么我们这块区间是什么时候进行释放?
这里我们就用到了shared_ptr,那么shared_ptr是如何来实现区间的释放呢?
其实是采用了计数的原理,就好比老师让最后一个离开教室的把们关上一样,如果最后只剩下一个指针管理这块区域,那么当对象销毁时,这块空间也随之释放。
shared_ptr的特点:
1. 通过计数来实现多个shared_ptr对象管理同一块资源
2. 当对象被销毁时,将每个资源所指向的计数-1,
3. 如果当前资源的计数为0,则将其释放,如果不为0,则说明还有指针指向当前资源
那么它的基本实现是怎样呢?
#pragma once
namespace bit
{
template<class T>
class shared_ptr
{
void Release()
{
if (--(*_count) && _ptr)
{
delete _ptr;
_ptr = nullptr;
delete _count;
_count = nullptr;
}
}
//构造函数
shared_ptr(T* ptr)
:_ptr(ptr),
_count(new int(1))
{}
//转移资源
shared_ptr(const shared_ptr<T>& sp)
:_ptr(sp._ptr),
_count(sp._count)
{
++(*_count);
}
//赋值
shared_ptr<T>& operator=(const shared_ptr<T>& sp)
{
if (_ptr != sp._ptr)
{
Release();
_ptr = sp._ptr;
_count = sp._count;
*(_count)++;
}
return *this;
}
//析构函数
~shared_ptr()
{
Release();
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
T* get()
{
return _ptr;
}
private:
T* _ptr;
int* _count;
};
}
这里最为关键的就是Release函数,通过该函数我们可以得知是否所指向的资源要释放,这也是shared_ptr的灵魂所在。
7. week_ptr
那么shared_ptr就是完美了吗?
在不整花活的情况下,使用shared_ptr是完全足够的,但是总会有特殊情况

如果我们要实现这样的情况,会发生什么呢?

我们发现在程序终止时,没有打印出析构函数里面的字符串,因为我们可以发现没有当p1、p2出了作用域后,没有进行销毁,产生了内存泄漏。
那么这是怎么发生了呢?

那么当p1、p2释放后,计数值还是1,_next的释放取决于_prev释放,而_prev的释放又取决于_next,那么这样相互依赖的关系被称为循环引用。
那么我们是如何来避免进行循环引用的呢?,这里C++11就提出了一个解决方案:引入了一个新指针-- week_ptr,由于我们的计数的由于_next和_prev指向了空间,导致我们的计数+1,又因为我们想让_next和_prev指向空间,因为我们可以当它指向空间时。当前空间不计数,这也是week_ptr的原理,也就是小弟帮大哥办事。
namespace bit
{
template <class T>
class week_ptr
{
public:
week_ptr(const T* ptr)
:_ptr(ptr)
{}
week_ptr(const week_ptr<T>& wp)
:_ptr(wp._ptr)
{}
week_ptr(const shared_ptr<T>& sp)
:_ptr(sp.get())
{}
void operator = (const shared_ptr<T>& sp)
{
_ptr = sp.get();
}
private:
T* _ptr;
};
}
只干事没有实权,这就是小弟应该做的事!!!
本文介绍了C++中的智能指针,包括RAII原则,详细讲解了auto_ptr的缺陷,以及boost库中的智能指针。接着,文章阐述了unique_ptr的不可复制特性及其定制删除器的使用,shared_ptr的计数机制和防止内存泄漏的设计,最后讨论了weak_ptr解决循环引用问题的重要性。
852

被折叠的 条评论
为什么被折叠?



