浅析为什么要有auto_ptr_ref这个类

本文探讨了一段实现 C++ AutoPtr 类的代码,在不同编译器环境下编译行为的差异,特别是在 VS2013 和 Linux 下的表现。文章详细分析了如何通过类型转换解决构造函数中的 const 约束问题。

首先在VS2013的环境下的一段测试代码

#include<iostream>
using namespace std;
template<class T>
class AutoPtr
{
public:
    AutoPtr(T* pStr = NULL)
        : _pStr(pStr)
    {
        cout << "AutoPtr()" << endl;
    }
    AutoPtr(AutoPtr &ad)//通常拷贝构造都是const类型的但这里是要修改这个传入的参数的因此不>能是const类型
        :_pStr(ad._pStr)//移交管理权
    {
        if (_pStr)
        {
            ad._pStr = NULL;
        }
    }
AutoPtr<T>& operator =(AutoPtr &ad)
{
    if (this != &ad)//判断是不是自己给自己赋值
    {
        delete _pStr;
        _pStr = ad._pStr;
        ad._pStr = NULL;
        return (*this);
    }
}
~AutoPtr()
{
    if (_pStr)
    {
        cout << "~AutoPtr()" << endl;
        delete _pStr;
        _pStr = NULL;
    }
}
T& operator*()
{
    return *_pStr;
}
T* operator->()
{
    return _pStr;
}
protected:
    T* _pStr;
};
void FunTest()
{
    AutoPtr<int>ap(AutoPtr<int>(new int));
}
int main()
{
    FunTest();
    system("pause");
    return 0;
}

它在VS2013的环境下能够编译成功,并且能打印出正确的语句。

但是实际上这段代码是有问题的,同样的代码放在Linux环境下,编译这个cpp文件是不会成功的。
这里写图片描述
问题出在了这条语句:

AutoPtr<int>ap(AutoPtr<int>(new int));

为什么在VS2013下能编译成功:
如果我们使用无名对象去创建ap,肯定会去调用它的拷贝构造函数,一旦调用了拷贝构造函数就会修改这个无名对象里的内容,但无名对象是具有常性的是const类型的,如果修改了就会违背const的约束,按理说会出现编译错误,但实际上并没有?为什么?
但实际上们在逐步调试这个程序的时候会发现并没有去调用它的拷贝构造函数,只是去调用了它的构造函数,因为无名对象在使用过后在整个程序中就失去了作用,对于这种情况,c++会优化掉该临时对象的产生,直接以相同参数调用相关构造函数,也就是说AutoPtr ap1(AutoPtr(new int))直接会被被系统优化成AutoPtr ap1(new int)。
而在Linux下则是没有这步优化的,用无名对象去构造ap时一定会去调用它的拷贝构造函数的,而拷贝构造函数是非const类型的,但是无名对象是const类型的在拷贝构造里对这个无名对象做了修改,就一定是违反了const约束的,因此根本无法通过编译。

上述代码只是简单的实现了标准库里的auto_ptr
但是如果调用C++标准库的auto_ptr的这个类

auto_ptr<int>ap(auto_ptr<int>(new int))

它是进行如下操作的
这里写图片描述
首先去调用auto_ptr(Ty*ptr=0)这个构造函数将无名对象创建出来
然后调用operator auto_ptr_ref<_other>() _THROW0()这个类型转换函数,将无名对象的类型转换成auto_ptr_ref这种类的并且返回的是auto_ptr_ref这个类的临时对象
然后再调用auto_ptr(auto_ptr_ref<_Ty> _Right) _THROW0()这个构造函数
为什么这样做?
也许有人会想把auto_ptr这个类的拷贝构造函数的参数作为值传递,就不会违反const约束了,但是同时问题是在你传参的过程中会产生临时对象势必会再去调用拷贝构造函数,不断调用拷贝构造这样进入了死循环。
因为我们要通过无名对象去构造ap,而无名对象是具有常性的不能修改的但在拷贝构造的参数却是非const类型的因此根本不行,为了解决这个问题我们才再写一个auto_ptr_ref的类,为了将这个无名对象从auto_ptr这个类转换成auto_ptr_ref这个类,然后再返回auto_ptr_ref这个类的临时对象,而这个临时对象又是可以作为auto_ptr的构造函数的参数的,因此就可以间接的通过这个无名对象进行转换构造出ap这个对象。

如果我们在自己写的测试代码中加入类型转换和一个接受转换后的类型的对象作为AutoPtr的参数那么即使在Linux下没有VS的优化工作也是可以通过编译的。
例如加上如下
因此auto_ptr_ref这个类必不可少。

template<class T>
 struct AutoPtrRef
   {
         AutoPtrRef(T*pStr)
          :_Ref(pStr)
          {

          }
          T* _Ref;
  };
   AutoPtr(AutoPtrRef<T> ref)
                  :_pStr(ref._Ref)
         {

          }
      operator AutoPtrRef<T>()
  {
          T*tmp = _pStr;
          AutoPtrRef<T>ref(tmp);
          _pStr = NULL;
          return ref;
  }

此时在Linux环境下再去测试这份代码不仅编译通过还能打印出正确的结果。
这里写图片描述

小结一下:尽管C++标准库里的auto_ptr仍然是存在缺陷的,并且建议不要使用的,但是它的一些巧妙的设计方法还是值得借鉴的。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值