C++中使用new和delete动态开辟和释放内存。但有时我们会忘记释放内存,这样就有可能出现内存泄漏等一些危险的情况。有时在尚有指针引用内存的情况下我们就释放了它,在这种情况下就会产生引用非法内存的指针。
为了更容易,也更安全地使用动态内存,C++提供了智能指针类型来管理动态对象。智能指针遵从RAII规则,我们先来介绍什么是RAII.
RAII:是一种规范,一种解决问题的思想。负责资源分配即初始化,我们定义一个类来封装资源的分配和释放,在构造函数完成资源的分配和初始化,在析构函数完成资源的清理,可以保证资源的正确初始化和释放。
1,auto_ptr
auto_ptr是早期的智能指针设计,它是有缺陷的,其中使用了管理权转移。我们来简单模拟auto_ptr,更深层的理解它。
template<class T>
class AutoPtr
{
public:
//RAII
AutoPtr(T* ptr = NULL)
:_ptr(ptr)
{}
//ap2(ap1)
AutoPtr(AutoPtr<T>& ap)//拷贝构造
:_ptr(ap._ptr)
{
ap._ptr = NULL;
}
//ap2 = ap3
AutoPtr<T>& operator = (AutoPtr<T>& ap)//赋值运算符重载
{
if (this != &ap)
{
delete _ptr;
_ptr = ap._ptr;
ap._ptr = NULL;//管理权转移
}
return *this;
}
~AutoPtr()//析构
{
if (_ptr)
{
cout << _ptr << endl;
delete _ptr;
_ptr = NULL;
}
}
T& operator*()
{
return *_ptr;
}
T& operator->()
{
return _ptr;
}
protected:
T* _ptr;
};
int main()
{
AutoPtr<int> ap1(new int(10));
AutoPtr<int> ap2 = ap1;//ap2(ap1)error//ap1地址指向随机
AutoPtr<int> ap3(new int(20));
ap2 = ap3;//ap3地址为随机值
system("pause\n");
return 0;
}
我们来看一下调试过程中ap1,ap2,ap3的地址有何变化
从调试过程明显看出auto_ptr的缺陷,当想通过原先的指针访问原先所管理的空间时,会找不到原来的空间,因此,我们要避免使用auto_ptr。
注意以下几点:
(1)aotuptr不能共享所有权
(2)autoptr不能指向数组delete
(3)autoptr不能作为容器的成员
(4)不能通过赋值操作来初始化autoptr
(5)不要把autoptr放入容器
2,scoped_ptr
这是一种高效、简洁的智能指针,实现原理是,将拷贝构造函数和赋值运算符的重载写成私有,且只声明不定义。这是一种简单粗暴的防拷贝方式,在不需要拷贝和赋值时,我们尽量使用scoped_ptr.
template<class T>
class ScopedPtr
{
public:
//RAII
ScopedPtr(T* ptr = NULL)
:_ptr(ptr){}
~ScopedPtr()
{
if (_ptr)
{
delete _ptr;
}
}
T& operator*()
{
return *_ptr;
}
T& operator->()
{
return _ptr;
}
protected:
T* _ptr;
private://只声明不定义,且声明为私有
ScopedPtr(const ScopedPtr<T>& sp);
ScopedPtr<T>& operator = (const ScopedPtr<T>&);
};
int main()
{
ScopedPtr<int> sp1(new int(30));
//ScopedPtr<int> sp2(sp1);//错误
return 0;
}
3,shared_ptr
shared_ptr允许多个指针指向同一个对象,我们可以认为每一个shared_ptr都有一个关联的计数器,通常称其为引用计数,无论何时我们拷贝一个shared_ptr,计数器就会增加当我们给shared_ptr赋予一个新值或是shared_ptr被销毁时,计数器就会递减。一旦一个shared_ptr的计数器变为0,它就会自动释放自己所管理的对象。但shared_ptr存在循环引用的缺陷,可能导致空间无法正常清理,我们用仿函数模拟定制删除器来解决此类问题。
template<class T>
struct Delete //清理单个空间
{
void operator()(T* ptr)//定制删除器(仿函数)
{
delete ptr;
}
};
template<class T>
class DeletArray//清理数组
{
public:
void operator()(T* ptr)
{
delete[] ptr;
}
};
template<class T,class D = Delete<T>>//默认是delete,不是delete[]
class SharedPtr
{
public:
SharedPtr(T *ptr)
:_ptr(ptr)
, _countRef(new int(1)){}
SharedPtr<T,D>& operator= (const SharedPtr<T,D>& sp)//赋值运算符的重载
{
if (_ptr != sp._ptr)
{
Release();
_ptr = sp._ptr;
_countRef = sp._countRef;
++(*_countRef);
}
return *this;
}
SharedPtr(const SharedPtr<T, D>& sp)//拷贝构造
:_ptr(sp._ptr)
, _countRef(sp._countRef)
{
++(*_countRef);
}
inline void Release()
{
if (--(*_countRef) == 0)
{
cout << _ptr << endl;
_del( _ptr);
delete _countRef;
}
}
~SharedPtr()
{
Release();
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
int UseCount()//引用计数
{
return *_countRef;
}
protected:
T* _ptr;
int* _countRef;//引用计数
D _del;//定制的仿函数删除器
};
boost库中提供一个weak_ptr来解决循环引用的问题,weak_ptr是一种不控制所指向对象生存期 的智能指针,它指向由一个shared_ptr管理的对象。将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数,一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放。
由于对象可能不存在,我们不能使用weak_ptr直接访问对象,而必须调用lock,此函数检查weak_ptr指向的对象是否仍存在,如果存在,lock返回一个指向共享对象的shared_ptr.
shared_ptr<int> p(new int (10));
weak_ptr<int> wp(p);//wp弱共享p,p的引用计数不改变
if(shared_ptr<int> np = wp.lock())//若np不为空则条件成立
{
//np与p共享对象
}
我们简单模拟一下weak_ptr的实现
template<class T>
class WeakPtr;
template<class T>
class SharedPtr
{
friend class WeakPtr<T>;
public:
SharedPtr(T* ptr)
:_ptr(ptr)
, _refcount(new int(1))
{}
SharedPtr(const SharedPtr<T>& sp)
:_ptr(sp._ptr)
, _refcount(sp._refcount)
{
++(*_refcount);
}
void Relese()
{
if (--(*_refcount) == 0)
{
if (_ptr)
{
cout << this << endl;
delete _ptr;
}
delete _refcount;
}
}
SharedPtr<T>& operator = (const SharedPtr<T>& sp)
{
if (_ptr != sp._ptr)
{
Relese();
_ptr = sp._ptr;
_refcount = sp._refcount;
++(*_refcount);
}
return *this;
}
~SharedPtr()
{
Relese();
}
T& operator*()
{
return *_return;
}
T* operator->()
{
return _ptr;
}
private:
T* _ptr;
int* _refcount;
};
template<class T>
class WeakPtr
{
public:
WeakPtr()
:_ptr(NULL)
{}
WeakPtr(const SharedPtr<T> sp)
:_ptr(sp._ptr)
{}
T* operator->()
{
return _ptr;
}
T& operator*()
{
return *_ptr;
}
private:
T* _ptr;
};
struct ListNode
{
WeakPtr<ListNode> _prev;
WeakPtr<ListNode> _next;
};
void Test()
{
SharedPtr<ListNode> cur = new ListNode;
SharedPtr<ListNode> next = new ListNode;
cur->_next = next;
next->_prev = cur;
}
int main()
{
Test();
system("pause");
return 0;
}