关于auto_ptr_ref的一点问题

原文链接:http://blog.csdn.net/benny5609/article/details/2516822

今天我在看 The C++ Standard Library 的时候,总觉的上面讲的关于auto_ptr_ref的问题没有说清楚,查了网上的资料发现也没有说清。 也许还有很多朋友像我一样对此存在疑问。其实,这个问题有没有弄清楚,对实际编程影像并不大,但是本着“勿在浮沙筑高台”的精神,我用了一个晚上的时间,来个彻底的大调查,终于基本上弄明白了其中的道理。(大多数时间都浪费在VC上,因为 VC支持对右值的非 常应用,而标准C++不可以,所以在VC中,有没有auto_ptr_ref实际上都无所谓!)

STL中有一个智能指针auto_ptr,可以实现简单的内存自动回收,防治内存泄漏(memory leakage)。auto_ptr实际是一个类,在该类析构时自动调用delete,从而打到了内存回收的效果。

但是,由于同一个指针同一时刻只能被一个auto_ptr占用,如果采用赋值操作(=)或者拷贝构造函数调用等,就会发生所有权转移,例如:

auto_ptr<int> p(new int(0));

auto_ptr<int> q;

此时,p拥有指向一个int的指针,q的指针为空。如果执行

q=p;

则,p指向空,q指向int;

但是,这样所有权转换的问题同样发生在参数传递中,例如

void foo(auto_ptr<int> t);

如果调用 foo(p);

那么p就丢失了指针,所以一个解决方法是用引用

例如 void foo1(auto_ptr<int>& t);

         void foo2 (const auto_ptr<int>& t);

两者都是可以的,不过foo1非常不安全,因为在函数里面很容易通过类似赋值的操作使t丢失指针,而foo2不会。例如

void foo2(const auto_ptr<int>& t)

{ auto_ptr<int> m; m=t;}

会发生编译错误,从而避免灾难的发生。

但随之又出现一个很大的问题,就是auto_ptr类的拷贝构造函数,或者赋值函数。

最理想的情况是这样(如果能成功,就不会有别的什么问题):

auto_ptr(const auto_ptr& rhs):ap(rhs.release()){}

但由于上述的原因,会发生编译错误(因为调用了release(),而release()会改变成员变量,不再是const)。

所以只能去掉const,变为

auto_ptr(auto_ptr& rhs):ap(rhs.release()){}

这样可以编译成功,而且往往也能正确运行,但是唯一的问题是: 当rhs为右值时会出现问题。

为了简化问题,先假设拷贝构造函数什么都不做,即:

auto_ptr(auto_ptr& rhs){}

那么,如果有 auto_ptr<int> p(new int(10))

执行 auto_ptr<int> q(p),不会有任何问题,因为p是左值。

但如果执行      auto_ptr<int> q(auto_ptr<int>(new int(10))) ,则会发生编译错误,因为auto_ptr<int>(new int(10)) 是右值,对右值的引用只能是常引用,也就是"const auto_ptr& rhs"的形式。但这里要注意的是,刚才那段代码用VC编译没有任何问题,并且可以顺利运行,但是用GCC之类的标准c++就不能顺利编译。

在VC中

auto_ptr<int>& p=auto_ptr<int>(new int(0))     是合法的,但在标准C++中是不合法的,只有

const auto_ptr<int>& p=auto_ptr<int>(new int(0)) 才是合法的,也即在标准C++中,对右值的引用只能是常引用。

所以说,要在标准C++中实现 auto_ptr<int> p(auto_ptr<int>(new int(0))) 就变得不可能了,因为如上所说,拷贝构造函数是这样的形式:

auto_ptr(auto_ptr<T>& rhs):ap(rhs.release()){}

但是不能把右值传到一个非 常引用中。

但毕竟有聪明的人能想到解决办法,利用代理类( proxy class)

声明如下结构,为了方便,我用int代替模板参数

struct auto_ptr_ref

{

   int* p;

   auto_ptr_ref(int *t):p(t){}

};

然后在auto_ptr类中增加了以下东东:

auto_ptr(auto_ptr_ref rhs):ap(rhs.p){}

auto_ptr& operator=(auto_ptr_ref rhs){reset(rhs.p); return *this;}

operator auto_ptr_ref(){return auto_ptr_ref(release());}

之后,如果在标准C++有以下调用(VC中也会按照这个步骤调用,虽然没有auto_ptr_ref它也能直接调用)

auto_ptr<int> p(auto_ptr<int>(new int(0)))

便可以成功,过程如下:

1. 构造临时对象 auto_ptr<int>(new int(0))

2. 想将临时对象通过拷贝构造函数传给p,却发现没有合适的拷贝构造函数,因为只有auto_ptr(auto_ptr& rhs)这个不能用,又没有auto_ptr(const auto_ptr& rhs) (因为用了在所有权转移中会出错),呵呵!

3. 编译器只能曲线救国,看看类型转换后能不能传递。

4. 由于我们定义了 operator auto_ptr_ref() 所以编译器自然就可以试一下转为 auto_ptr_ref类型。

5. 编译器猛然间发现,我们定义了 auto_ptr(auto_ptr_ref rhs):ap(rhs.p){} 的构造函数,可以传递。

6. 顺利构造p,任务完成。

其实说白了问题很简单,因为构造函数不能接受右值,则取 中间左值=右值, 然后再让函数接受中间左值。 而这一系列过程正是利用编译器能够自动进行类型转换而完成的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值