在C++里面开辟空间和内存的释放都存在的内存泄漏的隐患,在C++里面引进了智能指针。auto_ptr:
auto_ptr的实现机制是依靠资源的转移,在拷贝构造和赋值运算符重载的时候进行赋值对象的指向转换。
template<class T>
class AutoPtr
{
public:
AutoPtr( T* _ptr = NULL)
:ptr(_ptr)
{}
AutoPtr(AutoPtr& au)
:ptr(au.ptr)
{
au.ptr = NULL;
}
AutoPtr& operator=(AutoPtr &au)
{
if (this != &au)
{
ptr = au.ptr;
au.ptr = NULL;
}
}
private:
T* ptr;
};
unique_ptr:
防拷贝,将拷贝构造函数和赋值运算符的重载放在protect里面,同时只给出这两个函数的声明。
简单实现代码:
template<typename T>
class Uniqueptr
{
public:
Uniqueptr(const T* _ptr = NULL)
:ptr(_ptr)
{
}
~Uniqueptr()
{}
private:
Uniqueptr(const Uniqueptr&u);
Uniqueptr& operator=(const Uniqueptr& u);
private:
T* ptr;
};
注:在这个类里面虽然不能调用拷贝构造函数进行拷贝构造和赋值运算符的重载进行实例化出其他的对象,但是如果在类里面含有其他的友元函数进行调用拷贝构造或者是调用赋值运算符重载函数进行构造其他的类,那么就存在这很大的隐患。在《effective C++》里面提供了一种比较好的处理方法就是利用继承的方式解决这个问题,因为在继承里面友元是不能继承的,所以当把 基类的拷贝构造和赋值运算符的重载的访问限定符声明成private的virtual函数,那么在类外面想要利用构造函数和赋值运算符的重载来实例化对象的时候就会先调用基类的构造函数,那么这样就会将限制放在基类里面,间接地将上面存在的问题解决了。
scoped_ptr:
这个指针只能够管理单独的空间,实现代码和unique_ptr类似。
shared_ptr:
实现机制是依靠引用计数,在类的内部含有两个指针,一个是ptr,一个是pcount,pcount用来存储当前内存被多少对象管理。
简单实现代码:
template <typename T>
class SharedPtr
{
public:
SharedPtr()
:ptr(NULL)
,pcount(NULL)
{}
SharedPtr( T* _ptr)
:ptr(_ptr)
,pcount(new int(1))
{
}
SharedPtr(const SharedPtr& s)
:ptr(s.ptr)
,pcount(s.pcount)
{
IsCount();
}
SharedPtr & operator=(const SharedPtr& s)
{
if (this != &s)
{
IsDelete();
ptr = s.ptr;
pcount = s.pcount;
IsCount();
}
return*this;
}
~SharedPtr()
{
IsDelete();
}
private:
void IsDelete()
{
if (ptr&& 0 == --*pcount)
{
delete ptr;
delete pcount;
ptr = NULL;
pcount = NULL;
}
}
void IsCount()
{
if (pcount)
++*pcount;
}
T* ptr;
int *pcount;
};
智能指针虽然在实际运用的过程里面有很多的好处但是不是任何智能指针都是没有缺点的,比如在shared_ptr里面就存在着引用计数的问题:
当这里的p1指向p2的时候p2里面的count就会自加1,同时在这里的p2指向p1的时候p1里面的count也会自加1.这样在两个结点里面的count都是2,那么在这两个结点要销毁的时候就会只是count-1,而这两个结点依然存在,没有销毁。这样就会出现内存泄漏的危险。
C++在处理这个问题的时候采用了一个weak_ptr,这个智能指针在底层含有两个指针,当这个weak_ptr赋值或者拷贝构造的时候增加的并不是这里count里面的值,而是另外一个指针里面的值。而当结点需要被销毁的时候依据 的是count里面的值,那么这个结点就会被销毁。
当p1给p2赋值的时候,p2里面的weak计数会变成2,但是count还是原来的值。同样p2给p1赋值的时候同样weak计数加一,count不变。
当p1销毁的时候,p1里面的count会自减,这时候会使p1被销毁,同样p2需要销毁的时候也会被销毁,这样就有效的避免了内存泄漏的危险。