智能指针
智能指针是行为类似于指针的类对象,利用对象的析构函数,在对象过期时让其析构函数删除指向的堆内存。 智能指针主要有两类:
- 不带引用计数的智能指针
C++库的auto_ptr
Boost库的scrope_ptr
- 带引用计数的智能指针
shared_ptr
auto_ptr是C++98提供的方案,现在C++11已经摒弃。
一、auto_ptr
template<typename T>
class Auto_ptr
{ public:
Auto_ptr(T* ptr = NULL):_ptr(ptr)
{
ptr = NULL;
}
Auto_ptr(Auto_ptr<T>& rhs)
{
_ptr = rhs._ptr;
rhs._ptr = NULL;
}
Auto_ptr<T>& operator=(Auto_ptr<T>& rhs)
{
if(this != &rhs)
{
delete _ptr;
_ptr = rhs._ptr;
rhs._ptr = NULL;
}
return *this;
}
~Auto_ptr()
{
delete _ptr;
_ptr = NULL;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private: T* _ptr;
};
class Test
{
public:
Test(int a = 10):ma(a){}
void Show()
{
cout<<ma<<endl;
}
private: int ma;
};
void Test1(int a)
{
int* p = new int();
if (a <= 0)
{
throw exception("a < 0");
}//如果说发生异常,delete p不会被调用,会造成内存泄漏 delete p;
}
void Test2()
{
Test* p = new Test(10);
p->Show();
delete p;
//如果没有delete p;则Test类的析构函数无法被调用。在Test2函数中,p是一个局部指针变量,函数结束p会被释放掉,但是p所指向的堆内存无法被释放。
}
int main()
{
Test* p = new Test(10);
p->Show();
delete []p;
//如果忘记写delete就会造成内存泄漏
Test* p1 = new Test(20);
Auto_ptr<Test> ap1(p1);
ap1->Show();
Auto_ptr<Test> ap2(ap1);
//程序会出错 ap1->Show(); return 0;
}
基于上述的情况,为了解决忘记delete或由于其他原因而造成的内存泄漏问题,使用了智能指针。将一个指针托管于一个对象,利用对象的特点:系统调用构造函数和析构函数,也就是说在Auto_ptr的析构函数释放,生存周期到时,自动会释放掉托管的指针指向的堆内存。
缺点: 在上述代码中,到ap2时程序会崩溃,这也就是auto_ptr的缺点:一个对象只能被一个智能指针所指向,调用ap2(ap1)时,就会把ap1置NULL,在ap1->Show()就会崩溃。
另外,auto_ptr不能管理对象数组。一次分配多个对象我们必须要用delete[]去释放,不然会出错。
最终的问题其实就是delete和delete[]的区别问题。 delete用于释放单个对象,获取的是对象的地址,并且调用free,而对象数组的分配有所不同,除了分配所需要大小的堆内存空间外,这段堆内存的开头还分配了4个字节的空间,用于存放分配对象的个数,在释放的时候必须把最开头的四个字节包含进去,delete[]在释放的时候,将得到的地址减4,这样取得了最开始的地址,这样的释放才是正确的释放,而delete的释放并没有减4的操作,所以无法正确的释放。 可以看到auto_ptr的析构函数中只有delete的释放,所以对象数组必然出错。
不要讲auto_ptr储存在容器中,因为赋值和拷贝构造后原指针无法使用。
上图来自https://my.oschina.net/hevakelcj/blog/465978
二、scoped_ptr
scoped_ptr 的实现原理是防止对象间的拷贝和赋值,即将拷贝构造和赋值运算符重载放入保护或私有的访问限定符内,只声明不定义防止他人在类外拷贝。简单粗暴地解决了auto_ptr的缺点,提高了代码的安全性,但是导致功能不完整。
template<class T>
class ScopedPtr
{
public:
ScopedPtr(T * p = NULL)
:_Ptr(p)
{
}
~ScopedPtr()
{
if (NULL != _Ptr)
{
delete _Ptr;
_Ptr = NULL;
}
}
T& operator*()
{
return *_Ptr;
}
T* operator->()
{
return _Ptr;
}
private:
ScopedPtr(ScopedPtr<T>& sp);//只声明不实现,并且声明为私有,防止其他人实现并调用
ScopedPtr<T>& operator=(ScopedPtr<T>& sp);
private:
T* _Ptr;
};
三、shared_ptr & weak_ptr
shared_ptr的实现原理是通过引用计数(int *count)来实现,拷贝或赋值时将引用计数加1,析构时只有当引用计数减到0才释放空间,否则只需将引用计数减1即可。
shared_ptr没有解决相互引用导致内存泄漏的问题,即指两个对象相互引用,造成相互制约,导致指针不能释放。使用一个弱引用智能指针(weak_ptr)来打破循环引用(weak_ptr不增加引用计数)
weak_ptr是一个弱引用,只引用,不计数。如果一块内存被shared_ptr和weak_ptr同时引用,当所有shared_ptr析构了之后,不管还有没有weak_ptr引用该内存,内存也会被释放。所以weak_ptr不保证它指向的内存一定是有效的,在使用之前需要检查weak_ptr是否为空指针。
weak_ptr是一种不控制所指向对象生存期的智能指针,它指向一个shared_ptr管理的对象。将一个weak_ptr绑定到shared_ptr不会改变shared_ptr的引用计数。一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放,即使有weak_ptr指向对象,对象还是会被释放。当我们创建一个weak_ptr时,要用一个shared_ptr来初始化它。
class RefManage
{
public:
static RefManage& getIntance()
{
return rm;
}
private:
RefManage() :length(0){}
RefManage(const RefManage&rhs) :length(0){}
public:
void addRef(void* ptr)
{
cout << "add:" << endl;
if (ptr != NULL)
{
int index = find(ptr);
if (index < 0)//不存在 插入到新的结点上
{
Node tmp(ptr);
tmp.refcount++;
node[length++] = tmp;
cout << " addr: " << ptr << " refcount: " << tmp.refcount << endl;
}
else//存在 引用计数加1
{
node[index].refcount++;
cout << " addr: " << ptr << " refcount: " << node[index].refcount<< endl;
}
}
}
void delRef(void* ptr)
{
cout << "delRef" << endl;
if (ptr != NULL)
{
int index = find(ptr);
if (index < 0)
{
throw exception("addr not exsit!");
}
else
{
if (node[index].refcount != 0)
{
node[index].refcount--;
}
cout << " addr: " << ptr << " refcount: " << node[index].refcount << endl;
}
}
}
int getRef(void *ptr)
{
if (ptr == NULL)
{
return 0;
}
int index = find(ptr);
if (index < 0)//addr不存在返回-1
{
return -1;
}
return node[index].refcount;//存在返回引用计数
}
private:
int find(void* ptr)
{
for (int i = 0; i < length; ++i)
{
if (node[i].refaddr == ptr)
{
return i;
}
}
return -1;
}
class Node
{
public:
Node(void* p = NULL) :refaddr(p), refcount(0){}
void* refaddr;
int refcount;
};
Node node[10];
int length;//记录当前数组的有效长度
static RefManage rm;
};
RefManage RefManage::rm;
template<typename T>
class Shared_ptr
{
public:
Shared_ptr(T* p = NULL)
{
_ptr = p;
AddRef();
}
Shared_ptr(const Shared_ptr<T>& rhs)
{
_ptr = rhs._ptr;
AddRef();
}
Shared_ptr<T>& operator=(const Shared_ptr<T>& rhs)
{
if (this != &rhs)
{
DelRef();
if (0 == GetRef())
{
delete _ptr;
}
_ptr = rhs._ptr;
AddRef();
}
return *this;
}
T* GetPtr()const
{
return _ptr;
}
~Shared_ptr()
{
DelRef();
if (0 == GetRef())
{
delete _ptr;
}
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
void AddRef()
{
rm.addRef(_ptr);
}
void DelRef()
{
rm.delRef(_ptr);
}
int GetRef()
{
return rm.getRef(_ptr);
}
T* _ptr;
static RefManage &rm;
};
template<typename T>
RefManage& Shared_ptr<T>::rm = RefManage::getIntance();
template<typename T>
class Weak_ptr
{
public:
Weak_ptr(T* p = NULL)
{
_ptr = p;
}
Weak_ptr(const Shared_ptr<T>& rhs)
{
_ptr = rhs.GetPtr();
}
Weak_ptr<T>& operator=(const Shared_ptr<T>& rhs)
{
_ptr = rhs.GetPtr();
return *this;
}
~Weak_ptr()
{
_ptr = NULL;
}
private:
T* _ptr;
};