如 int* p = new int;//p是裸指针
因为有时忘记delete或者遇到return导致程序退出
智能指针的出现 是为了解决内存泄露,它利用了栈上的对象出了作用域就会自动析构的特点。所以它的设计思想是如果能在堆上手动开辟,然后由系统释放,那就很完美了。
智能指针的分类:
C++98的智能指针:auto_ptr
C++11的引入智能指针:scoped_ptr,unique_ptr( move )、shared_ptr、weak_ptr,废弃了auto_ptr
不带引用计数的智能指针包括:auto_ptr,unique_ptr,scoped_ptr
带引用计数的智能指针包括:shared_ptr weak_ptr
1.auto_ptr 是通过转移管理权来完成对象的拷贝与赋值
为了防止程序抛出异常或者不正常return导致程序结束但没有delete堆上的内存资源,我们使用auto_ptr让它在自己的析构函数中进行资源释放。
但是,它也存在一定的缺点:
例如,auto_ptr<A> ptr1(new A(10)); auto_ptr<A> ptr2 = ptr1;
这两行代码执行完成之后,ptr1已经不再指向A这个对象,ptr1=NULL,它的管理权已经释放,转移到了ptr2上。此时只有ptr2拥有对A对象的管理权。
假如我们进行以下操作:ptr1->fun();此时会报错,因为ptr1此时指向的是NULL,非法访问。
并且如 vector < auto_ptr> vec;
我们不能将auto_ptr类型的指针作为STL容器的元素,因为容器免不了要拷贝构造和赋值。
所以auto_ptr是一个被C++11废弃的智能指针,因为它存在中途释放,后面可能还要使用的问题。
2.unique_ptr 可以看成是auto_ptr的替代品,它是独占型的。在某一时刻,只能有一个unique_ptr指向的对象。
unique_ptr无法进行赋值和拷贝构造操作。auto_ptr可以进行赋值和拷贝,只是存在中途释放后续无法使用的问题,有时候会可能会搞得人莫名其妙。但是unique_ptr比较直接,它根本无法进行赋值和拷贝操作。如果一定要转移所有权的话,在C++11标准中,unique_ptr可以通过move函数实现赋值与拷贝操作,
如
unique_ptr < A > ptr1(new A(10));
unique_ptr< A > ptr2(std::move(ptr1));
以上代码和auto_ptr的效果是一样的,ptr1依然不再拥有A对象。
3.scoped_ptr 不支持管理权的转移,禁止用户进行拷贝与赋值。auto_ptr可以进行拷贝构造和赋值,只是存在问题。unique_ptr是独占型的,但可以通过move函数明确的告诉用户所有权转移了。scoped_ptr把拷贝构造函数和赋值运算符重载函数写在private下面,保证用户无法进行拷贝构造。
4.shared_ptr 可以多个智能指针同时拥有一个对象,shared_ptr的实现方式是使用引用计数
引用计数是在统计有多少个对象管理这个堆内存
引用计数的原理是,多个智能指针同时拥有一个对象,每当有一个新的引用指向对象时,引用计数就加1,每当删除一个引用,引用计数就减1,当引用计数为0时,就释放该对象占用的资源。
就比如,教室里每进来一个人,学生数就加1,每出去一个人,学生数就减1,当一直减到0时,教室里最后一个人离开后,需要关灯,相当于释放对象占用的资源。
5.weak_ptr 没有对对象的使用权,只有监控权。它不会增加和减少引用计数,也不会释放对象所占用的资源。
所以,weak_ptr一定要与shared_ptr配合使用,不能单独使用。在创建对象的时候,一定要强智能指针,不能用弱智能指针。
实现一个shared_ptr
(1)资源引用计数管理类
class CHeapManager
{
public:
static CHeapManager& getInstance( )
{
return heapmanager;
}
void addRef( void *ptr )
{
vector<ResItem>::iterator it = find(_vec.begin(),_vec.end(),ptr);
if(it == _vec.end())//没找到,添加[没找到就不能有it->_refcount的操作]
{
ResItem item(ptr);//拷贝构造一个ResItem类型的对象,尾插
_vec.push_back(item);
cout<<"new addr:"<<ptr<<" refcount:"<<1<<endl;
}
else//找到了,加1
{
it->_refcount++;
cout<<"add res:"<<ptr<<" refcount:"<<it->_refcount<<endl;
}
}
void delRef(void *ptr)
{
vector<ResItem >::iterator it = find(_vec.begin(),_vec.end(),ptr);
if(it != _vec.end())//找到了,减1
{
it->_refcount-- ;
cout<<"del addr:"<<ptr<<" refcount:"<<it->_refcount<<endl;
}
//如果没有找到可以抛出异常
}
int getRef(void *ptr)
{
vector<ResItem>::iterator it = find(_vec.begin(),_vec.end(),ptr);
if(it != _vec.end())
{
return it->_refcount;
}
throw "no resource";
}
private:
struct ResItem//节点类型
{
ResItem(void *ptr=NULL):_paddr(ptr),_refcount(0)
{
_refcount = 1;
}
bool operator==(void *ptr)
{
return _paddr == ptr;
}
void *_paddr;//记录资源的起始地址
int _refcount;//引用计数
};
vector<ResItem> _vec;
CHeapManager( ){ }
static CHeapManager heapmanager;
};
CHeapManager CHeapManager : : heapmanager ;
//(2)智能指针类
template<typename T>
class CSmartPtr
{
public:
CSmartPtr(T *ptr = NULL):_ptr(ptr)
{
if(_ptr != NULL)
addRef();
}
CSmartPtr(const CSmartPtr<T> &src):_ptr(src._ptr)
{
if(_ptr != NULL)
addRef();
}
CSmartPtr<T>& operator=(const CSmartPtr<T> &src)
{
if(_ptr == src)//1.先判断是否自赋值
return *this;
delRef(); //2.减引用计数
if(0 == getRef()) //3.判断引用计数是否为0,为 0 则删除
delete _ptr;
_ptr = src._ptr; //4.进行赋值操作
if(_ptr != NULL) //5.如果又有智能指针引用,则加1
addRef();
return *this; //6.返回 *this
}
~CSmartPtr()
{
delRef();
if(getRef() == 0)
delete _ptr;
}
void addRef(){_heapManager.addRef(_ptr);}
void delRef(){_heapManager.delRef(_ptr);}
int getRef(){return _heapManager.getRef(_ptr);}
private:
T *_ptr;
static CHeapManager& _heapManager;//引用计数管理类的引用计数应该是所有对象所共享的,所以为静态
};
template<typename T>
CHeapManager& CSmartPtr<T>::_heapManager = CHeapManager::getInstance();//初始化静态成员