这几天开始拜读侯捷先生和孟岩先生的译作《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

为什么需要auto_ptr_ref的更多相关文章

  1. auto_ptr源码剖析

    /* * Copyright (c) 1997-1999 * Silicon Graphics Computer Systems, Inc. * * Permission to use, copy, ...

  2. C++11 auto_ptr 的问题

    auto_ptr作为最早的智能指针,可以实现以RAII手法管理堆区对象,但它设计的本意只是简单的利用C++对于栈区对象的自动析构管理堆区对象, 并不像shared_ptr那样包含引用计数,可以在每次拷 ...

  3. auto_ptr解析

    auto_ptr是当前C++标准库中提供的一种智能指针,或许相对于boost库提供的一系列眼花缭乱的智能指针, 或许相对于Loki中那个无所不包的智能指针,这个不怎么智能的智能指针难免会黯然失色.诚然 ...

  4. 【C++深入浅出】智能指针之auto_ptr学习

    起:  C++98标准加入auto_ptr,即智能指针,C++11加入shared_ptr和weak_ptr两种智能指针,先从auto_ptr的定义学习一下auto_ptr的用法. template& ...

  5. c++11 auto_ptr介绍

    在代码里面看到了auto_ptr这个东西,正好以前一哥们曾经问过我这个问题..所以特意去搜了搜帖子,学习学习 http://www.cnblogs.com/gaoxianzhi/p/4451803.h ...

  6. auto_ptr, which can release the space automatically

    C++的auto_ptr所做的事情,就是动态分配对象以及当对象不再需要时自动执行清理. 使用std::auto_ptr,要#include <memory>.[1]  中文名 自动指针 外 ...

  7. c++智能指针《一》 auto_ptr

    转载http://www.cnblogs.com/gnagwang/archive/2010/11/19/1881811.html C++的auto_ptr auto_ptr所做的事情,就是动态分配对 ...

  8. auto_ptr 要点解析

    今天看了auto_ptr类的用法,又仔细看了看C++标准库中的符合标准的auto_ptr类别的实作,觉得自己基本上理解了auto_ptr的原理,下面就我的心得写几句,有不正确的地方,希望多多指教. 1 ...

  9. 以对象管理资源——C++智能指针auto_ptr简介

    auto_ptr是C++标准库提供的类模板,它可以帮助程序员自动管理用new表达式动态分配的单个对象.auto_ptr对象被初始化为指向由new表达式创建的对象,当auto_ptr对象的生命期结束时, ...

随机推荐

  1. UML学习(类图和序列图等)

    visio绘制UML图使用visio 提示此UML形状所在的绘图页不是UML模型图的一部分 请问这个问题怎么解决?新建->选择绘图类型->选择软件与数据库模板->选择UML模型图-& ...

  2. hibernate 自生双向一对多 多对一管理 (树)

    <span style="font-size: large;">package com.javacrazyer.test; import java.io.Seriali ...

  3. 运行ASP程序报错

    错误提示: An error occurred on the server when processing the URL. Please contact the system administrat ...

  4. 【mysql】MySQL存储IP地址

    为什么要问如何存储IP 首先就来阐明一下部分人得反问:为什么要问IP得怎样存,直接varchar类型不就得了吗? 其实做任何程序设计都要在功能实现的基础上最大限度的优化性能.而数据库设计是程序设计中不 ...

  5. phpstorm laravel单元测试 配置

    laravel中集成了单元测试工具phpunit可以在项目的根目录下进行使用,命令是:phpunti ./tests/单元测试文件名称.在phpstorm中使用phpunit需要做一些配置,指定com ...

  6. Maven修改镜像仓库地址

    修改maven根目录下的conf文件夹中的setting.xml文件,如果你修改了默认仓库的存储位置,即.m2文件夹下没有本地仓库,但是有个setting.xml文件,那就修改这个文件就可以. 具体内 ...

  7. php获取一年中某一周的开始和结束时间

    PHP来获取一年中的每星期的开始日期和结束日期的代码 函数get_week()通过传入参数$year年份,获取当年第一天和最后一天所在的周数,计算第一周的日期,通过循环获取每一周的第一天和最后一天的日 ...

  8. 黄聪:WordPress 函数:add_filter()(添加过滤器)

    add_filter() 可以挂载一个函数到指定的过滤器上. 用法 add_filter( $tag, $function_to_add, $priority, $accepted_args ); 参 ...

  9. c++中ifstream读文件的问题(关于eof())

    今天帮别人找BUG,是一段关于c++读写文件的问题,使用的是ifstream与outstream类,关于ofstream与ifstream的用法,此处不再獒述,见代码: #include<ios ...

  10. PC-Lint安装配置与集成到VS2010

    第一篇 PC-lint 9 安装及配置教程 1.从这里下载PC-lint.9.0e.rar,解压缩(目录中的patch文件夹不用,因为它只能将PC-lint升级到9.0e ) 2.点击pclint9s ...