20180312 C++ 请以pass-by-reference-to-const替换pass-by-value

缺省情况下C++以by value方式(一个继承自C的方式)传递对象至(或来自)函数,除非你另外指定,否则函数参数就是以实际实参的复件(副本)为初值,而调用端所获得的亦是函数返回值的一个复件。这些复件(副本)是由对象的copy构造函数产出,这可能使得pass-by-value成为昂贵的(费时的)操作。考虑以下class继承体系:
class Person{
public:
  Person();         //为求简化,省略参数
  virtual ~Person();
  ...
private:
  std::string name;
  std::string address;
};

class Student: public Person{
public:
  Student();
  ~Student();
  ...
private:
  std::string schoolName;
  std:;string schoolAddress;
};

现在考虑以下代码,其中调用函数validateStudent,后者需要一个Student实参(by value)并返回它是否有效:
bool validateStudent(Student s);//函数以by value方式接受学生
Student yanhui;//颜回,孔子的学生
bool yanhuiisOK = validateStudent(yanhui);//调用函数

上述函数被调用时,无疑的,Student的copy构造函数会被调用,以yanhui为蓝本将颜回初始化。同样明显地,当validateStudent返回s会被销毁。因此,对于函数而言,参数的传递成本是“一次Student 拷贝构造函数调用,加上一次Student析构函数被调用”。
详细过程为:Student对象内有两个string对象,所以每次构造一个Student对象也就构造了两个string对象,此Student对象继承自Person对象,所以每次构造Student对象也必须构造出一个Person对象。一个Person对象又有两个string对象在其中,因此每一次Person构造动作又需承担两个string构造动作。最终结果是,以by value方式传递一个Student对象会导致调用一次Student 拷贝构造函数,一次Person拷贝构造函数,四次string拷贝构造函数。当函数内的那个Student复件被销毁,每一个构造函数调用动作都需要一个对应的析构函数调用动作。因此,以by value方式传递一个Student对象,总体成本是“六次构造函数和六次析构函数”。

如果想出一个方法可以回避所有构造和析构函数就好了。有的!就是pass by reference-to-const:
bool validateStudent(const Student& s);
现在Student以by reference方式传递,将它声明为const是必要的,因为不这样做的话调用者会忧虑validateStudent会不会改变他们传入的那个Student。

以by reference方式传递参数也可以避免对象切割(slicing)问题。假设在一组classes上工作,用来实现一个图形窗口系统:
class Window{
public:
  ...
  std::string name() const;//返回窗口名称
  virtual void display() const;//显示窗口和其他内容
};

class WindowWithScrollBars:public Window{
public:
  ...
  virtual void display() const;
};
所有Window对象都带有一个名称,你可以通过name函数取得它。所有窗口都可显示,你可以通过display函数完成它。display是一个virtual函数,这意味着简易朴素的基类Window对象的显示方式和华丽WindowWithScrollBars对象的显示方式不同。(派生类是基类的扩展)。
现在假设写个函数打印窗口名称,然后显示窗口,下面的示范是错误的:
void printNameAndDisplay(Window w)//不正确! 参数可能被切割
{
  std::cout<<w.name();
  w.display();
}
当调用上述函数并交给他WindowWithScrollingBars对象 会怎样呢?
WindowWithScrollingBars wwsb;
printNameAndDiaplay(wwsb);
参数w会被构造成一个Window对象;它是pass by value,而造成wwsb“是个WindowWithScrollingBars对象”的所有特化信息都会被切除。在printNameAndDisplay函数内不论传递过来的对象原本是什么类型,参数w就像一个Window对象(因为其类型的Window)。因此在printNameAndDisplay内调用display掉用的总是Window::display,绝对不会是WindowWithScrollingBars::diaplay。

解决切割(slicing)问题的办法,就是以by reference-to-const的方式传递w:
void printNameAndDisplay(const Window& w)//很好,参数不会被切割
{
  std::cout<<w.name;
  w.dispaly();
}
现在,传进来的窗口是什么类型,w就表现出那种类型。

在C++底层实现中,reference往往以指针实现,因此pass by reference通常意味着真正传递的是指针,因此若对象属于内置类型,pass by value往往比pass by reference效率更高,这种情况也适用于STL的迭代器和函数对象。

注意:
1、尽量以pass-by-reference-to-const替换pass-by-value。前者通常比较高效,并可以避免切割问题(slicing problem)。
2、以上规则并不适用于内置类型,以及STL的迭代器和函数对象。对它们而言,pass-by-value往往比较合适。

C++ 请以pass-by-reference-to-const替换pass-by-value相关推荐

  1. Effective C++条款20解读:宁以pass by reference to const替换pass by value

    我们先来看一个简单的程序: #include <iostream> using namespace std;class A { private:string name;string add ...

  2. pass by value 与pass by reference 小结

    缺省条件下,c++都是以pass by value形式传递函数参数的,而值传递的参数,编译器知道这个参数收到保护,不会修改其值,实际上修改的都是通过拷贝构造函数另外创建的参数副本,当函数返回时还要调用 ...

  3. [C++学习] effective c++ 笔记 pass by value ,pass by reference

    在编写函数的返回值和形参的时候,能 传引用(pass by reference) 就不要 传值 (pass by value). 这样是为了少调用 copy constructor ,可以提高效率. ...

  4. Java - 传参到底是哪种? pass by value or pass by reference

    在了解Java传参数是pass by value或是pass by reference之前,先了解=赋值的用法会对理解传参很有帮助 赋值(=)的用法 =的意义是赋值,但是这个赋值用在 基本类型 和 对 ...

  5. 数组名传参(pass by reference)

    数组名作为函数参数(pass by reference) 说明:以下定义了一个doube_array函数,接受整个数组为函数参数,将其中的值修改为原来的2倍. #include <stdio.h ...

  6. Pass by reference和pass by value区别举例

    这是我见过的最好的解释pass by value和pass by reference的例子. 假设我要和你分享一个网页, 如果我告诉你URL链接,那我就是pass by reference,你可以通过 ...

  7. 方法参数中pass by reference(传引用)和 pass by value(传值)的区别

    如果你把㆒个名为foo的对象作为参数传给某个函数,那么「对参数赋值」意味改变foo,使它引用(参考.指涉.指向)另㆒个对象.如果你在「被传入对象」身㆖进行什么操作,那没问题,我也总是这样干.我只针对「 ...

  8. perl中子程序中参数的两种引用(传递)方式:pass by value and pass by Reference(传入引用)

    这里主要讨论的是当你要传递的参数不是仅仅一个的时候,也就是两个以上,并且他们数据的类型还不一致的时候,我们要遇到的问题: 下面是一个例子: use strict;#这里是两个数组 my @i =('1 ...

  9. 数据成员是reference或const时该如何赋值?

    #include <string> #include <iostream>class Dog {public:// 成员是reference或const时必须在初始化列表中进行 ...

  10. intbyreference java_从内存出发,java是pass by value 还是pass by reference

    如果有人问你,java到底是pass by value还是pass by reference, 你一定要先斩钉截铁的说,java is pass by value. 我们先看一个简单的例子 publi ...

最新文章

  1. Bug: CuteEditor与IE8不兼容
  2. Java新手小程序之三
  3. 计算机检索技巧知识,初学者常用电脑技巧知识
  4. Jni Helloworld
  5. java实现metro风格_Metro风格的Java组合框(JMetro)–重新介绍
  6. 互联网架构设计中的poll和push
  7. Python模块的使用
  8. idea如何设置黑色主题
  9. 迈高图手机版_迈高图地图下载
  10. 2022年3月22日
  11. 为什么说全球变暖不是阴谋论
  12. ❤️「Python」初阶,必看系列, 万字只为你,建议点赞收藏~❤️
  13. rejected X11 forwarding request错误
  14. QoS 基础: 什么是QoS, 我真的需要吗?
  15. 安卓面试中高级安卓开发工程师总结之——大公司面试的方向和套路以及应对方法
  16. Element UI学习6--Carousel 走马灯
  17. CSS实现文字凹凸效果
  18. Opencascade可视化--视图渲染流程分析
  19. 山东女子学院计算机专业分数线,山东女子学院历年录取分数线汇总、
  20. 有关计算机专业工作室的名字,独特好听的游戏工作室名字

热门文章

  1. 服务器配置和部署(待完善)
  2. aws--s3存储类别--对s3进行存储分层优化
  3. JS 判断客户端环境(以及如何区分QQ客户端和QQ浏览器)
  4. linux卡住重启_linux死机解决办法
  5. STM32——WebSocket
  6. 【源码】应用于各类工业控制的通用PID调谐器仿真设计
  7. The quieter you become,The more you are able to hear.
  8. Android插件化换肤
  9. Git配置远程仓库(密匙链接)
  10. 谷歌浏览器按F12调试js代码的时候找不到代码在哪