zoukankan      html  css  js  c++  java
  • C++ 智能指针Auto_PTR 分析

          C++的动态内存的分配与释放是个挺折磨人的事情,尤其异常分支复杂时(比如一堆try catch中,各catch里需要做delete 掉相关的堆上分配的内存),极有可能产生内存泄露的情况。C++中提供了智能指针作为可选的解决方案, C++标准库中自带的智能指针是auto_ptr,它在大多数场景下是满足需求的。针对auto_ptr的缺点,boost和loki两套库都扩展出一些智能指针,并且boost中有两位幸运儿入选了tr1中(std::tr1::shared_ptr,std::tr1::weak_ptr)。本文就gcc中auto_ptr的实现做些分析,以飨自己。笔记采用注释源码的方式。

    /**
    * 这个wrapper类提供auto_ptr以引用语义,在下面的操作中有介绍。
    */

    template<typename _Tp1>

    struct auto_ptr_ref {

          _Tp1* _M_ptr;

          explicit auto_ptr_ref(_Tp1* __p) :

                _M_ptr(__p) {

          }

    };

    /**

    * auto_ptr的实现还是很简单的,使用上也简单。在创建auto_ptr对象后,

    * 通常的使用也就是调用它的*和->操作符,如下面的sample片段:

    * AutoPtr<Admin> ptr1(new Admin());

    * cout<<ptr1->getAge()<<endl;

    * cout<<”obj:”<<*ptr1<<endl;

    * 可以看到,auto_ptr中的成员函数都是throw()不抛异常的。

    */

    template<typename _Tp>

    class auto_ptr {

    private:

          _Tp* _M_ptr;//

    public:

          /// The pointed-to type.

          typedef _Tp element_type;

    /**
    * 构造函数,将auto_ptr绑定到指针__p。
    * __p是一个指向new出来的对象的指针,默认为0(NULL)是说auto_ptr的构造函数可以不传参构造,
    * 这时成员_M_ptr=0,如果接着解引用auto_ptr对象,将Segmentation fault。当然,通常应用auto_ptr的构造
    * 函数会传参的。auto_ptr提供了get函数来判断_M_ptr是否为空、reset函数重置_M_ptr指针。
    * 在继承情况下,_M_ptr可以是__p的基类型。
    * 构造函数声明为explicit表示禁止参数的自动类型转换(因为它们总是邪恶的)。
    *
    */

          explicit auto_ptr(element_type* __p = 0) throw () :

                _M_ptr(__p) {

          }

    /**
    * 辅助函数
    */

          element_type*     release() throw () {

                element_type* __tmp = _M_ptr;

                _M_ptr = 0;

                return __tmp;

          }

    /**
    * auto_ptr的复制构造函数是很邪恶的,它的逻辑是将参数__a中的指针挪给新对象的,
    * 原来的__a的内置指针被置空,接下来就不能继续操作__a引用的对象,否则就掉进出错的陷阱。
    * 另外,复制构造函数的参数不是个const,因为它需要修改参数内容的。
    */

                      auto_ptr(auto_ptr& __a) throw () :

                _M_ptr(__a.release()) {

          }

    /**
    * 成员函数模板。好吧,我承认,我对模板也是半知半解(注意,不是一知半解)。这个函数
    * 用于将继承体系中的子类型上溯成基类型。举个例子:
    * 假如User是基类,Admin是派生类,那么下面的操作是ok的。
    * auto_ptr<Admin> ptr2(new Admin());
    * auto_ptr<User> ptr3(ptr2);
    * 编译器的原理类型识别和转换的大致过程是:当模板参数类型不匹配时(_Tp1转成_Tp),
    * 编译首先检查是否存在合适的类型转换操作符(auto_ptr是没有的),如果没有则检查是否
    * 存在合适的成员函数模板完成类型转换。在_Tp1是_Tp的派生类的情况下,这种转换就会成功。
    * 这也是说,编译器检查的是模板参数类型而不是对象的实际类型,所以,下面的例子编译就会失败:
    * auto_ptr<User> ptr2(new Admin());
    * auto_ptr<Admin> ptr3(ptr2);
    */

          template<typename _Tp1> auto_ptr(auto_ptr<_Tp1>& __a) throw () :

                _M_ptr(__a.release()) {

          }

    /**
    * @brief 重置管理的对象指针,如果_M_ptr不为空,会delete掉。如果重置的指针就是本身
    * 的_M_ptr,就是个空操作。
    * @param __p 对象指针.
    */

          void reset(element_type* __p = 0) throw () {

                if (__p != _M_ptr) {

                      delete _M_ptr;

                      _M_ptr = __p;

                }

          }

    /**
    * 赋值操作符,和复制构造函数一样是邪恶的,赋值操作会delete掉右值管理的对象指针。
    * 如果auto_ptr对象作为函数参数传递,并且是传值,那么这个调用过程会涉及到赋值操作符
    * 的调用,产生并不期待的delete外部对象的结果。
    */

                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;

          }

    /**
    * 析构函数,操作很明显,因为是delete,所以auto_ptr是不支持数组指针的,否则会有
    * 内存泄露。
    */

          ~auto_ptr() {

                delete _M_ptr;

          }

    /**
    * 解引用操作符
    */

                element_type& operator*() const throw () {

                _GLIBCXX_DEBUG_ASSERT(_M_ptr != 0);

                return *_M_ptr;

          }

    /**
    * ->操作符,是auto_ptr被用得最多的调用吧。
    */

                            element_type* operator->() const throw () {

                _GLIBCXX_DEBUG_ASSERT(_M_ptr != 0);

                return _M_ptr;

          }

    /**
    * 返回auto_ptr管理的指针,这通常用于判断指针是否为空的情况,所以,如果要判断
    * auto_ptr管理的指针是否为空,不要使用if(auto_ptr_obj){}而是使用get函数(实际上,
    * 因为auto_ptr并没用定义指向element_type的dumb指针的隐式类型转换操作符,所以根本
    * 编译不过if(auto_ptr_obj))。
    * 但是,auto_ptr并没有禁止你进一步操作你得到的指针,甚至delete它使
    * auto_ptr对象内置的指针悬空。
    */

          element_type* get() const throw () {

                return _M_ptr;

          }

    /**
    * 下面的三个函数连带auto_ptr_ref是auto_ptr中的神奇之笔,因为我拍了好多次脑袋才想明白
    * 是怎么的应用原理。考虑两种使用情况:
    * 1)void foo(auto_ptr< User> ptr);
    * 2)auto_ptr< User> func_returning_auto_ptr(…..);
    * auto_ptr ptr< User> = func_returning_auto_ptr(…..);
    * 对于第一种情况,当调用方式是:foo(auto_ptr< User>(new User));时,因为是传值调用,
    * 而实参是个临时对象,所以需要做赋值构造对象,但auto_ptr的赋值构造函数参数并不是const的
    * 所以不匹配其复制构造函数。auto_ptr采用了曲线策略,编译器接着检查类型转换操作符,
    * 发现operator auto_ptr_ref<_Tp1>()是匹配的,所以将临时对象转成auto_ptr_ref,再调用
    * auto_ptr(auto_ptr_ref __ref)把auto_ptr_ref转成auto_ptr。
    */

          auto_ptr(auto_ptr_ref<element_type> __ref) throw () :

                _M_ptr(__ref._M_ptr) {

          }

          template<typename _Tp1> operator auto_ptr_ref<_Tp1>() throw () {

                return auto_ptr_ref<_Tp1> (this->release());

          }

    /**
    * 和auto_ptr(auto_ptr_ref __ref)相似
    */

                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<_Tp1>() throw () {

                return auto_ptr<_Tp1> (this->release());

          }

    };

    //auto_ptr是不支持void类型的模板特化。

    template<> class auto_ptr<void> {

    public:

          typedef void element_type;

    };

  • 相关阅读:
    GPS文件处理
    ArcEngine整个Map选择集的闪烁以及跨图层选择集导出为Shp
    查询和“1002”号的同学学习的课程完全相同的其他同学学号和姓名
    GPS文件处理(后续)——计算单词数
    辩者二十一事
    ArcEngine添加栅格后,不能闪烁问题
    struts2 模型驱动的action赋值优先顺序
    easyui placeholder 解决方案
    velocity的foreach下标
    javascript动态改变iframe的src
  • 原文地址:https://www.cnblogs.com/skyofbitbit/p/3649776.html
Copyright © 2011-2022 走看看