一个auto_ptr_ref引发的连锁思考

最近在看《C++标准程序库》,功力尚浅,看着有些磕绊,不过着实收获不小,不过看到auto_ptr实现里关于auto_ptr_ref的时候,对这个类不甚了解,并且联想到之前看《STL源码解析》时候,也有瞥见过这个代理类,是时候一并解决一下。

auto_ptr的特性

auto_ptr建立在“拥有权”这个概念之上,一个对象只能被一个auto_ptr所拥有,因此与一般类不同,auto_ptr的拷贝构造和赋值符重载的参数是 非const引用(一般类用const的原因是出于对右值传递的需要),因为在拷贝或者赋值的时候,需要修改传递的原auto_ptr的成员变量,才能完成“拥有权”的转移。

这样就会出现以下问题,假如需要定义这样一个auto_ptr:

    /*用一个auto_ptr临时对象初始化ptr_1,会调用拷贝构造*/
    class A;
    auto_ptr<A> ptr_1(auto_ptr<A>(new A()));

那么由于auto_ptr<A>(new A())是右值,而右值是不能传递给非const引用,所以将无法匹配拷贝构造函数。

    template <class T>
        class my_auto_ptr
        {
        public:
            my_auto_ptr(T *t)
            {
                pt = t;
                cout<<"in constructed func !"<<endl;
            }

            my_auto_ptr(my_auto_ptr& tmp)
            {
                cout<<"in copy constructed fun !"<<endl;
            }

        private:
            T *pt;
        };
    int main(int argc, char const *argv[])
    {
        my_auto_ptr<int> q(my_auto_ptr<int>(new int(10)));
        return 0;
    }
    /*编译不过,如果把拷贝构造函数参数前加上const,则会调用拷贝构造函数进行*/
    /*ps.g++编译可能需要加上-fno-elide_constructors选项*/
    /*否则编译器会进行RVO优化,导致输出没有调用拷贝构造,导致产生和拷贝构造函数不相关的错觉,其实不然*/
auto_ptr_ref

那么对于这个临时对象需要怎么才能传递呢?拷贝构造函数是不能去动,那么是否可以写出这样的构造函数:

    auto_ptr(auto_ptr __a) throw() : _M_ptr(__a.release()) { }   
    /*临时对象是auto_ptr类型,这样子就可以传递了*/
    /*但是这样显然编译不过的,会造成构造函数循环调用*/
为什么会构造函数循环调用

说到底构造函数也是函数,对于上述声明pass-by-value(因为是临时对象,pass-by-reference只能给const引用),需要传递外部对象,显然调用函数时候会首先构造一个临时对象,如果没有拷贝构造函数的话,这种情况如果定义类似A(A a)的构造函数,假设外部对象为a1,内部为a2,以此类推。那么整个构造过程就变成:A a1(A())->A a2(A())->(此时构造a2发现匹配构造函数,需要临时对象a3)->A a3(A())……显然,递归调用而且不会停止。所以如果有定义此种构造函数,编译器在编译阶段就制止了。所以一般这种需要传递对象进入一个调用函数,都是会调用拷贝构造函数,这也是拷贝构造函数的使命吧(严肃脸,纯属个人臆断23333333)

小插曲:面对百度解答的过程中,发现有个帖子提了构造函数调用构造函数的循环调用问题,在此写一下我自己的见解。
原帖地址http://bbs.csdn.net/topics/320261343

    class Test
    {
    public:
        Test(int i){m_i = i;}
        Test(){Test(m_i);} /*在此处的Test(m_i)并不是调用构造函数,此种写法表示定义一个Test对象m_i,也就是等同于Test m_i*/
        void SetValue (int j)
        {
              m_i = j;
        }
        int GetValue()
        {
            return m_i;
        }
    private:
        int m_i;
    };

    int main()
    {

        Test b;//设断点
        b.SetValue(10);
        int value = b.GetValue();
        cout<<value<<endl;
        return 0;
    }

在默认构造函数中,Test(m_i)其实没有匹配带参构造函数,在这里这种写法,因为m_i前面没有declare过,,其实等同于Test m_i,所以就一直循环调用不带参的构造函数。

可以写个例子验证一下:

    class A
    {
    public:
        A()
        {
            cout<<"in constructed func !"<<endl;
        }

        A(int i)
        {
            cout<<"in Ai constructed fun !"<<endl;
        }
        ~A()
        {
            cout<<"in destructor func !"<<endl;
        }

        A(const A &tmp)
        {
            cout<<"in copy constructed fun !"<<endl;
        }
    };

    int main(int argc, char const *argv[])
    {
        //my_auto_ptr<int> q(my_auto_ptr<int>(new int(10)));
        //test(A());
        int test = 1;
        A(test);       /*error: 'test' has a previous declaration as ‘int test’*/
        A((int)test)    /*OK*/
        A(test_1)       /*输出in constructed func !*/
                        /*in destructor func !*/
        return 0;
    }

很明显,对与A(test)这种调用,编译器认为是等同于A test,除非显示指出test是一个变量。所以对于帖子里的问题,解决办法就是:

    Test(){Test(m_i);}//这个调用的是自身
    Test(){Test((int)m_i);}//这样才是调用另一个
    Test(){Test(3);}//这样也是调用另一个
    Test(){Test(GetValue());}//这样也是另一个
    Test(){Test(this->m_i);}

继续auto_ptr_ref,那么既然写不出A (A a)这种构造函数,那只能曲线救国。

        template<typename _Tp1>  
        struct auto_ptr_ref  
        {  
          _Tp1* _M_ptr;  

          explicit 
          auto_ptr_ref(_Tp1* __p): _M_ptr(__p) { }  
        };  

那么就可以写出如下构造函数:

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

通过ref的_M_ptr来构造auto_ptr的_M_ptr。

剩下的就是写一个auto_ptr到auto_ptr_ref的隐式转换了:

        template<typename _Tp1>     
        operator auto_ptr_ref<_Tp1>() throw()   { return auto_ptr_ref<_Tp1>(this->release()); }
        /*将auto_ptr临时对象隐式转换为auto_ptr_ref*/

至此就可以写出auto_ptr<A> ptr_1(auto_ptr<A>(new A()));这样的定义了。

行文至此。

最后,总结一下,平时对构造函数析构函数拷贝构造函数其实流于表面,纸上得来终觉浅,还是要多躬行。

以上。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值