• 为什么需要auto_ptr_ref


    这几天开始拜读侯捷先生和孟岩先生的译作《C++标准程序库:自修教程与参考手册》 。两位先生确实译功上乘,读得很顺。但是读到P55页关于auto_ptr_ref的讨论,却百思不得其解:为什么需要引入auto_ptr_ref这个辅助类呢?

    从书中描述来看,仿佛与拷贝构造函数右值类型转换 有关。于是,结合auto_ptr的源代码,google之、baidu之,找了一推资料,终于初步 搞清该问题。

    auto_ptr的拥有权

    C++常见的智能指针有std::auto_ptr、boost::shared_ptr、boost::scoped_ptr、boost::shared_array、boost::scoped_array等。auto_ptr只是其中一种而已。但是,为什么auto_ptr才有auto_ptr_ref ,而boost::shared_ptr却没有shared_ptr_ref呢?

     

    答案与auto_ptr的特性有关。auto_ptr强调对资源的拥有权 (ownership)。也就是说,auto_ptr是"它所指对象"的拥有者。而一个对象只能属于一个拥有者,严禁一物二主,否则就是重婚罪,意料外的灾难将随之而来。

    为了保证auto_ptr的拥有权唯一,auto_ptr的拷贝构造函数和赋值操作符做了这样一件事情:移除另一个auto_ptr的拥有权 。为了说明拥有权的转移。


    auto_ptr的拷贝构造函数与赋值操作符  

     /**
    
     *  @brief  An %auto_ptr can be constructed from another %auto_ptr.
    
     *  @param  a  Another %auto_ptr of the same type.
    
     *
    
     *  This object now @e owns the object previously owned by @a a,
    
     *  which has given up ownsership.
    
     */
    
    auto_ptr(auto_ptr& __a) throw() : _M_ptr(__a.release()) {}
    
     
    
    /**
    
     *  @brief  %auto_ptr assignment operator.
    
     *  @param  a  Another %auto_ptr of the same type.
    
     *
    
     *  This object now @e owns the object previously owned by @a a,
    
     *  which has given up ownsership.  The object that this one @e
    
     *  used to own and track has been deleted.
    
     */
    
    auto_ptr&
    
    operator=(auto_ptr& __a) throw () {
    
    reset(__a.release());
    
    return *this;
    
    }
    可以看到,auto_ptr的拷贝构造函数、赋值操作符,它们的参数都是auto_ptr& ,而不是auto_ptr const & 。

        一般来说,类的拷贝构造函数和赋值操作符的参数都是const &。但是auto_ptr的做法也是合理的:确保拥有权能够转移

        如果auto_ptr的拷贝构造函数和赋值操作符的参数是auto_ptr const & ,那么实参的拥有权将不能转移。因为转移拥有权需要修改auto_ptr的成员变量,而实参确是一个const对象,不允许修改。


    右值与const &

    假设我们想写出下面的代码:

    #include <iostream>
    
    #include <memory>
    
    using namespace std;
    
     
    
    int main(int argc, char **argv) {
    
    auto_ptr<int> ptr1(auto_ptr<int>(new int(1)));  //使用临时对象进行拷贝构造
    
    auto_ptr<int> ptr2(NULL);
    
    ptr2 = (auto_ptr<int>(new int(2)));     //使用临时对象进行赋值
    
    }

    假设没有定义auto_ptr_ref类及相关的函数,那么这段代码将不能通过编译。主要的原因是,拷贝构造函数及赋值操作符的参数:auto_ptr<int>(new int(1))和 auto_ptr<int>(new int(2)) 都是临时对象 。临时对象属于典型的右值 ,而非const &是不能指向右值的(就是说给引用赋值) (参见More Effective C++ ,Item 19)。auto_ptr的拷贝构造函数及赋值操作符的参数类型恰恰是auto_ptr&,明显 非const &。


     左值和右值

    左值可以出现在赋值语句的左边或右边

    右值只能出现在赋值语句的右边。

    例如 x*y是个右值,编译表达式x*y=10;则出现错误

    非const引用不能绑定右值

    普通类不会遇到这个问题,是因为他们的拷贝构造函数及赋值操作符(不管是用户定义还是编译器生成的版本),参数都是const &。

    auto_ptr_ref之原理

        很显然,下面的构造函数,是可以接收auto_ptr临时对象的。

    auto_ptr(auto_ptr __a) throw() : _M_ptr(__a.release()) { }  //__a是传值,生成__a需要调用auto_ptr的构造函数,于是便形成了在构造函数中调用构造函数。
    
    但另一个问题也很显然:上述构造函数不能通过编译。如果能通过编译,就会陷入循环调用。我们稍作修改:

    auto_ptr(auto_ptr_ref<element_type> __ref) throw()  //element_type就是auto_ptr的模板参数。   
          : _M_ptr(__ref._M_ptr) { } 

    该版本的构造函数,可以接收auto_ptr_ref的临时对象。如果auto_ptr可以隐式转换到auto_ptr_ref,那么我们就能够用auto_ptr临时对象来调用该构造函数。这个隐式转换不难实现:
    template<typename _Tp1>   
    operator auto_ptr_ref<_Tp1>() throw()   { return auto_ptr_ref<_Tp1>(this->release()); }//将auto_ptr临时对象隐式转换为auto_ptr_ref

    至此,我们可以写出下面的代码,并可以通过编译:

        #include <iostream>   
        #include <memory>   
        using namespace std;   
          
        int main(int argc, char **argv) {   
            auto_ptr<int> ptr1(auto_ptr<int>(new int(1)));  //调用auto_ptr_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;   
             }  
    

       那么,下面的代码也可以通过编译:

        #include <iostream>   
        #include <memory>   
        using namespace std;   
          
        int main(int argc, char **argv) {   
            auto_ptr<int> ptr2(NULL);   
            ptr2 = (auto_ptr<int>(new int(2)));  //调用auto_ptr_ref版本的赋值操作符   
        }  
    

    auto_ptr_ref之本质

    本质上,auto_ptr_ref赋予了auto_ptr“引用”的语义,这一点可以从auto_ptr_ref的注释看出:

        /**  
           *  A wrapper class to provide auto_ptr with reference semantics.  
           *  For example, an auto_ptr can be assigned (or constructed from)  
           *  the result of a function which returns an auto_ptr by value.  
           *  
           *  All the auto_ptr_ref stuff should happen behind the scenes.  
           */  
          template<typename _Tp1>   
            struct auto_ptr_ref   
            {   
              _Tp1* _M_ptr;   
                 
              explicit  
              auto_ptr_ref(_Tp1* __p): _M_ptr(__p) { }   
            };  
    
    因为non-const引用不能绑定到右值,所以没有从rvalue auto_ptr到 auto_ptr的转换,此时需要一个中间代理:auto_ptr_ref来完成:
    rvalue auto_ptr --> auto_ptr_ref--> auto_ptr的转换。


    转自:http://www.cnblogs.com/skyofbitbit/archive/2012/09/12/2681687.html



  • 相关阅读:
    GTK+(基于DirectFB)的字体处理
    控制的字体属性
    [linux手机平台]让应用程序单实例运行
    serif vs sans serif
    关于做事的几点感想
    推荐一部环保电影难以忽视的真相
    短信应用设计备忘录
    手机搜索设计备忘录
    一点概念,
    毕业了,怎么策划的让有意思点呢,
  • 原文地址:https://www.cnblogs.com/ggzone/p/4052447.html
Copyright © 2020-2023  润新知