通常是经由类模板来实现。借由模板来达成泛型,通常借由类的析构函数来达成自动释放指针所指向的内存或对象。
智能指针的最初动机是使得下面的代码更安全,最初使用
void foo()
{
Type* ptr = new Type[10];
// 对p指向的内存块进行赋值
do_something();
delete[] ptr;
}
当上面的foo()函数出现异常时:
void foo()
{
MyClass* p;
try {
p = new MyClass;
p->DoSomething();
delete p;
}
catch (...) {
delete p;
throw;
}
}
当程序代码越来越长时,程序代码会显得臃肿不堪
auto_ptr:
在使用auto_ptr时,须十分注意资源所有权的概念。auto_ptr是现在标准库里面一个轻量级的智能指针的实现,存在于头文件 memory中,之所以说它是轻量级,是因为它只有一个成员变量(拥有对象的指针),相关的调用开销也非常小。
当有了智能指针时我们可以将上面的代码进行如下改写:
void foo()
{
auto_ptr<MyClass> p(new MyClass);
p->DoSomething();
}
下面我们来看看auto_ptr实现了些什么:(以下的auto_ptr的声明摘自ISO/IEC 14882, section 20.4.5:)
namespace std {
template <class Y> struct auto_ptr_ref {};
template <class X>
class auto_ptr {
public:
typedef X element_type;
// 20.4.5.1 construct/copy/destroy:
explicit auto_ptr(X* p =0) throw();
auto_ptr(auto_ptr&) throw();
template <class Y> auto_ptr(auto_ptr<Y>&) throw();
auto_ptr& operator=(auto_ptr&) throw();
template <class Y> auto_ptr& operator=(auto_ptr<Y>&) throw();
auto_ptr& operator=(auto_ptr_ref<X>) throw();
~auto_ptr() throw();
// 20.4.5.2 members:
X& operator*() const throw();
X* operator->() const throw();
X* get() const throw();
X* release() throw();
void reset(X* p =0) throw();
// 20.4.5.3 conversions:
auto_ptr(auto_ptr_ref<X>) throw();
template <class Y> operator auto_ptr_ref<Y>() throw();
template <class Y> operator auto_ptr<Y>() throw();
};
}
再来看看赋值运算符的重载和拷贝构造的写法:
//拷贝构造
auto_ptr(_Myt& _Right) _THROW0()
: _Myptr(_Right.release())
{}
auto_ptr(auto_ptr_ref<_Ty> _Right) _THROW0()
{
_Ty *_Ptr = _Right._Ref;
_Right._Ref = 0; // release old
_Myptr = _Ptr; // reset this
}
//赋值
_Myt& operator=(_Myt& _Right) _THROW0()
{ // assign compatible _Right (assume pointer)
reset(_Right.release());
return (*this);
}
_Myt& operator=(auto_ptr_ref<_Ty> _Right) _THROW0()
{
// assign compatible _Right._Ref (assume pointer)
_Ty *_Ptr = _Right._Ref;
_Right._Ref = 0; // release old
reset(_Ptr); // set new
return (*this);
}
//release
_Ty *release() _THROW0()
{
_Ty *_Tmp = _Myptr;
_Myptr = 0;
return (_Tmp);
}
eg1:
MyClass* p(new MyClass);
MyClass* q = p;
delete p;
p->DoSomething();
p = NULL;
q->DoSomething();
可以看出:auto_ptr只是简单的把新构造的对象q 指向了p 的内存空间。delete p 之后 p 可能依然指向某块内存(悬挂的)但是却是无效的指针。
eg2:
void main()
{
auto_ptr<MyClass> p(new MyClass);
auto_ptr<MyClass> q(p);
}
eg3:
void main()
{
auto_ptr<MyClass> p(new MyClass);
auto_ptr<MyClass> q(new MyClass);
{
q = p;
}
//恢复对p的操作,就找不到p
}
q指向了p的内存,出作用域后先析构q,p指向空然而这时的p已经找不到了,导致p依然指向某块内存,但却是无效的指针。
简洁版auto_ptr:
template <class T>
class SmartPtr
{
public :`
SmartPtr(T * ptr = NULL)
: _ptr(ptr )
{}
~ SmartPtr()
{
if (_ptr )
{
delete _ptr ;
}
}
SmartPtr(SmartPtr & ap)
: _ptr(ap ._ptr)
{
ap._ptr = 0;
}
SmartPtr& operator =(SmartPtr& ap)
{
if (this != &ap)
{
_ptr = ap ._ptr;
ap._ptr = 0;
}
return *this ;
}
T* GetPtr()
{
return _ptr;
}
T& operator * ()
{
return *_ptr ;
}
T* operator -> ()
{
return _ptr ;
}
private :
T* _ptr ;
};
void Test1 ()
{
SmartPtr<int > p1 = new int (10);
SmartPtr<int > p2 = p1;
SmartPtr<int > p3;
p3 = p2 ;
// *p1 = 4;
}
auto_ptr的几点注意事项:
1、auto_ptr不能共享所有权
2、auto_ptr不能指向数组
3、auto_ptr不能作为容器的成员
4、不能通过复制操作来初始化auto_ptr
std::auto_ptr p(new int(42)); //OK
std::atuo_ptrp = new int(42);//Error
这是因为auto_ptr的构造函数被定义了explicit
5、不要把auto_ptr放入容器
对象复制给另一个 auto_ptr p2对象以后,p1则被置空,如果访问p1,则会出现非法访问, auto_ptr 的接口设计存在缺陷!
uniqu-ptr(socped_ptr):
uniqu_ptr的拷贝构造函数和赋值运算符都声明为deleted,也就是说它不能被拷贝,只能通过std::move来转递它所指向的内存的所有权。
std::unique_ptr<int> p1(new int(5));
std::unique_ptr<int> p2 = p1; // 编译会出错
std::unique_ptr<int> p3 = std::move (p1); // 转移所有权,现在那块内存归p3所有, p1成为无效的指针。
p3.reset(); //释放内存。
p1.reset(); //实际上什么都没做。
std::auto_ptr依然存在,但在C++11中被标为"弃用".
简洁版unique_ptr:
template <class T>
class SmartPtr
{
public :
SmartPtr(T * ptr = NULL)
: _ptr(ptr )
{}
~ SmartPtr()
{
if (_ptr )
{
delete _ptr ;
}
}
T* GetPtr()
{
return _ptr ;
}
T& operator * ()
{
return *_ptr ;
}
T* operator -> ()
{
return _ptr ;
}
private :
SmartPtr(SmartPtr & ap);
SmartPtr& operator =(SmartPtr& ap);
//!=
//== 拷贝构造 和赋值 不被允许 则这两个也没有意义
private :
T* _ptr ;
};
void Test1 ()
{
SmartPtr<int > p1 = new int (10);
//SmartPtr<int> p2 = p1;
//SmartPtr<int> p3;
//p3 = p2;
}
shared_ptr
shared_ptr对象除了包括一个所拥有对象的指针(px)外,还必须包括一个引用计数代理对象(shared_count)的指针(pn)。而这个引用计数代理对象包括一个真正的多态的引用计数对象(sp_counted_base)的指针(_pi),真正的引用计数对象在使用VC编译器的情况下包括一个虚表,一个虚表指针,和两个计数器
shared_ptr完美解决auto_ptr在对象所有权上的局限性(auto_ptr是独占的),在使用引用计数的机制上提供了可以共享所有权的智能指针。
template <class T>
class SmartPtr
{
public :
SmartPtr(T* ptr = NULL)
: _ptr(ptr )
, _countPtr(new int(1))
{}
~ SmartPtr()
{
Release();
}
SmartPtr(const SmartPtr& ap)
: _ptr(ap ._ptr)
, _countPtr(ap ._countPtr)
{
++ _countPtr[0];
}
SmartPtr& operator =(const SmartPtr& ap )
{
if (this != &ap)
{
this->Release ();
_ptr = ap ._ptr;
_countPtr = ap ._countPtr;
++ _countPtr[0];
}
return *this ;
}
T* GetPtr ()
{
return _ptr ;
}
int GetCount ()
{
return *_countPtr ;
}
T& operator * ()
{
return *_ptr ;
}
T* operator -> ()
{
return _ptr ;
}
protected :
void Release ()
{
if (--_countPtr [0] == 0)
{
delete _countPtr ;
if (_ptr )
{
delete _ptr ;
}
}
}
private :
T* _ptr ;
int* _countPtr ;
};
void Test1 ()
{
SmartPtr<int > p1 = new int (1);
cout<<"p1->count:" <<p1. GetCount()<<endl ;
SmartPtr<int > p2 = p1;
cout<<"p1->count:" <<p1. GetCount()<<" " ;
cout<<"p2->count:" <<p2. GetCount()<<endl ;
SmartPtr<int > p3;
p3 = p2 ;
cout<<"p1->count:" <<p1. GetCount()<<" " ;
cout<<"p2->count:" <<p2. GetCount()<<" " ;
cout<<"p3->count:" <<p3. GetCount()<<endl ;
}
还有shared_array
template <class T>
class SmartArrayPtr
{
public :
SmartArrayPtr(T * ptr = NULL)
: _ptr(ptr )
, _countPtr(new int(1))
{}
~ SmartArrayPtr()
{
Release();
}
SmartArrayPtr(const SmartArrayPtr& ap)
: _ptr(ap ._ptr)
, _countPtr(ap ._countPtr)
{
++ _countPtr[0];
}
SmartArrayPtr& operator =(const SmartArrayPtr& ap)
{
if (this != &ap)
{
this->Release ();
_ptr = ap ._ptr;
_countPtr = ap ._countPtr;
++ _countPtr[0];
}
return *this ;
}
T* GetPtr ()
{
return _ptr ;
}
int GetCount ()
{
return *_countPtr ;
}
T& operator [](size_t index)
{
return _ptr [index];
}
protected :
void Release ()
{
if (--_countPtr [0] == 0)
{
delete _countPtr ;
if (_ptr )
{
delete[] _ptr ;
}
}
}
private :
T* _ptr ;
int* _countPtr ;
};
void Test2 ()
{
SmartArrayPtr<int > p1 = new int [20];
cout<<"p1->count:" <<p1. GetCount()<<endl ;
SmartArrayPtr<int > p2 = p1;
cout<<"p1->count:" <<p1. GetCount()<<" " ;
cout<<"p2->count:" <<p2. GetCount()<<endl ;
SmartArrayPtr<int > p3;
p3 = p2 ;
cout<<"p1->count:" <<p1. GetCount()<<" " ;
cout<<"p2->count:" <<p2. GetCount()<<" " ;
cout<<"p3->count:" <<p3. GetCount()<<endl ;
p1[1] = 1;
p3[2] = 2;
cout<<p1 [1]<<" "<< p1[2]<<endl ;
}