引入:
C++程序员最痛苦的莫过于对内存管理,没有像C#和Java那样的内存回收机制,C++程序员在复杂的程序中容易造成内存泄漏,即便是程序员非常小心,异常的产生也会造成部分内存泄漏。(由于异常而导致delete语句没有释放动态分配的内存)从而引出了智能指针(smart_pointer)。
什么是智能呢?
这种指针实质是一个包装了一个指针的类,通过RAII机制来自动释放内存。下面会详细介绍RAII。通过在类里重载 * 运算符,和->运算符来实现基本指针的操作。
RAII(Resource Acqisition Is Initialzation)的缩写,也就是资源获取继初始化。是一种资源管理,避免内存泄漏的方法。RAII的做法是使用一个对象在构造时获取资源,在对象生命周期结束时自动调用析构函数来释放内存。
C++提供了基本的三种智能指针库里的(auto_ptr ,scoped_ptr)和C++11 boost库里的
shared_ptr.
下面来一一分析以下几个指针:
auto_ptr 第一代智能指针
template<class T>
class Auto_ptr
{
public:
Auto_ptr(T*p=0)
:_ptr(p)
{}
~Auto_ptr()
{
delete _ptr;
_ptr = NULL;
}
T& operator *()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
T operator=(T&t)
{
*_ptr = t;
}
private:
T *_ptr;
};
class my_struct
{
public:
my_struct(int i=10,int j=20)
:_i(i)
, _j(j)
{}
int _i;
int _j;
};
int main()
{
Auto_ptr<int> p1(new int(3));
Auto_ptr<my_struct> p2(new my_struct);
cout<<"*p1 = "<<*p1<<endl;
cout << "p2->_i= "<<p2->_i<<endl;
cout <<"p2->_j= "<< p2->_j<<endl;
return 0;
}
测试:
auto_ptr首先具有RAII也就是智能,也具有指针的普通操作。
但以上程序没介绍到赋值运算符重载 和拷贝构造。是因为auto_ptr的赋值运算符重载是一种管理权限转移的操作。如下图
要实现p1=p2,假设p1已经指向一块空间,那么就会让p2先指向这块空间,把p1至为空。也就是管理权限转变给了p2,这和我们的初心大相径庭。
而如果两个同时指向一块空间,析构的时候就会把原来的数据给丢失。所以auto_ptr是一个失败的例子。
scoped_ptr解决了auto_ptr的缺陷,也就是只给出了赋值运算符和拷贝构造的定义,并且为了防止恶意破坏,封装在private里。
template<class T>
class scoped_ptr
{
public:
scoped_ptr(T*p = 0)
:_ptr(p)
{}
~scoped_ptr()
{
delete _ptr;
_ptr = NULL;
}
T& operator *()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
scoped_ptr(scoped_ptr<T>& ptr);
scoped_ptr<T>& operator=(scoped_ptr<T>& ptr);
T *_ptr;
};
最后C++提出了shared_ptr在boost库里,里面运用了引用计数的管理方法进行赋值,等操作。
template<class T>
struct Delete
{
void operator()(T* ptr)
{
delete ptr;
}
};
template<class T>//定制的删除数组删除器
struct DeleteArray
{
void operator()(T* ptr)
{
delete[] ptr;
}
};
template<class T, class D = Delete<T>>
class SharedPtr
{
public:
SharedPtr(T* ptr)
:_ptr(ptr)
,_countRef(new int(1))
{}
SharedPtr(const SharedPtr<T, D>& sp)
:_ptr(sp._ptr)
,_countRef(sp._countRef)
{
++(*_countRef);
}
SharedPtr<T, D>& operator=(const SharedPtr<T, D>& sp)
{
if(_ptr != sp._ptr)
{
Release();
_ptr = sp._ptr;
use_count = sp.use_count;
++(*use_count);
}
return *this;
}
inline void Release()
{
if (--(*_countRef) == 0)
{
cout<<_ptr<<endl;
//delete _ptr;
_del(_ptr);
delete _countRef;
}
}
~SharedPtr()
{
Release();
}
//operator*()
//operator->()
int UseCount()
{
return *_countRef;
}
protected:
T* _ptr; //
int* _countRef; // 引用计数
D _del; // 定制的仿函数删除器
};
但是shared_ptr指向一个双链表节点的时候会出现循环引用,也就是这个节点的引用计数是循环的
每个节点的引用计数永远是2,产生错误。
为了解决问题 boost库里引入weak_ptr指针解决。包含头文件