之前弃用博客园的原因是其不支持markdown语法。到今天偶然进来试了一下,发现Markdown toggle原来是能支持的(不知道是不是因为它升级了),遂重新启用。

在一年前学C++的时候就对引用,常引用和右值引用迷迷糊糊的,再加上老师实在是不给力啊!(当时我问她一个很基础的关于函数对象的问题,她问我,什么是函数对象)所以那时候就只知道使用惯例而不知其具体内涵。再加上近一年时间都在其他语言上花费精力,对它们的区别就更加模糊了。直到前几天在stackoverflow上问了个关于SFINAE的问题,才猛地感觉到自己的C++完全要回炉重练了。今天重新看回C++ Prime Plus,终于有了一种恍然大悟的感觉了。

为何要接受引用为参数,为何要返回引用?

既然引用参数/返回值的使用常出现在类的定义里,就以类为例:

  1. class C {
  2. int data[1024];
  3. }
  4. C foo(C c) { /* do sth */; return c; }
  5. const C& foo(C& c) { /* do sth */; return c; }

如果是以值来传递参数,返回值,那么一个函数的调用需要经历这几步:

  1. C a;
  2. C b = foo(a);

a => temp val (as arg) => arg c => temp val (as return value) => temp val (as arg in constructor) => b

我们可以用c++语句来描述上述过程

a => tmpa = a => arg c => tmpc = c => tmpb = tmpc => b

从a到tmpa的过程自然是为了保证值传递的语义,使得函数内对参数的修改不会影响到传入的变量本身,亦即不产生副作用。而从c到返回值tmpc的过程,当c是临时变量时,将发生”Named Return Value Optimization“。现代的编译器在返回之前将看到被返回的是一个临时变量且变量生命周期将随着离开函数而终结,因此编译器将变量转移到上层scope的临时对象中加以保存。编译器将根据变量提供的copy/move构造器决定这一过程是通过copy还是move来完成。

显然,从实参到形参的拷贝,和返回值时的拷贝过程是完全可以避免的。方法就是将参数/返回值类型由值改为引用。这么一捋,引用类型参数所引用的对象,和返回值所引用的对象就非常清晰了。参数引用的对象自然是实参,而返回值的引用对象则是返回语句中返回的对象。也就是:

  1. C& tmpa = a; // passing argument
  2. const C& tmpc = c; // returning result
  3. const C& tmpb = tmpc; // construct b

参数/返回值的传递就相当于上述语句。自然地,对于引用类型的限制的特性也同样作用于参数和返回值。比如:

  • 非常引用不能引用临时对象,自然地,不能引用表达式(包括lambda表达式)
  • 常引用引用临时对象时将产生警告

也就是说,无论如何,左值引用都无法引用表达式(因为表达式的结果是临时对象,即右值)。正是因为引用类型参数和原本的值类型参数在此行为上的差异。便产生了能够引用表达式的引用——右值引用。如果你希望你的函数在接受参数时具有和接受值类型参数一样的行为,又想避免拷贝开销,就将参数类型声明为右值引用。

现在将临时对象传递给引用参数的问题解决了,那么返回值呢?如何返回一个临时变量,且避免拷贝开销?一个可选方案是,将接受返回结果的变量引用传递进函数内。事实上很多库函数就是采用这样的方法的。下面是一个简单的例子:

  1. const int& add(const int& a, const int& b)
  2. { return a + b; } // warning: returning reference to local temporary object
  3. void add(const int& a, const int& b, int& r)
  4. { r = a + b; } // OK

当传入参数为右值引用时,在函数体内并不会被看作临时对象,这是出于安全的考虑。因为参数在函数内可能被引用多次,而很可能其中一次的引用就“would be free to rip its guts out”掉了,这样的话接下来的对它的引用就都变成非法的了。正因为如此,程序员需要使用std::move函数来明确指定在哪个地方可以将它看作临时对象。比如在下面的例子里,在函数中,调用dosth时传入的是a的副本,而在dosthelse里则可以放心传入a本身了。

  1. C foo(C&& a)
  2. {
  3. dosth(a);
  4. dosthelse(std::move(a));
  5. }

其实从clang在返回临时对象给引用的编译警告-Wreturn-stack-address我们就能大概看出左值和右值的区别所在了。右值和左值的根本区别就在于右值不能提供通过地址访问在任何时刻的有效性。自然也就无法被安全引用了。

再看C++引用类型的更多相关文章

  1. 再看Ajax

    再回顾Ajax相关的内容,再次梳理学习还是很有必要的,尤其是实际的开发中,ajax更是必不可少,仔细学习以便避免不必要的错误. 文章导读: --1.使用XMLHttpRequest---------- ...

  2. 再看ftp上传文件

    前言 去年在项目中用到ftp上传文件,用FtpWebRequest和FtpWebResponse封装一个帮助类,这个在网上能找到很多,前台使用Uploadify控件,然后在服务器上搭建Ftp服务器,在 ...

  3. 再看 AspriseOCR - OCR应用开发 -20151124

    再看 AspriseOCR - OCR应用开发 我写这个博文时间为 2015/11/24日,注意时间因为,网上很多文章时间上很久远,有的已经不能参考了 很多人面对从图片中识别文字或者数字0~9  A~ ...

  4. Android菜鸟的成长笔记(17)—— 再看Android中的Unbounded Service

    原文:Android菜鸟的成长笔记(17)-- 再看Android中的Unbounded Service 前面已经写过关于startService(Unbounded Service)的一篇文章:&l ...

  5. 再看case语句

    再看case语句,case语句只处理单条记录,而不是set 列名的使用,可以当做数值来使用: case when 后面简直是完美的的,什么东西都是能放的,只要是一个逻辑上的true/false的逻辑就 ...

  6. android 智能指针的学习先看邓凡平的书扫盲 再看前面两片博客提升

    android 智能指针的学习先看邓凡平的书扫盲 再看前面两片博客提升

  7. python基础----再看property、描述符(__get__,__set__,__delete__)

    一.再看property                                                                          一个静态属性property ...

  8. perf使用的问题,再看perf record,perf record 设置的采样频率,采样频率是如何体现在

    当perf stat -e branches 是统计 再看perf record,perf record是为了是记录时间发生的时候的调用栈, 在我的测试代码中总共有200,000,000条branch ...

  9. 再看Scrapy(1) 基本概念

    再看Scrapy(1) 基本概念 1 准备 安装scrapy: 国内镜像源(官方的pypi不稳定)安装 pip3 install -i https://pypi.douban.com/simple/ ...

随机推荐

  1. 文字超出DIV的边框

    已经给div设置了高宽,但是文字还是会戳出div而不是换行 鼓捣了一下好像是因为这个原因 如果全是 aaaaaaaaaaaaaaaaaaaaa 这样的纯英文,那么测试的时候是不会换行的,因为浏览器认为 ...

  2. SGU 149. Computer Network

    时间限制:0.25s 空间限制:4M: 题意: 给出一颗n(n<=10000)个节点的树,和n-1条边的长度.求出这棵树每个节点到最远节点的距离: Solution: 对于一个节点,我们可以用D ...

  3. 了解负载均衡 会话保持 session同步(转)

    一,什么负载均衡 一个新网站是不要做负载均衡的,因为访问量不大,流量也不大,所以没有必要搞这些东西.但是随着网站访问量和流量的快速增长,单台服务器受自身硬件条件的限制,很难承受这么大的访问量.在这种情 ...

  4. jQuery-弹窗登录

    在jQuery中实现弹窗常要用到的方法有: width()  :元素的宽度 outerWidth()  元素的宽度 盒子的padding+border 总的宽度 scrollTop()  鼠标滚轮自上 ...

  5. linux 定时执行shell

    第一步:安装 crontab ,命令 yum -y install vixie-cron                扩展:service crond start //启动服务            ...

  6. Drupal7安装完整教程

    Drupal7 史前准备工作(安装 AppServ)AppServ 是 PHP 网页架站工具组合包,作者将一些网络上免费的架站资源重新包装成单一的安装程序,以方便初学者快速完成架站,AppServ 所 ...

  7. IE 6最小最大宽度与高度的写法

    最小最大宽度,最小最大高度,这是CSS很常见的一个要求.在现代浏览器中,一个 min-height,min-width 就可以解决问题,但是在IE系列,比如IE6则比较繁琐一点.下面总结一些IE 6下 ...

  8. Python学习 - 使用BeautifulSoup来解析网页一:基础入门

    写技术博客主要就是总结和交流的,如果文章用错,请指正啊! 以前一直在使用SGMLParser,这个太费时间和精力了,现在为了毕业设计,改用BeautifulSoup来实现HTML页面的解析工作的. 一 ...

  9. jQuery在on绑定事件时,使用Function.prototype.bind上下文,只能用off(event)解绑函数,否则可能导致事件叠加

    因为一个bind函数,未解绑成功导致事件叠加,搞了一下午. keyup事件绑定: this.$document.on('keyup', this.keyUp.bind(this)); 原解绑函数: t ...

  10. ACM组队安排

    Problem Description   ACM亚洲区比赛结束,意味着开始备战明年的浙江省大学生程序设计竞赛了!  杭州电子科技大学ACM集训队也准备开始组队.  教练想把所有的n个队员组成若干支队 ...