为什么需要auto_ptr_ref
来源:互联网 发布:数据归一化方法 编辑:程序博客网 时间:2024/06/08 02:04
这几天开始拜读侯捷先生和孟岩先生的译作《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的拥有权 。为了说明拥有权的转移 ,请看下面的代码示例:
#include <iostream>#include <memory>using namespace std;int main(int argc, char **argv){ auto_ptr<int> ptr1(new int(1)); auto_ptr<int> ptr2(ptr1); //ptr1的拥有权被转移到ptr2 auto_ptr<int> ptr3(NULL); ptr3 = ptr2; //ptr2的拥有权被转移到ptr3 cout<<ptr1.get()<<endl; //结果为0 cout<<ptr2.get()<<endl; //结果为0 cout<<*ptr3<<endl; //结果为1}
auto_ptr的拷贝构造函数与赋值操作符
由于需要实现拥有权的转移,auto_ptr的拷贝构造函数和赋值操作符,与一般类的做法不太相同。我们可以看看MinGW5.1.6实现的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引用不能绑定右值
2011-9-3 16:44
提问者: 古刃霜剑 | 浏览次数:78次
为什么 const &a=40是对的,而&a=40是错的,其中具体的原理是怎样的?
&a是对a解引用了,是a的地址,当然不能把40复给他
而const必须在声明时初始化,也就是const &a=40是初始化而不是把40给赋值,所以编译能通过
同理,下面的两段代码,也不会通过编译:
#include <iostream>#include <memory>using namespace std;auto_ptr<int> f();int main(int argc, char **argv) { auto_ptr<int> ptr3(f()); //使用临时对象进行拷贝构造 auto_ptr<int> ptr4(NULL); ptr4 = f(); //使用临时对象进行赋值}
#include <iostream>#include <memory>using namespace std; auto_ptr<int> f(){ return auto_ptr<int>(new int(3)); //这里其实也使用临时对象进行拷贝构造}
普通类不会遇到这个问题,是因为他们的拷贝构造函数及赋值操作符(不管是用户定义还是编译器生成的版本),参数都是const &。
auto_ptr_ref之目的
传说当年C++标准委员会的好多国家,因为这个问题都想把auto_ptr从标准库中剔除。好在Bill Gibbons和Greg Colvin创造性地提出了auto_ptr_ref,解决了这一问题,世界清静了。
auto_ptr_ref之原理
很显然,下面的构造函数,是可以接收auto_ptr临时对象的。
auto_ptr(auto_ptr __a) throw() : _M_ptr(__a.release()) { }
但另一个问题也很显然:上述构造函数不能通过编译。如果能通过编译,就会陷入循环调用。我们稍作修改:
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()); }
至此,我们可以写出下面的代码,并可以通过编译:
#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) { }};
auto_ptr_ref之代码
这里列出auto_ptr_ref相关的函数,共参考:
auto_ptr(auto_ptr_ref<element_type> __ref) throw() : _M_ptr(__ref._M_ptr) {} 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_ref<_Tp1>() throw () { return auto_ptr_ref<_Tp1> (this->release()); } template<typename _Tp1> operator auto_ptr<_Tp1>() throw () { return auto_ptr<_Tp1> (this->release()); }
http://www.cnblogs.com/skyofbitbit/archive/2012/09/12/2681687.html
- 为什么需要 auto_ptr_ref
- 为什么需要auto_ptr_ref
- 为什么需要auto_ptr_ref
- 为什么需要auto_ptr_ref
- 为什么需要auto_ptr_ref
- 为什么需要auto_ptr_ref
- auto_ptr_ref
- 浅析为什么要有auto_ptr_ref这个类
- auto_ptr &auto_ptr_ref
- 为什么需要Web Service
- 为什么需要软件度量
- 为什么需要程序评论家?
- 为什么需要Linux?
- 为什么需要AJAX
- 为什么我们需要单元测试?
- 为什么我们需要站内搜索?
- 为什么需要CSS
- 为什么需要精确管理?
- poj 1067和 hdu 1527 取石子游戏
- 一些开源产品,没事可以读读源代码
- jquery操作select小结
- 绑定类别的方法
- vmare 虚拟机安装xp错误output error file to the following location A:\GHOSTERR.TXT
- 为什么需要auto_ptr_ref
- io表
- STM32通过调用库函数进行编程
- linux的input子系统(五)
- NYOJ290-动物统计加强版
- jdbc总结
- 我在兰亭这三年之开展自动化
- 通过Spring快速创建RMI应用
- JAVA网络编程-----TCP通信