智能指针,即智能指针对象,实现了自主的内存回收机制。
- 智能指针是一个模板,由智能指针实例化出来的对象具有和常规指针相似的行为,但是智能指针能够自动的释放所指向的对象。
- 对编译器来说,智能指针实际上是一个栈对象,并非指针类型,在栈对象生命周期你即将结束时,智能指针通过析构函数释放由它管理的堆内存。
智能指针的分类:
- 不带引用计数的智能指针:auto_ptr、unique_ptr、scoped_ptr
- 带引用计数的智能指针:shared_ptr、weak_ptr
1. auto_ptr
一块堆内存 只要有一个智能指针拥有所有权
部分源码如下:
template<typename T>
class Auto_Ptr
{
public:
Auto_Ptr(T* ptr) :mptr(ptr){}
Auto_Ptr(const Auto_Ptr<T>& rhs)
{
mptr = rhs.mptr;
rhs.Release();
}
Auto_Ptr<T>& operator=(const Auto_Ptr<T>& rhs)
{
if (this != &rhs)
{
delete mptr;//delete NULL;
mptr = rhs.mptr;
rhs.Release();
}
return *this;
}
~Auto_Ptr()
{
if (mptr != NULL)
{
delete mptr;
}
mptr = NULL;
}
T& operator*()
{
return *mptr;
}
T* operator->()
{
return mptr;
}
private:
void Release()const
{
(T*)mptr = NULL;
}
T* mptr;
};
2.带标志位的智能指针
所有权不唯一,释放权唯一。
旧的智能指针有释放权 -> 新的智能指针就有释放权
旧的智能指针无释放权 -> 新的智能指针无释放权
旧的智能指针赋值后肯定没有释放权
即:
this->flag = rhs.flag;
rhs.flag = false;
(带标志位的智能指针实际是对auto_ptr的改进)
部分源码如下:
template<typename T>
class SmartPtr
{
public:
SmartPtr(T* ptr) :mptr(ptr)
{
flag = true;
}
~SmartPtr()
{
if (flag)
{
delete mptr;
}
mptr = NULL;
}
SmartPtr(const SmartPtr<T>& rhs)
{
this->mptr = rhs.mptr;
this->flag = rhs.flag;
rhs.flag = false;
}
SmartPtr<T>& operator=(const SmartPtr<T>& rhs)
{
if (this != &rhs)
{
if (flag)
{
delete mptr;
}
mptr = rhs.mptr;
this->flag = rhs.flag;
rhs.flag = false;
}
return *this;
}
T& operator*()
{
return *mptr;
}
T* operator->()
{
return mptr;
}
private:
T* mptr;
mutable bool flag;//是否拥有释放权 true 有 false 无
};
带标志位的智能指针存在的问题:
由于所有权的转移,可能使内存提前释放,导致其他的智能指针失效。
3.scoped_ptr
一个堆内存只能有一个智能指针来引用。(是最简单的智能指针)
为了防止拷贝带来的问题,scoped_ptr从根本上就不允许拷贝和赋值,即禁止使用拷贝构造函数和赋值运算符的重载函数。
部分源码如下:
template<typename T>
class Scope_Ptr
{
public:
Scope_Ptr(T* ptr) :mptr(ptr){}
~Scope_Ptr()
{
delete mptr;
}
T& operator*()
{
return *mptr;
}
T* operator->()
{
return mptr;
}
//将拷贝构造函数和赋值运算符的重载函数写在私有成员下,从而“防拷贝,防赋值”
private:
Scope_Ptr(const Scope_Ptr<T>&);
Scope_Ptr<T>& operator=(const Scope_Ptr<T>&);
T* mptr;
};
4. shared_ptr
强智能指针,允许多个智能指针指向同一个对象。引用计数管理器来管理指向对象的智能指针。
部分源码如下:
//引用计数管理器
class Ref_Management
{
private:
Ref_Management() :index(0){}
Ref_Management(const Ref_Management&);
public:
static Ref_Management* getInstance()
{
return &rm;
}
public:
void addRef(void* ptr) //增加计数
{
int idx = FindAddr(ptr);
if (idx < 0)
{
//node[index].ptr = ptr;
//node[index++].ref = 1;
Node tmp(ptr, 1);
node[index++] = tmp;
}
else
{
node[idx].ref++;
}
}
void delRef(void* ptr) //删除计数
{
int idx = FindAddr(ptr);
if (idx < 0)
{
throw std::exception("addr is not exist!");
}
else
{
if (node[idx].ref != 0)
{
node[idx].ref--;
}
}
}
int getRef(void* ptr) //得到计数
{
int idx = FindAddr(ptr);
if (idx < 0)
{
throw std::exception("addr is not exist!");
}
return node[idx].ref;
}
private:
int FindAddr(void* ptr) //查找计数
{
int rt = -1;
for (int i = 0; i < 10; i++)
{
if (node[i].ptr == ptr)
{
rt = i;
break;
}
}
return rt;
}
class Node
{
public:
Node(void* p = NULL, int r = 0) :ptr(p), ref(r){}
public:
void* ptr;//堆内存的地址
int ref; //对应的引用计数
};
Node node[10];
int index;
static Ref_Management rm;
};
Ref_Management Ref_Management::rm;
//强智能指针
template<typename T>
class Shared_Ptr
{
public:
Shared_Ptr(T* ptr = NULL) :mptr(ptr)
{
prm->addRef(mptr);
}
Shared_Ptr(const Shared_Ptr<T>& rhs) :mptr(rhs.mptr)
{
prm->addRef(mptr);
}
Shared_Ptr<T>& operator=(const Shared_Ptr<T>& rhs)
{
if (this != &rhs)
{
prm->delRef(mptr);
if (prm->getRef(mptr) == 0)
{
delete mptr;
}
mptr = rhs.mptr;
prm->addRef(mptr);
}
return *this;
}
~Shared_Ptr()
{
prm->delRef(mptr);
if (prm->getRef(mptr) == 0)
{
delete mptr;
}
mptr = NULL;
}
T& operator*()
{
return *mptr;
}
T* operator->()
{
return mptr;
}
T* getPtr()const
{
return mptr;
}
private:
T* mptr;
static Ref_Management* prm;
};
template<typename T>
Ref_Management* Shared_Ptr<T>::prm = Ref_Management::getInstance();
shared_ptr存在的问题:
强智能指针的相互引用,使指针无法释放,导致内存泄漏。
5.weak_ptr
由于强智能指针的相互引用问题,由此引出弱智能指针。
weak_ptr不加引用计数,不能释放内存,不能单独使用,要结合shared_ptr使用
部分源码如下:
template<typename T>
class Weak_Ptr
{
public:
Weak_Ptr(T* ptr = NULL) :mptr(ptr){}
Weak_Ptr(const Weak_Ptr<T>& rhs) :mptr(rhs.mptr){}
Weak_Ptr<T>& operator=(const Shared_Ptr<T>& rhs) //weak_ptr不能单独使用,要结合shared_ptr使用
{
mptr = rhs.getPtr(); //只获得计数,不加计数
return *this;
}
~Weak_Ptr(){}
private:
T* mptr;
};