最近开了auto_ptr,看了它的很多的应用与规则,有些迷惑的地方,故拿来它的源码研究一翻:
- template<typename _Tp1>
- struct auto_ptr_ref
- {
- _Tp1* _M_ptr;
- explicit
- auto_ptr_ref(_Tp1* __p): _M_ptr(__p) { }
- };
- template<typename _Tp>
- class auto_ptr
- {
- private:
- _Tp* _M_ptr;
- public:
- /// The pointed-to type.
- typedef _Tp element_type;
- explicit
- auto_ptr(element_type* __p = 0) throw() : _M_ptr(__p) { }
- auto_ptr(auto_ptr& __a) throw() : _M_ptr(__a.release()) { }
- template<typename _Tp1>
- auto_ptr(auto_ptr<_Tp1>& __a) throw() : _M_ptr(__a.release()) { }
- auto_ptr&
- operator=(auto_ptr& __a) throw()
- {
- reset(__a.release());
- return *this;
- }
- template<typename _Tp1>
- auto_ptr&
- operator=(auto_ptr<_Tp1>& __a) throw()
- {
- reset(__a.release());
- return *this;
- }
- ~auto_ptr() { delete _M_ptr; }
- element_type&
- operator*() const throw()
- {
- _GLIBCXX_DEBUG_ASSERT(_M_ptr != 0);
- return *_M_ptr;
- }
- element_type*
- operator->() const throw()
- {
- _GLIBCXX_DEBUG_ASSERT(_M_ptr != 0);
- return _M_ptr;
- }
- element_type*
- get() const throw() { return _M_ptr; }
- element_type*
- release() throw()
- {
- element_type* __tmp = _M_ptr;
- _M_ptr = 0;
- return __tmp;
- }
- void
- reset(element_type* __p = 0) throw()
- {
- if (__p != _M_ptr)
- {
- delete _M_ptr;
- _M_ptr = __p;
- }
- }
- auto_ptr(auto_ptr_ref<element_type> __ref) throw()
- : _M_ptr(__ref._M_ptr) { }
- auto_ptr&
- operator=(auto_ptr_ref<element_type> __ref) throw()
- {
- if (__ref._M_ptr != this->get())
- {
- delete _M_ptr;
- _M_ptr = __ref._M_ptr;
- }
- return *this;
- }
- template<typename _Tp1>
- operator auto_ptr_ref<_Tp1>() throw()
- { return auto_ptr_ref<_Tp1>(this->release()); }
- template<typename _Tp1>
- operator auto_ptr<_Tp1>() throw()
- { return auto_ptr<_Tp1>(this->release()); }
- };
1 首先看一下开始的哪个小结构体
- template<typename _Tp1>
- struct auto_ptr_ref
- {
- _Tp1* _M_ptr;
- explicit
- auto_ptr_ref(_Tp1* __p): _M_ptr(__p) { }
- };
这个结构体成员中仅有一个指针和一个显示构造函数。仅从表面上来看,它封装了一个指针类型,从源码来看,它提供了一种转换,即:从这种结构体类型到auto_ptr的转换。
2 类型定义
- typedef _Tp element_type;
为auto_ptr定一个成员类型。我们可以如下方法使用这种类型:
- #include <iostream>
- #include <memory>
- #include <typeinfo>
- #include <string>
- using namespace std;
- int main()
- {
- auto_ptr<int>::element_type p = 6;
- cout << p << endl;
- cout << typeid(p).name() << endl;
- system("pause");
- return 0;
- }
在程序中auto_ptr<int>::element_type 其实就是int类型。我们在程序中也验证了。
3 构造函数
- explicit
- auto_ptr(element_type* __p = 0) throw() : _M_ptr(__p) { }
构造函数被定义为explicit,那么我们在定义对象的时候的存在隐式转换的定义就无法通过编译了。如:
- auto_ptr<int> p = new int(6); //error
- auto_ptr<int> p (new int(6)); // ok;
4 拷贝构造函数
- auto_ptr(auto_ptr& __a) throw() : _M_ptr(__a.release()) { }
- template<typename _Tp1>
- auto_ptr(auto_ptr<_Tp1>& __a) throw() : _M_ptr(__a.release()) { }
拷贝构造函数被定义了两个版本,一个模板版本,一个非模板版本。为什么这样做呢?这不禁令我想起了模板的一个性质,即,当一个函数同时存在模板版本和非模板版本的时候,在函数进行调用的时候,编译器首先拿实参和非模板版本来进行比较,如果匹配就优先调用非模板版本。而不去再实例化一个版本。只有类型和非模板版本的参数类型不完全匹配或是不匹配的时候才去实例化一个新版本。
这里提供一个模板版本的目的是为了能够允许类型自动转换从而构造处合适的auto_ptr。例如:可以根据一个派生类对象构造出一个基类对像的auto_ptr。
4 赋值函数
- auto_ptr&
- operator=(auto_ptr& __a) throw()
- {
- reset(__a.release());
- return *this;
- }
- template<typename _Tp1>
- auto_ptr&
- operator=(auto_ptr<_Tp1>& __a) throw()
- {
- reset(__a.release());
- return *this;
- }
这个函数同样也有两个版本,其意义和拷贝构造函数的相同。由于构造函数被声明成为explicit。故这样的行为也无法通过编译:
- auto_ptr<int> p (new int(5));
- auto_ptr<int> q;
- q = p; // ok.
- q = new int (6); //error
- q = auto_ptr<int>(new int(6)); // ok;
5 析构函数
- ~auto_ptr() { delete _M_ptr; }
析构函数简单,仅仅释放空间就ok了。
6 操作符重载
- element_type&
- operator*() const throw()
- {
- _GLIBCXX_DEBUG_ASSERT(_M_ptr != 0);
- return *_M_ptr;
- }
- element_type*
- operator->() const throw()
- {
- _GLIBCXX_DEBUG_ASSERT(_M_ptr != 0);
- return _M_ptr;
- }
这里重载了两个操作符,一个‘*’ 一个‘->’。这里面使用了断言。我猜测它_GLIBCXX_DEBUG_ASSERT就是assert。其实这样个函数的目的一个是为了得到指针所指的内容,一个是得到指针。
7 三个小函数
- element_type*
- get() const throw() { return _M_ptr; }
- element_type*
- release() throw()
- {
- element_type* __tmp = _M_ptr;
- _M_ptr = 0;
- return __tmp;
- }
- void
- reset(element_type* __p = 0) throw()
- {
- if (__p != _M_ptr)
- {
- delete _M_ptr;
- _M_ptr = __p;
- }
- }
get()函数就是为了获得封装的指针。release()函数其实也是一个封装操作,他这里干了两件事,一个是返回对象封装的指针,然后把该指针赋0.它的目的是释放现在的指针,令它指向0,并传递指针的地址。reset()函数是用来设置指针。首先判断当前指针是否有值,若有值释放之,并赋值新值。
8 和auto_ptr_ref有关的函数
- auto_ptr(auto_ptr_ref<element_type> __ref) throw()
- : _M_ptr(__ref._M_ptr) { }
- auto_ptr&
- operator=(auto_ptr_ref<element_type> __ref) throw()
- {
- if (__ref._M_ptr != this->get())
- {
- delete _M_ptr;
- _M_ptr = __ref._M_ptr;
- }
- return *this;
- }
- template<typename _Tp1>
- operator auto_ptr_ref<_Tp1>() throw()
- { return auto_ptr_ref<_Tp1>(this->release()); }
- template<typename _Tp1>
- operator auto_ptr<_Tp1>() throw()
- { return auto_ptr<_Tp1>(this->release()); }
首先是一个构造函数,然后是赋值函数,然后便是类型转换符。
构造函数和赋值函数提供了auto_ptr_ref类型到auto_ptr类型的转换。两个转换函数同样提供了auto_ptr_ref和auto_ptr之间的相互转换。
至此整个auto_ptr的源码就看完了,但是更多的东西还需在使用中才能有更深的体会。。
现在我们看auto_ptr的使用例子:
拥有权的转移
- #include <iostream>
- #include <memory>
- using namespace std;
- template <typename T>
- ostream& operator << (ostream& strm,const auto_ptr<T>&p)
- {
- if (p.get() == NULL)
- {
- strm << "NULL";
- }
- else
- {
- strm << *p;
- }
- return strm;
- }
- int main()
- {
- auto_ptr<int> p(new int(42));
- auto_ptr<int> q;
- cout << "after initialization: " << endl;
- cout << " p: " << p << endl;
- cout << " q: " << q << endl;
- q = p;
- cout << "after assigning auto pointers: " << endl;
- cout << " p: " << p << endl;
- cout << " q: " << q << endl;
- *q += 13;
- p = q;
- cout << "after change and reassignment: " << endl;
- cout << " p: " << p << endl;
- cout << " q: " << q << endl;
- system("pause");
- return 0;
- }
const auto_ptr<T> 拥有权不能转移,但可以改变它的值。
- #include <iostream>
- #include <memory>
- using namespace std;
- template <typename T>
- ostream& operator << (ostream& strm,const auto_ptr<T>&p)
- {
- if (p.get() == NULL)
- {
- strm << "NULL";
- }
- else
- {
- strm << *p;
- }
- return strm;
- }
- int main()
- {
- auto_ptr<int> p(new int(42));
- const auto_ptr<int> t(new int(5));
- cout << t << endl;
- *t = 6; // ok.
- cout << t << endl;
- p = t;///error.
- system("pause");
- return 0;
- }