STL赏析——auto_ptr

先放代码

auto_ptr
  1 /*

  2  * Copyright (c) 1997-1999

  3  * Silicon Graphics Computer Systems, Inc.

  4  *

  5  * Permission to use, copy, modify, distribute and sell this software

  6  * and its documentation for any purpose is hereby granted without fee,

  7  * provided that the above copyright notice appear in all copies and

  8  * that both that copyright notice and this permission notice appear

  9  * in supporting documentation.  Silicon Graphics makes no

 10  * representations about the suitability of this software for any

 11  * purpose.  It is provided "as is" without express or implied warranty.

 12  *

 13  */

 14 

 15 #ifndef __SGI_STL_MEMORY

 16 #define __SGI_STL_MEMORY

 17 

 18 #include <stl_algobase.h>

 19 #include <stl_alloc.h>

 20 #include <stl_construct.h>

 21 #include <stl_tempbuf.h>

 22 #include <stl_uninitialized.h>

 23 #include <stl_raw_storage_iter.h>

 24 

 25 

 26 __STL_BEGIN_NAMESPACE

 27 

 28 #if defined(__SGI_STL_USE_AUTO_PTR_CONVERSIONS) && \

 29     defined(__STL_MEMBER_TEMPLATES)

 30 

 31 template<class _Tp1> struct auto_ptr_ref {

 32   _Tp1* _M_ptr;

 33   auto_ptr_ref(_Tp1* __p) : _M_ptr(__p) {}

 34 };

 35 

 36 #endif

 37 

 38 template <class _Tp> class auto_ptr {

 39 private:

 40   _Tp* _M_ptr;

 41 

 42 public:

 43   typedef _Tp element_type;

 44 

 45   explicit auto_ptr(_Tp* __p = 0) __STL_NOTHROW : _M_ptr(__p) {}

 46   auto_ptr(auto_ptr& __a) __STL_NOTHROW : _M_ptr(__a.release()) {}

 47 

 48 #ifdef __STL_MEMBER_TEMPLATES

 49   template <class _Tp1> auto_ptr(auto_ptr<_Tp1>& __a) __STL_NOTHROW

 50     : _M_ptr(__a.release()) {}

 51 #endif /* __STL_MEMBER_TEMPLATES */

 52 

 53   auto_ptr& operator=(auto_ptr& __a) __STL_NOTHROW {

 54     if (&__a != this) {

 55       delete _M_ptr;

 56       _M_ptr = __a.release();

 57     }

 58     return *this;

 59   }

 60 

 61 #ifdef __STL_MEMBER_TEMPLATES

 62   template <class _Tp1>

 63   auto_ptr& operator=(auto_ptr<_Tp1>& __a) __STL_NOTHROW {

 64     if (__a.get() != this->get()) {

 65       delete _M_ptr;

 66       _M_ptr = __a.release();

 67     }

 68     return *this;

 69   }

 70 #endif /* __STL_MEMBER_TEMPLATES */

 71 

 72   // Note: The C++ standard says there is supposed to be an empty throw

 73   // specification here, but omitting it is standard conforming.  Its 

 74   // presence can be detected only if _Tp::~_Tp() throws, but (17.4.3.6/2)

 75   // this is prohibited.

 76   ~auto_ptr() { delete _M_ptr; }

 77 

 78   _Tp& operator*() const __STL_NOTHROW {

 79     return *_M_ptr;

 80   }

 81   _Tp* operator->() const __STL_NOTHROW {

 82     return _M_ptr;

 83   }

 84   _Tp* get() const __STL_NOTHROW {

 85     return _M_ptr;

 86   }

 87   _Tp* release() __STL_NOTHROW {

 88     _Tp* __tmp = _M_ptr;

 89     _M_ptr = 0;

 90     return __tmp;

 91   }

 92   void reset(_Tp* __p = 0) __STL_NOTHROW {

 93     if (__p != _M_ptr) {

 94       delete _M_ptr;

 95       _M_ptr = __p;

 96     }

 97   }

 98 

 99   // According to the C++ standard, these conversions are required.  Most

100   // present-day compilers, however, do not enforce that requirement---and, 

101   // in fact, most present-day compilers do not support the language 

102   // features that these conversions rely on.

103   

104 #if defined(__SGI_STL_USE_AUTO_PTR_CONVERSIONS) && \

105     defined(__STL_MEMBER_TEMPLATES)

106 

107 public:

108   auto_ptr(auto_ptr_ref<_Tp> __ref) __STL_NOTHROW

109     : _M_ptr(__ref._M_ptr) {}

110 

111   auto_ptr& operator=(auto_ptr_ref<_Tp> __ref) __STL_NOTHROW {

112     if (__ref._M_ptr != this->get()) {

113       delete _M_ptr;

114       _M_ptr = __ref._M_ptr;

115     }

116     return *this;

117   }

118 

119   template <class _Tp1> operator auto_ptr_ref<_Tp1>() __STL_NOTHROW 

120     { return auto_ptr_ref<_Tp1>(this->release()); }

121   template <class _Tp1> operator auto_ptr<_Tp1>() __STL_NOTHROW

122     { return auto_ptr<_Tp1>(this->release()); }

123 

124 #endif /* auto ptr conversions && member templates */

125 };

126 

127 __STL_END_NAMESPACE

128 

129 #endif /* __SGI_STL_MEMORY */

130 

131 

132 // Local Variables:

133 // mode:C++

134 // End:

auto_ptr是资源持有型的指针工具,只支持自动释放功能。需要注意的是

1. 不要多个auto_ptr共同支持有一个资源。

2.不要对被赋值后的auto_ptr引用,那样将非法无效。

3.auto_ptr不是copyable对象,所以不要在容器中使用。

4.auto_ptr不支持数组形式。

首先说明关于auto_ptr_ref放在最后再说~

私有数据域,只有一个即_M_ptr指针。

然后轻量级抽象数据类型(STL商用手法)。

typedef _Tp element_type.

第一个构造函数

explicit auto_ptr(_Tp* __p = 0) __STL_NOTHROW : _M_ptr(__p) {}

注意技巧:

1. explicit防止了,不必要的隐式数据转换。

2. 加上了默认参数,这样允许无参数构造。

3.__STL_NOTHROW 其实就是 throw().注意此处作用,只是告诉调用者,该函数不会抛出异常,而实现者需要注意不要在此抛出异常。

第二个构造函数

auto_ptr(auto_ptr& __a) __STL_NOTHROW : _M_ptr(__a.release()) {}

由于是资源持有型的特点,所以参数需要释放持有权,向资源传给当前指针。

 

然后是member template的构造函数。

#ifdef __STL_MEMBER_TEMPLATES
template <class _Tp1> auto_ptr(auto_ptr<_Tp1>& __a) __STL_NOTHROW
: _M_ptr(__a.release()) {}

前提是编译器支持这种功能。关于member template,我在pair的伤心中又提到过。

 

然后是2个赋值重载,第二个也是member template的。

auto_ptr& operator=(auto_ptr& __a) __STL_NOTHROW {
if (&__a != this) {
delete _M_ptr;
_M_ptr = __a.release();
}
return *this;
}

#ifdef __STL_MEMBER_TEMPLATES
template <class _Tp1>
auto_ptr& operator=(auto_ptr<_Tp1>& __a) __STL_NOTHROW {
if (__a.get() != this->get()) {
delete _M_ptr;
_M_ptr = __a.release();
}
return *this;
}
#endif /* __STL_MEMBER_TEMPLATES */

注意事项:

1.两者的不同,前者是判断引用两者地址是否相同,后者是判断两者引用对象是否相同。//这里没太明白区别两者的作用。

2.赋值前,要释放原有资源,防止内存泄漏。

3.返回该对象引用(operator = 要求)。

 

然后是几个非常naive,非常易懂的函数。

~auto_ptr() { delete _M_ptr; }

_Tp& operator*() const __STL_NOTHROW {
return *_M_ptr;
}
_Tp* operator->() const __STL_NOTHROW {
return _M_ptr;
}
_Tp* get() const __STL_NOTHROW {
return _M_ptr;
}
_Tp* release() __STL_NOTHROW {
_Tp* __tmp = _M_ptr;
_M_ptr = 0;
return __tmp;
}
void reset(_Tp* __p = 0) __STL_NOTHROW {
if (__p != _M_ptr) {
delete _M_ptr;
_M_ptr = __p;
}
}

特别需要注意的是:

1.是用release函数,该指针释只放了资源的持有权,并没有释放资源,所以请注意,如果release到一个指针上,请注意自己释放资源。

2.reset函数 至于判断当前持有资源和要reset的资源是否相等,如果相等,不要释放,否则会出错。

 

接下来是几个member template函数。

public:
auto_ptr(auto_ptr_ref<_Tp> __ref) __STL_NOTHROW
: _M_ptr(__ref._M_ptr) {}

auto_ptr& operator=(auto_ptr_ref<_Tp> __ref) __STL_NOTHROW {
if (__ref._M_ptr != this->get()) {
delete _M_ptr;
_M_ptr = __ref._M_ptr;
}
return *this;
}

template <class _Tp1> operator auto_ptr_ref<_Tp1>() __STL_NOTHROW
{ return auto_ptr_ref<_Tp1>(this->release()); }
template <class _Tp1> operator auto_ptr<_Tp1>() __STL_NOTHROW
{ return auto_ptr<_Tp1>(this->release()); }

#endif /* auto ptr conversions && member templates */

其中包括了 auto_ptr_ref的转换等重要工作。

我们先看看auto_ptr_ref的定义。

非常轻量简单,也非常使用。

template<class _Tp1> struct auto_ptr_ref {
_Tp1* _M_ptr;
auto_ptr_ref(_Tp1* __p) : _M_ptr(__p) {}
};

只有一个指针域指向对应的auto_ptr。

但是注意,该函数没有指定一定要指向auto_ptr……

如果仔细看一下第二个构造函数,我们发现 他不是拷贝构造函数!因为少了一个const,因为auto_ptr的定义资源持有型,所以该函数不可以又传统的(const&)的拷贝构造函数。而非常量引用不能绑定右值,这样就导致auto_ptr的语义缺失。

众所周知,没有优质绑定将导致一系列的语法不能使用。

例如说:

auto_ptr<int> f();

auto_ptr ptr(f())无法使用。诸如此类的问题。

所以就提出了auto_ptr_ref,他的工作方式就是提供一个间接转换过程,从auto_ptr到auto_ptr_ref到auto_ptr.

于是乎,便又一个上述的隐式类型转换。

我们以

auto_ptr<int> f();

auto_ptr ptr(f())为例,简单说明一下auto_ptr_ref的工作原理。

f() 返回一个一个auto_ptr但是由于 auto_ptr的没有const 拷贝构造函数,没法进行右值引用,而有

auto_ptr(auto_ptr_ref<_Tp> __ref) __STL_NOTHROW
: _M_ptr(__ref._M_ptr) {}

以及

template <class _Tp1> operator auto_ptr_ref<_Tp1>() __STL_NOTHROW
{ return auto_ptr_ref<_Tp1>(this->release()); }

所以完成了一次右值转换。从右值只有的资源释放给auto_ptr_ref,auto_ptr_ref存在在堆栈中,右值auto_ptr销毁。

调用匹配的构造函数auto_ptr(auto_ptr_ref<_Tp> __ref),完成资源转让,完成构造。

该方式十分cool,十分巧妙。非常间接的解决了右值引用的问题。

你可能感兴趣的:(auto)