auto_ptr是常用的智能指针,其实现很简单,源代码也很短,但是中间有个代理类auto_ptr_ref用的很巧妙,值得学习。
/*
* Copyright (c) 1997-1999
* Silicon Graphics Computer Systems, Inc.
*
* Copyright (c) 1999
* Boris Fomitchev
*
* This material is provided "as is", with absolutely no warranty expressed
* or implied. Any use is at your own risk.
*
* Permission to use or copy this software for any purpose is hereby granted
* without fee, provided the above notices are retained on all copies.
* Permission to modify the code and to distribute modified code is granted,
* provided the above notices are retained, and a notice that the code was
* modified is included with the above copyright notice.
*
*/
#ifndef _STLP_AUTO_PTR_H
#define _STLP_AUTO_PTR_H
_STLP_BEGIN_NAMESPACE
// implementation primitive
//auto_ptr基类,声明指针和指针初始化操作
class __ptr_base {
public:
void* _M_p;
void __set(const volatile void* p) { _M_p = __CONST_CAST(void*,p); }
void __set(void* p) { _M_p = p; }
};
//使用auto_ptr_ref,在源代码后面举例说明
template <class _Tp>
class auto_ptr_ref {
public:
__ptr_base& _M_r;
_Tp* const _M_p;
//_M_r是引用,_M_p是常量指针,在构造函数初始化列表初始化
auto_ptr_ref(__ptr_base& __r, _Tp* __p) : _M_r(__r), _M_p(__p) { }
//释放,把基类_M_r指针设为空
_Tp* release() const { _M_r.__set(__STATIC_CAST(void*, 0)); return _M_p; }
private:
//explicitely defined as private to avoid warnings:
//显示定义,避免警告。
//重载“=”为私有函数,不允许调用
typedef auto_ptr_ref<_Tp> _Self;
_Self& operator = (_Self const&);
};
template<class _Tp>
class auto_ptr : public __ptr_base {
public:
typedef _Tp element_type;
typedef auto_ptr<_Tp> _Self;
_Tp* release() _STLP_NOTHROW {
_Tp* __px = this->get();
this->_M_p = 0;
return __px;
}
void reset(_Tp* __px = 0) _STLP_NOTHROW {
_Tp* __pt = this->get();
if (__px != __pt)
delete __pt;
this->__set(__px);
}
_Tp* get() const _STLP_NOTHROW
#if !defined (__GNUC__) || (__GNUC__ > 2)
//像C语言一样,用static_cast<>
{ return __STATIC_CAST(_Tp*, _M_p); }
#else
//像C++一样用reinterpret
{ return __REINTERPRET_CAST(_Tp*, _M_p); }
#endif
#if !defined (_STLP_NO_ARROW_OPERATOR)
//返回指针
_Tp* operator->() const _STLP_NOTHROW {
//断言,确保返回指针不是空指针
_STLP_VERBOSE_ASSERT(get() != 0, _StlMsg_AUTO_PTR_NULL)
return get();
}
#endif
//返回引用
_Tp& operator*() const _STLP_NOTHROW {
//断言,确保指针不是空指针,这样才可以解引用
_STLP_VERBOSE_ASSERT(get() != 0, _StlMsg_AUTO_PTR_NULL)
return *get();
}
//显示构造函数,不允许隐式调用
explicit auto_ptr(_Tp* __px = 0) _STLP_NOTHROW { this->__set(__px); }
/*
复制构造函数,需要注意的是复制构造函数是auto_ptr传递的是指针的所有权。
因为要修改传入参数的所有权,所以传入参数不是const(不同于其他复制构造函数)
auto_ptr<int>p1(new int(10));
auto_ptr<int>p2(p1);
p2指向new int(10)后,p1就成了空指针,他们不能共享所有权,因此auto_ptr不能作为
容器元素,因为容器元素要支持拷贝和复制
*/
#if defined (_STLP_MEMBER_TEMPLATES)
# if !defined (_STLP_NO_TEMPLATE_CONVERSIONS)
template<class _Tp1> auto_ptr(auto_ptr<_Tp1>& __r) _STLP_NOTHROW {
_Tp* __conversionCheck = __r.release();
this->__set(__conversionCheck);
}
# endif
template<class _Tp1> auto_ptr<_Tp>& operator=(auto_ptr<_Tp1>& __r) _STLP_NOTHROW {
_Tp* __conversionCheck = __r.release();
reset(__conversionCheck);
return *this;
}
#endif
//同类型auto_ptr作为复制构造函数参数,不用模板
auto_ptr(_Self& __r) _STLP_NOTHROW { this->__set(__r.release()); }
//赋值操作符和复制构造函数类型,传递的是所有权,不是值传递
_Self& operator=(_Self& __r) _STLP_NOTHROW {
reset(__r.release());
return *this;
}
//析构函数很简单,只是调用指针指向对象的析构函数,释放指针指向的空间
//delete而不是delete[],所以auto_ptr不能指向数组
~auto_ptr() _STLP_NOTHROW { /* boris : reset(0) might be better */ delete this->get(); }
//使用auto_ptr_ref,在源代码后面举例说明
auto_ptr(auto_ptr_ref<_Tp> __r) _STLP_NOTHROW
{ this->__set(__r.release()); }
_Self& operator=(auto_ptr_ref<_Tp> __r) _STLP_NOTHROW {
reset(__r.release());
return *this;
}
#if defined(_STLP_MEMBER_TEMPLATES) && !defined(_STLP_NO_TEMPLATE_CONVERSIONS)
template<class _Tp1> operator auto_ptr_ref<_Tp1>() _STLP_NOTHROW
{ return auto_ptr_ref<_Tp1>(*this, this->get()); }
template<class _Tp1> operator auto_ptr<_Tp1>() _STLP_NOTHROW
{ return auto_ptr<_Tp1>(release()); }
#else
operator auto_ptr_ref<_Tp>() _STLP_NOTHROW
{ return auto_ptr_ref<_Tp>(*this, this->get()); }
#endif
};
_STLP_END_NAMESPACE
#endif /* _STLP_AUTO_PTR_H */
// Local Variables:
// mode:C++
// End:
auto_ptr是智能指针。在赋值(复制构造函数或赋值操作符)的时候是传递指针所用权,而不是传递值。所以在复制构造函数或复制操作符函数要取消原来指针的所有权,因此就不能把传入的参数声明为const类型。但是有些情况下,必须要声明为const类型,例如
auto_ptr<int> p(auto_ptr<int>(new int(10));
使用临时对象时,必须用const修饰
auto_ptr(auto_prt<int> const&),而auto_ptr要修改原来指针的所有权,声明成了auto_ptr(auto_prt<int> &),上面代码不能通过编译。
再例如
auto_ptr<int> p1(NULL);
p1=(auto_ptr<int>(new int(10));
有和上面一样的问题,临时对象是右值,非const&不能指向右值。
这样情况下就引入了auto_ptr_ref,auto_ptr可以隐式转换为auto_ptr_ref,这样上面的程序就不会出错。