智能指针的意义
在C++中,new和delete申请和销毁堆内存的关键字,而程序员在进行堆内存申请之后,常常容易忘记delete释放堆内存,而这也就容易造成一种现象–内存泄漏,也就是一块堆内存被申请之后,没有被释放,导致其他程序也不能使用这块堆内存,从而造成堆内存的浪费,降低了c++的开发效率。在java等语言中,只有new的关键字,而没有delete关键字,这就是因为java语言内存回收机制,而由于之前C++对于代码运行效率的极度追求,所以也没有引入这种机制。但是。随着C11标准等的发布,智能指针逐渐被大家所接受,虽然会降低一点效率,也大大的减少了开发人员在开发过程遇到的内存泄漏等问题。
智能指针并不是一个指针,而是一类模板,通过智能指针实例化出来的对象可以在对象销毁的时候,自动释放内存,而不用开发人员手动释放。
智能指针的实现思路
对于一个对象的生成和销毁,分别有以下的步骤:
对象的生成:
- 开辟堆内存;
- 调用构造函数。
对象的销毁:
- 调用析构函数;
- 释放内存;
根据对象销毁时的重要特性:自动调用析构函数 ,我们只需要将delete释放内存的函数写到析构函数中,则完成了在对象销毁时,系统可以自动的释放堆内存,从而避免了内存泄漏等问题。
C++11标准下的智能指针
C++为开发者提供了两类智能指针:
- 带引用计数的智能指针
- 不带引用计数的智能指针
分别是auto_ptr,unique_ptr,shared_ptr,weak_ptr
等智能指针。其中auto_ptr
是在c98标准中就引入到的智能指针,而剩余的几种智能指针则是后来参考boost库中智能指针的实现而引入到C++中。他们都存在与memory
库中,所以在使用智能指针时
auto_ptr
auto_ptr早在c98标准发布是就存在于STL库中,但现在由于各种各样的性能问题而被弃用。先研究一下auto_ptr的源代码:
template<class _Ty>
class auto_ptr
{ // wrap an object pointer to ensure destruction
public:
typedef auto_ptr<_Ty> _Myt;
typedef _Ty element_type;
// 构造函数
explicit auto_ptr(_Ty *_Ptr = 0) _THROW0()
: _Myptr(_Ptr)
{ // construct from object pointer
}
// 拷贝构造函数,调用release方法
auto_ptr(_Myt& _Right) _THROW0()
: _Myptr(_Right.release())
{ // construct by assuming pointer from _Right auto_ptr
}
// 赋值运算符函数,调用release方法
template<class _Other>
_Myt& operator=(auto_ptr<_Other>& _Right) _THROW0()
{ // assign compatible _Right (assume pointer)
reset(_Right.release());
return (*this);
}
// 析构函数
~auto_ptr() _NOEXCEPT
{ // destroy the object
delete _Myptr;
}
// 指针解引用运算符重载
_Ty& operator*() const _THROW0()
{ // return designated value
#if _ITERATOR_DEBUG_LEVEL == 2
if (_Myptr == 0)
_DEBUG_ERROR("auto_ptr not dereferencable");
#endif /* _ITERATOR_DEBUG_LEVEL == 2 */
return (*get());
}
// 指向符重载
_Ty *operator->() const _THROW0()
{ // return pointer to class object
#if _ITERATOR_DEBUG_LEVEL == 2
if (_Myptr == 0)
_DEBUG_ERROR("auto_ptr not dereferencable");
#endif /* _ITERATOR_DEBUG_LEVEL == 2 */
return (get());
}
// get方法——获取指针地址
_Ty *get() const _THROW0()
{ // return wrapped pointer
return (_Myptr);
}
// 剥夺原auto_ptr对指针的拥有权,赋予当前auto_ptr对指针的拥有权
_Ty *release() _THROW0()
{ // return wrapped pointer and give up ownership
_Ty *_Tmp = _Myptr;
_Myptr = 0;
return (_Tmp);
}
// 销毁指定对象并存储新指针
void reset(_Ty *_Ptr = 0)
{ // destroy designated object and store new pointer
if (_Ptr != _Myptr)
delete _Myptr;
_Myptr = _Ptr;
}
private:
_Ty *_Myptr; // the wrapped object pointer
};
Shared_ptr
shared_ptr和别的智能指针最大的区别是加入了引用计数,即可以对一块资源的使用对象进行计数,如果这块资源的使用对象变为0,则需要删除这一块内存。但是通常情况下要搭配weak_ptr使用。
代码如下:
ount;
};
trmplate<class T>
class Shard_ptr{
public:
Shared_ptr(T* p=0):_ptr(p)
{
cnt=new Counter;
if(p)
{
cnt->shardCount=1;
}
}
~Shared_ptr()
{
release();
}
Shared_ptr(Shared_ptr<T> const &src) //拷贝构造
{
_ptr=src._ptr;
//原shared对象的
(src.cnt)->shardCount++;
}
Shared_ptr(WeakPtr<T>&const w)
{
//复制weakptr对象指针
_ptr=w._ptr;
(w.cnt)->sharedCount++;
cnt=w.cnt;
}
Shared_ptr<T>& operator=(Shared_ptr<T>& src)
{
//防止自我引用
if(this!=&src)
{
//释放源管理对象的所有权
release();
(src.cnt)->sharedCount++;
cnt=src.cnt;
_ptr=src._ptr;
}
return *this;
}
T& operator*()
{
return* _ptr;
}
T* operator->()
{
return _ptr;
}
friend class WeakPtr<T>;
protected:
//释放源对象所有权,count为0,则回收内存
void release()
{
cnt->shardCount--;
if(cnt->shardCount<1)
{
delete _ptr;
if(cnt->weakCount<1)
{
delete cnt;
cnt=NULL;
}
}
}
private:
T* _ptr;
Counter* cnt;
};
weak_ptr
weak_ptr的诞生就是为了配合Shared_ptr来使用的,它的拷贝构造函数是通过shared_ptr和weak_ptr对象来构造,没有重载*和->,并且使用lock()来获取一个可用的对象。weak_ptr()不拥有内存的使用权,所以不能直接使用资源。通常是使用lock()来生成一个Shared_ptr来配合使用。
template<class T>
class WeakPtr
{
public:
WeakPtr()
{
_ptr=0;
cnt=new Counter();
}
WeakPtr(Sharedptr<T>& src):_ptr(src._ptr),_cnt(src.cnt)
{
cnt.weakCount++;
}
WeakPtr(WeakPtr<T>& src):_ptr(src._ptr),cnt(src.cnt)
{
cnt.weakCount++;
}
~WeakPtr()
{
release();
}
WeakPtr<T>& operator=(WeakPtr<T>& src)
{
if(this!=*src)
{
//释放原来的管理对象
release();
cnt=src.cnt;
cnt->weakCount++;
_ptr=src._ptr;
}
return *this;
}
WeakPtr<T>& operator=(WeakPtr<T>& src)
{
if(this!=*src)
{
release();
cnt=src.cnt;
cnt->shardCount++;
_ptr=src._ptr;
}
return *this;
}
Sharedptr<T> lock()
{
//将weakptr转成sharedptr
return Sharedptr<T>(*this);
}
bool expired()
{
if(cnt)
{
if(cnt->shardCount>0)
{
return false;
}
}
return true;
}
friend class Sharedptr<T>;
protected:
void release()
{
if(cnt)
{
cnt->weakCount--;
if(cnt->weakCount<1 && cnt->s<1)
{
cnt=NULL;
}
}
}
private:
T* _ptr;
Counter* cnt;
};
引入weak_ptr就是为了解决强智能指针Shared_ptr可能会导致交叉引用而导致计数器永远不会减到0而导致资源不能被释放。所以在定义对象是使用强智能指针,使用对象使用弱智能指针。