当前位置: 首页 > 编程日记 > 正文

条款22: 尽量用“传引用”而不用“传值”

c语言中,什么都是通过传值来实现的,c++继承了这一传统并将它作为默认方式。除非明确指定,函数的形参总是通过
“实参的拷贝”来初始化的,函数的调用者得到的也是函数返回值的拷贝。
正如我在本书的导言中所指出的,“通过值来传递一个对象”的具体含义是由这个对象的类的拷贝构造函数定义的。这使
得传值成为一种非常昂贵的操作。例如,看下面这个(只是假想的)类的结构:

ExpandedBlockStart.gif代码
class person {
public:
person(); 
// 为简化,省略参数
//
~person();
...
private:
string name, address;
};
class student: public person {
public:
student(); 
// 为简化,省略参数
//
~student();
...
private:
string schoolname, schooladdress;
};

现在定义一个简单的函数returnstudent,它取一个student参数(通过值)然后立即返回它(也通过值)。定义完后,调用这
个函数:
student returnstudent(student s) { return s; }
student plato; // plato(柏拉图)在
// socrates (苏格拉底)门下学习
returnstudent(plato); // 调用returnstudent
这个看起来无关痛痒的函数调用过程,其内部究竟发生了些什么呢?
简单地说就是:首先,调用了student的拷贝构造函数用以将s初始化为plato;然后再次调用student的拷贝构造函数用以将函
数返回值对象初始化为s;接着,s的析构函数被调用;最后,returnstudent返回值对象的析构函数被调用。所以,这个什么
也没做的函数的成本是两个student的拷贝构造函数加上两个student析构函数。
但没完,还有!student对象中有两个string对象,所以每次构造一个student对象时必须也要构造两个string对象。student对象
还是从person对象继承而来的,所以每次构造一个student对象时也必须构造一个person对象。一个person对象内部有另外两
个string对象,所以每个person的构造也必然伴随另两个string的构造。所以,通过值来传递一个student对象最终导致调用了
一个student拷贝构造函数,一个person拷贝构造函数,四个string拷贝构造函数。当student对象被摧毁时,每个构造函数对
应一个析构函数的调用。所以,通过值来传递一个student对象的最终开销是六个构造函数和六个析构函数。因为
returnstudent函数使用了两次传值(一次对参数,一次对返回值),这个函数总共调用了十二个构造函数和十二个析构函
数!
在c++编译器的设计者眼里,这是最糟糕的情况。编译器可以用来消除一些对拷贝构造函数的调用(c++标准——见条款
50—— 描述了具体在哪些条件下编译器可以执行这类的优化工作,条款m20给出了例子)。一些编译器也这样做了。但在不
是所有编译器都普遍这么做的情况下,一定要对通过值来传递对象所造成的开销有所警惕。
为避免这种潜在的昂贵的开销,就不要通过值来传递对象,而要通过引用:
const student& returnstudent(const student& s)
{ return s; }
这会非常高效:没有构造函数或析构函数被调用,因为没有新的对象被创建。
通过引用来传递参数还有另外一个优点:它避免了所谓的“切割问题(slicing problem)”。当一个派生类的对象作为基类
对象被传递时,它(派生类对象)的作为派生类所具有的行为特性会被“切割”掉,从而变成了一个简单的基类对象。这
往往不是你所想要的。例如,假设设计这么一套实现图形窗口系统的类:

class window {
public:
string name() const// 返回窗口名
virtual void display() const// 绘制窗口内容
};
class windowwithscrollbars: public window {
public:
virtual void display() const;
};

每个window对象都有一个名字,可以通过name函数得到;每个窗口都可以被显示,着可以通过调用display函数实现。
display声明为virtual意味着一个简单的window基类对象被显示的方式往往和价格昂贵的windowwithscrollbars对象被显示的方
式不同(见条款36,37,m33)。
现在假设写一个函数来打印窗口的名字然后显示这个窗口。下面是一个用错误的方法写出来的函数:
// 一个受“切割问题”困扰的函数
void printnameanddisplay(window w)
{
cout << w.name();
w.display();
}
想象当用一个windowwithscrollbars对象来调用这个函数时将发生什么:
windowwithscrollbars wwsb;
printnameanddisplay(wwsb);
参数w将会作为一个windows对象而被创建(它是通过值来传递的,记得吗?),所有wwsb 所具有的作为
windowwithscrollbars对象的行为特性都被“切割”掉了。printnameanddisplay内部,w的行为就象是一个类window的对象
(因为它本身就是一个window的对象),而不管当初传到函数的对象类型是什么。尤其是,printnameanddisplay内部对
display的调用总是window::display,而不是windowwithscrollbars::display。
解决切割问题的方法是通过引用来传递w:
// 一个不受“切割问题”困扰的函数
void printnameanddisplay(const window& w)
{
cout << w.name();
w.display();
}
现在w的行为就和传到函数的真实类型一致了。为了强调w虽然通过引用传递但在函数内部不能修改,就要采纳条款21的建
议将它声明为const。
传递引用是个很好的做法,但它会导致自身的复杂性,最大的一个问题就是别名问题,这在条款17进行了讨论。另外,更
重要的是,有时不能用引用来传递对象,参见条款23。最后要说的是,引用几乎都是通过指针来实现的,所以通过引用传
递对象实际上是传递指针。因此,如果是一个很小的对象——例如int—— 传值实际上会比传引用更高效。

转载于:https://www.cnblogs.com/lancidie/archive/2010/10/28/1863424.html

相关文章:

C++ RCSP智能指针简单实现与应用

智能指针的实现代码来源博客&#xff1a;《http://blog.csdn.net/to_be_better/article/details/53570910》 修改&#xff1a;添加 get()函数&#xff0c;用以获得原始指针&#xff08;raw pointer&#xff09;。 其余思路来源《Effective C》 智能指针的实现代码如下&#xff…

末学者笔记--openstack共享组件:rabbitmq(3)

openstack共享组件&#xff1a;消息队列rabbitmq 一、MQ 全称为 Message Queue, 消息队列&#xff08; MQ &#xff09; 是一种应用程序对应用程序的通信方法。应用程序通过读写出入队列的消息&#xff08;针对应用程序的数据&#xff09;来通信&#xf…

【matlab】2019.5.10第一节上机课练习

1、计算角度和弧度的方法 例 a 17&#xff0c;b 24&#xff0c;c 26 求一个角分别以角度和弧度的方式给出 解&#xff1a; %//用余弦定理求出余弦值 pos (a b c)/(2*a*b) %//用acos求出弧度值 angle acos(pos)%//求出角度值realangle angle*180/pi2、创建矩阵的方法 A…

【学习笔记】Silverlight框架:Jounce(4)——事件通信

Prism、CM和Jounce里都有各自的事件通信机制&#xff0c;也都叫EventAggregator。 相比于Prism&#xff0c;Jounce里的EventAggregator的风格更接近CM。当然作者也是这么说的&#xff1a;The pattern here is based on the lightweight version Rob Eisenburg introduced with …

10个最常用 Windows Vista运行命令

Windows Vista以其华丽的界面和全新的操作方式受到大众喜爱&#xff0c;但实际上&#xff0c;如果你善于使用“运行”命令&#xff0c;仍然可以大大简化操作&#xff0c;甚至是发现一些常规菜单里没有的功能的。 下面就给大家讲讲Windows Vista下面的一些工具命令&#xff0c;你…

非常好的博客网站

下面的内容是转载的&#xff0c; 原文&#xff1a; https://klionsec.github.io/archives/ http://www.leifc.com/ ------------------------------------------------------------------------------ 个人始终坚信,所有不以实际入侵和防御为目的的安全研究都是耍流氓 [ 目…

反向代理Nginx

引用&#xff1a;https://baijiahao.baidu.com/s?id1600687025749463237&wfrspider&forpc 参考下图&#xff0c;正向代理用途&#xff1a;Client无法直接访问Server&#xff0c;比如谷歌FQ&#xff0c;于是请求发送给代理&#xff0c;代理可以访问Server并将其返回信息…

asp.net mvc3 Razor引擎中@使用规则小记

项目中前台用的是asp.net mvc3&#xff0c;Razor引擎&#xff08;关于Razor的介绍可以参考&#xff1a;http://weblogs.asp.net/scottgu/archive/2010/07/02/introducing-razor.aspx&#xff09;&#xff0c;深深体验到了Razor引擎的方便强大。但在编码过程中也遇到了一些问题&…

【matlab】第二章基本使用方法

&#xff08;一&#xff09;操作练习 1、整型数据类型的定义 代码实现 x int8(50)2、创建一个复数 代码实现&#xff1a; //情况1 complex(2); //输出结果&#xff1a; ans 1.0000 0.0000i//情况2 complex(1,2);//输出结果&#xff1a; ans 1.0000 2.0000i//情况3a compl…

一位美国前辈工程师的十大职业发展忠告

1、好好规划自己的路&#xff0c;不要跟着感觉走&#xff01;根据个人的理想决策安排&#xff0c;绝大部分人并不指望成为什么院士或教授&#xff0c;而是希望活得滋润一些&#xff0c;爽一些。那么&#xff0c;就需要慎重安排自己的轨迹。从哪个行业入手&#xff0c;逐渐对该行…

Vagrant安装指南

ubuntu的易用性很高&#xff0c;安装很简单&#xff0c;颜值也高&#xff0c;但是我工作中经常使用centos&#xff0c;我希望我的笔记本也是centos&#xff0c;但是&#xff0c;centos颜值太低&#xff0c;配置文件很复杂&#xff0c;不想弄这个太麻烦&#xff0c;于是&#xf…

树状数组的理解(前缀和 and 差分)

二更—— 有神仙反映数星星那个题外链炸了&#xff0c;我决定把图给你们粘一下&#xff0c;汉语翻译的话在一本通提高篇的树状数组那一章里有&#xff0c;同时也修改了一些汉语语法的错误 这段时间学了线段树组&#xff0c;当神仙们都在学kmp和hash的时候&#xff0c;我这个蒟蒻…

【java】兴唐第十九节课(内部类)

内部类&#xff1a;在类的内部定义的类叫内部类 1、有名内部类&#xff1a; &#xff08;1&#xff09;实例化时必须先实例化外部对象&#xff0c;格式&#xff1a; 外部类.内部类 对象名 外部类对象名.new.内部类名&#xff08;&#xff09;&#xff1b; 代码实现&#xff1…

持续集成之“自动化部署”

转自&#xff1a;http://www.infoq.com/cn/news/2011/07/ci-automatic-deployment 在前文《依赖管理》中&#xff0c;我们讨论了如何在代码变得庞大&#xff0c;组件增多的情况下&#xff0c;做好外部库和内部组件依赖管理&#xff0c;从而提高构建效率。可以应用的实践包括&am…

1008: [HNOI2008]越狱(计数问题)

1008: [HNOI2008]越狱 Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 11361 Solved: 4914[Submit][Status][Discuss]Description 监狱有连续编号为1...N的N个房间&#xff0c;每个房间关押一个犯人&#xff0c;有M种宗教&#xff0c;每个犯人可能信仰其中一种。如果相邻房间…

他山之石:Github的使用

一.awesome 关键字 搜索和关键字匹配的优秀项目 awesome springboot 搜索优秀的springboot相关的项目&#xff0c;包括框架、教程等 二.通过in关键词限制搜索范围 xxx in:name 项目名包含xxx的 xxx in:description 项目描述包含xxx的 xxx in:readme 项目的readme文件中包含x…

【java】兴唐第二十节课(Collection 和 ArrayList)

(一)Collection 1、如果实现 --able 名称的接口则证明该类或其子类有该功能 &#xff08;1&#xff09;实现Iterable接口代表具有迭代功能 &#xff08;2&#xff09;实现Cloneable接口代表具有克隆功能 &#xff08;3&#xff09;实现Serializable接口代表具有序列化的功能 …

服务器系统架构分析

我目前的nginx配置是拆散的&#xff0c;这样可以便于在很多个虚拟主机和目录里重用部分配置。 总体是划分为这样一个结构&#xff1a; conf/conf/nginx.confconf/proxy.confconf/rewrite.confconf/location.confconf/port.confconf/upstream.confconf/servers/conf/servers/www…

SharePoint SiteCollection 和SubWeb之间的迁移

因为各种不同的原因&#xff0c;项目里可能碰到需要将一个Site Collection迁移为一个子站点的情况。 实现这种需求只能用 内容部署功能中的导出和导入〉 SiteCollectoin to sub web 示例&#xff1a; cd C:\Program Files\Common Files\Microsoft Shared\web server extensions…

20154312曾林 - Exp1 PC平台逆向破解

1.逆向及Bof基础实践说明 1.1-实践目标 对象&#xff1a;pwn1&#xff08;linux可执行文件&#xff09;目标&#xff1a;使程序执行另一个代码片段&#xff1a;getshell内容&#xff1a; 手工修改可执行文件&#xff0c;改变程序执行流程&#xff0c;直接跳转到getShell函数。利…

web App libraries跟referenced libraries的一些问题

该博文内容经参看网上其他资料归纳所成&#xff0c;并注明出处&#xff1a; 问题一&#xff1a;myeclipse中Web App Libraries无法自动识别lib下的jar包&#xff08;http://blog.csdn.net/tiancai1202000/article/details/49178721&#xff09; myeclipse&#xff0c;lib中的ja…

无法在数据库 'ycmis2' 中运行 BEGIN TRANSACTION,因为该数据库处于回避恢复模式。...

alter database ycmis2 set EMERGENCY alter database ycmis2 set online 转载于:https://www.cnblogs.com/qanholas/archive/2011/08/03/2126347.html

exchange2003备份与恢复

exchange2003备份与恢复owa 访问的是在线访问方式。连接到服务器里的访问邮箱&#xff0c;操作邮件是在服务器上.先发一邮件永久删除&#xff0c;直接从服务器里把此邮件删除掉。删除之后。服务器里已没有此邮件。下面就是来恢复邮件临时位置随便写一个存在的路径。恢复之后装载…

入门Promise的正确姿势

Promise是异步编程的一种解决方案&#xff0c;从语法上说&#xff0c;Promise是一个对象&#xff0c;从它可以获取异步操作的消息。 Promise的基本用法 Promise构造函数接受一个函数作为参数&#xff0c;该函数的两个参数分别是resolve和reject。它们是两个函数&#xff0c;由J…

雪花算法 Java 版

雪花算法根据时间戳生成有序的 64 bit 的 Long 类型的唯一 ID 各 bit 含义&#xff1a; 1 bit: 符号位&#xff0c;0 是正数 1 是负数&#xff0c; ID 为正数&#xff0c;所以恒取 041 bit: 时间差&#xff0c;我们可以选择一个参考点&#xff0c;用它来计算与当前时间的时间差…

【matlab】第二次上机课

1、采用complex建立一个复数数组和直接创立一个复数数组&#xff0c;并计算它们的幅度。 代码实现&#xff1a; a complex(1,2);b 1 3i;length1 abs(a)lengthe abs(b)重点&#xff1a; 使用comlex创建复数 用abs求幅度&#xff08;模长&#xff09; 2、将A[0.8147, 0…

运行代码功能尝试

首先定义个文本域并且给个ID <textarea id"O_txt_1" rows"8" cols"80"> <!--要运行的代码--> </textarea> 然后定义个按钮 <input type"button" value"运行代码" οnclick"runCode(O_txt_1)&qu…

删除当前及子文件夹中的空目录

在对文件进行操作的工程中不免会出现空目录的情况&#xff0c;你想怎么去删除那些空目录一个一个去找&#xff0c;然后删除&#xff1f;不会吧&#xff0c;这也太累了&#xff0c;用批处理吧&#xff0c;帮你提高工作效率的&#xff0c;它会准确的判断然后进行删除。 echo off …

基于WebSocket实现聊天室(Node)

基于WebSocket实现聊天室(Node) WebSocket是基于TCP的长连接通信协议&#xff0c;服务端可以主动向前端传递数据&#xff0c;相比比AJAX轮询服务器&#xff0c;WebSocket采用监听的方式&#xff0c;减轻了服务器压力 本文作为学习websocket的练习&#xff0c;实现在线聊天的功能…