智能指针(smart pointer)的一种通用实现技术是使用引用计数(reference count)。智能指针类将一个计数器与类指向的对象相关联,引用计数跟踪该类有多少个对象的指针指向同一对象。——摘自百度百科
简单来说,就是当同一个对象有多个指针指向它本身时,需要记录指向对象本身的指针个数,而智能指针就是利用引用计数的方式在记录指针,算作对于指针而言的管理层,有效的管理指针所指向的动态资源的释放问题,避免产生创建指针却忘记释放指针,避免野指针的问题。
智能指针的发展历史
RAII,也称为“资源获取就是初始化”,是c++等编程语言常用的管理资源、避免内存泄露的方法。它保证在任何情况下,使用对象时先构造对象,最后析构对象。——摘自百度百科
RAII并不等价于智能指针,智能指针只是RAII思想的一种实现。
1、智能指针可以防止内存泄漏,利用shared_ptr来管理这类问题。
当shared_ptr管理的引用计数为0时,也就是最后一个指向对象的指针被删除时,就会自动的删除对象,防止程序再也找不到曾经管理的那个对象,从而防止内存泄漏。
template<typename T>
class shared_ptr
{
public:
shared_ptr(T*p)
, _count(new int(1))
, _ptr(p)
{}
shared_ptr(shared_ptr<T>& p)
, _ptr(p._ptr)
, _count(&(++*p._count))
{}
T* operator->()
{
return _ptr;
}
T& operator*()
{
return *_ptr;
}
shared_ptr<T>& operator = (shared_ptr<T>& p)
{
++*p._count;
if (this->_ptr && (--*this->_count) == 0)
{
delete _count;
delete _ptr;
}
this->_ptr = p._ptr;
this->_count = p._count;
return *this;
}
~shared_ptr()
{
if ((--*this->_count) == 0)
{
delete _ptr;
delete _count;
}
_ptr = NULL;
_count = NULL;
}
int Ret_count()
{
return *_count;
}
private:
T*_ptr;
int*_count;
};
2、智能指针可以动态分配对象以及不需要对象时的自动释放,利用auto_ptr来实现。
int*p = new int(0);
auto_ptr ap(p);
我们只管创建,当对象需要释放的时候,auto_ptr会自己释放对象空间。
但是仍然存在一些问题,同一对象不能同时被两个auto_ptr管理,当这一对象需要释放的时候,两个auto_ptr检测到这个对象需要被释放,就会将一个对象释放两遍,会造成程序出问题,即同一对象被释放两遍,类似于malloc与free、new与delete、new[ ]与delete[ ],是有问题的。
auto_ptr,管理权有可能转移,带有严重缺陷的设计方法。
这里严重的缺陷即是会使某些指针悬空,不能很好的管理指针。
//ap2=ap3
Auto_Ptr<T>& operator=(Auto_Ptr<T>&ap)
{
if(this!=&ap)
{
if(_ptr)
{
delete _ptr;
}
_ptr=ap._ptr;
ap._ptr=NULL;
}
return *this;
}
3.scoped_ptr也是一种简单的智能指针,与auto_ptr类似,通过管理对象达到自动释放对象的效果,但是scoped_ptr与auto_ptr不同点在于,scoped_ptr的拷贝构造函数是私有访问的,也就意味着scoped_ptr不允许调用拷贝构造函数,以及作为函数返回值的赋值重载函数,而auto_ptr可以。scoped_ptr只声明不重载。
Scoped_Ptr(T*ptr)
:_ptr(ptr)
{}
~Scoped_Ptr()
{
delete[]_ptr;
}
T& operator[](size_t pos)
{
return _ptr[pos];
}
protected:
Scoped_Ptr(const Scoped_Ptr<T>&s);
4.weak_ptr是shared_ptr的辅助装置,并不占用引用次数。
但是,shared_ptr有一个问题,类内在实例化前,成员函数的成员变量调用是可以用this指针来调用的 ,可是this指针并不包括在shared_ptr的引用次数里,这里就是裸传了this这个指针,然而却没对其进行相应的管理,如果在函数调用的过程中间接使用this指针而使得对象被释放,就会产生问题。
循环引用
struct ListNode
{
ListNode*_prev; //Shared_Ptr<ListNode> _prev;声明
ListNode*_next; //Shared_Ptr<ListNode> _next;
};
以双链表为例,构造函数初始化阶段相当于定义阶段,如果不显示化构造函数,编译器默认构造缺省的构造函数,初始化的时候赋给随机值。但是有例外,const的变量初始化时候必须赋值,当引用别名的时候也得初始化,类的成员变量在初始化调用构造函数来初始化。
但是循环利用就会有问题,
循环引用会导致内存没有释放【内存泄漏】
利用weak_ptr的弱引用指针可以解决循环引用带来的智能指针混乱的问题。
利用_refCount内存的计数次数来控制,只要_refCount不为NULL,内存就不会释放,当_refCount–至0后,然后就会释放空间。