要说智能指针我们应该先了解一下RAII
RAII(Resource Acquisition Is Initialization),也成为“资源获取就是初始化”,是C++语言的一种管理资源、避免泄漏的惯用法。C++标准保证任何情况下,已构造的对象最终会销毁,即它的析构函数最终会被调用。简单的说,RAII 的做法是使用一个对象,在其构造时获取资源,在对象生命期控制对资源的访问使之始终保持有效,最后在对象析构的时候释放资源。
智能指针就融合了RAII这种思想。
void fun()
{
simple* mySimplePtr=new Simple();
mySimplePtr->go();
delete mySimplePtr;
}
如上代码,看着好着了,但是仍然可能存在内存泄露的问题,如果go()
抛出异常,那么mySimplePtr
便永远不会调用delete
。
如果我们用智能指针就不会有内存泄露。通过模拟实现auto_ptr将其写成 AutoPtr<int> mySimplePtr;mySimplePtr>go();mySimplePtrshiyong
使用完了就会自己调用析构。
1.模拟实现auto_ptr–不建议使用
//独占所有权,所有权转移
#include<iostream>
using namespace std;
template<class T>
class AutoPtr
{
public:
AutoPtr()
:_ptr(NULL)
{}
AutoPtr(T* ptr)//构造函数
:_ptr(ptr)
{}
AutoPtr(AutoPtr<T>& ap)//拷贝构造
{
_ptr=ap._ptr;
ap._ptr=NULL;
}
AutoPtr<T>& operator=(AutoPtr<T>& ap)
{
if(this!=&ap)
{
delete _ptr;
_ptr=ap._ptr;
ap._ptr=NULL;
}
return *this;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
~AutoPtr()
{
cout<<"~AutoPtr"<<endl;
delete _ptr;
}
protected:
T* _ptr;
};
void test1()
{
int* p1=new int;
AutoPtr<int> ap1;
AutoPtr<int> ap2(ap1);
ap2=ap1;
}
再来说说auto_ptr的缺点,先褒后贬!!!
auto_ptr的缺陷:只能由一个对象来管理空间当多个auto_ptr指向同一块空间时,会由于多次释放而导致崩溃。
2.scopedptr——容易实现的智能指针
scoped_ptr独占所有权,防拷贝,只要将其拷贝构造和赋值运算符重载声明为私有。
模拟实现scoped_ptr
//独占所有权,防拷贝
#include<iostream>
using namespace std;
template<class T>
class ScopedPtr
{
public:
ScopedPtr()
:_ptr(NULL)
{}
ScopedPtr(T* ptr)//构造函数
:_ptr(ptr)
{}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
~ScopedPtr()
{
cout<<"~ScopedPtr"<<endl;
delete _ptr;
}
protected:
ScopedPtr(ScopedPtr<T>& ap);//拷贝构造
ScopedPtr<T>& operator=(ScopedPtr<T>& ap);//赋值运算符重载
protected:
T* _ptr;
};
void test2()
{
ScopedPtr<int> p1;
}
3.shared_ptr–引用计数
shared_ptr避免了双重释放而引起的问题。
模拟实现shared_ptr
//共享所有权,引用计数
#include<iostream>
using namespace std;
template<class T>
class SharedPtr
{
public:
SharedPtr()
:_ptr(NULL)
,_pCount(NULL)
{}
SharedPtr(T* p)
:_ptr(p._ptr)
,_pCount(NULL)
{
if (_ptr!= NULL)
{
_pCount = new int[1];
}
}
SharedPtr(SharedPtr<T>& p)
:_ptr(p._ptr)
,_pCount(p._pCount)
{
if(_ptr!=NULL)
{
++(*_pCount);
}
sp._ptr=NULL;
}
SharedPtr<T>& operator=(SharedPtr<T>& p)
{
if (this != &p)
{
//没有管理空间
if(_pCount==NULL)
{
_ptr=p._ptr;
_pCount=p._pCount;
if(_pCount!=NULL)
{
++(*_pCount);
}
}
else if(*_pCount==1)//独自管理一段空间
{
delete[] _ptr;
delete[] _pCount;
_ptr=p._ptr;
_pCount=p._pCount;
if(_pCount==NULL)
{
++(*_pCount);
}
}
else //共享空间
{
--(*_pCount);
_ptr = p._ptr;
_pCount = p._pCount;
if (_pCount != NULL)
++(*_pCount);
}
}
return *this;
}
private:
T* _ptr;
int* _pCount;
};
void test3()
{
SharedPtr<int> p1;
SharedPtr<int> p2;
p2=p1;
}
3.shared_ptr依然有问题,循环引用
template<typename T>
class Base
{
public:
Base(const T& data)
: _data(data)
{
cout << "Base()" << endl;
}
~Base()
{
cout << "~Base" << endl;
}
shared_ptr<Base<T>> _next;
shared_ptr<Base<T>> _pre;
T _data;
};
int main()
{
//test1();
//test2();
//test3();
shared_ptr<Base<int>> s1(new Node<int>(1));
shared_ptr<Base<int>> s2(new Node<int>(2));
s1->_next = s2;
s2->_pre = s1;
return 0;
}
以上代码就会有循环引用的问题,为了解决循环引用问题提出了weak_ptr,我们要将
shared_ptr<Base<int>> s1(new Node<int>(1));
shared_ptr<Base<int>> s2(new Node<int>(2));
换为
weakptr<Base<int>> s1(new Node<int>(1));
weak_ptr<Base<int>> s2(new Node<int>(2));
weak_ptr可以包含由shared_ptr管理的内存的引用。但不会拥有这个内存,所以不能阻止shared_ptr释放内存。weak_ptr离开作用域时不会销毁它指向的内存;然而它可以用来判断内存是否已经被关联的shared_ptr释放了。(书上总结的)