一.什么是智能指针
所谓智能指针就是智能/自动化的管理指针所指向的动态资源的释放。实际上智能指针是一种抽象数据类型,行为表现的像一个指针。通过重载“->”和“*”,就可以像使用指针一样来使用智能指针;而智能指针又是如何来管理指针指向对象的释放问题呢?其实这是RAII的一种应用。RAII(Resource acquisition is initialization),即资源分配即初始化。定义一个类来封装资源的分配和释放,在构造函数完成资源的分配和初始化,在析构函数完成资源的清理,可以保证资源的正确初始化和释放 。那么有了指针我们为什么还需要智能指针呢?接下来看下边的例子。
#include <iostream>
#include <string>
using namespace std;
void Fun()
{
string* p1 = new string("hello");
bool IsEnd = true;
if (IsEnd)
{
throw 1;
delete p1;
}
delete p1;
}
int main()
{
try
{
Fun();
}
catch (int e)
{
cout << e << endl;
}
return 0;
}
这段代码看起来好像没什么问题,但其实隐藏了一个非常重要的错误--内存泄漏。但是从哪泄漏的呢?那就要从抛出异常说起了,在上述代码中,异常被抛出后,是在main函数中捕获的,但是Fun函数中的p1并没有被释放,所以造成内存泄漏。所以,这时候就需要智能指针,能够自动的帮我们释放空间。
二、智能指针的发展
智能指针的发展可以简单的分为三个阶段:
(1)auto_ptr c++98/03
(2)scoped_ptr/shared_ptr/week_ptr boost
(3)unique_ptr/shared_ptr/week_ptr c++11
auto_ptr 的本质是管理权的转移,带有缺陷的设计,一般不建议使用。scope_ptr/unique_ptr是防拷贝,功能不全。shared_ptr是通过引用计数来解决空间释放问题,功能强大,但是有时候会存在循环引用的问题,这时候就需要配合week_ptr来解决这个问题。
首先来说说aotu_ptr,我们先看下边一段代码:
template<typename T>
class AutoPtr
{
public:
AutoPtr(T* ptr = NULL)
:_ptr(ptr)
{}
AutoPtr(AutoPtr<T>& ap)//管理权的转移
{
this->_ptr = ap._ptr;
ap._ptr = NULL;
}
//s2=s1,先将s2释放掉,再将s1赋给s2,将s1置空
AutoPtr<T>& operator=(AutoPtr<T>& ap)
{
if (this != &ap)
{
delete this->_ptr;
this->_ptr = ap._ptr;
ap._ptr = NULL;
}
return *this;
}
T& operator*()// *的重载
{
return *_ptr;
}
T& operator->()// ->的重载
{
return _ptr;
}
~AutoPtr()
{
delete _ptr;
}
void Reset(T* ptr = NULL)//赋空
{
if (_ptr != ptr)
{
delete _ptr;
}
_ptr = ptr;
}
protected:
T* _ptr;
};
对于内置类型来说,两个指针是可以同时指向同一块空间的,并且通过这个指针对该空间进行操作是不会出现问题。但在自定义类型中,两个指针指向同一块空间会产生问题。管理权的转移就是一种解决办法。在拷贝构造和运算符重载的时候会发生管理权的转移,我们的目的是通过拷贝构造,让两个智能指针管理同一个对象。如果用默认的拷贝构造函数就会产生浅拷贝问题,两个指针指向同一个对象在进行析构的时候,这块空间会被释放两次,就会出现内存错误。所以,一个简单的办法,就是让原来的那个指针放弃对该空间的操作权限,所以实际上只有一个指针起作用。同时,这也是auto_ptrd的一个缺陷。
管理权转移的步骤如下: