Effective C++ 2e Item22

STM32中RS485方向控制引脚应用详解 深入解析STM32如何通过GPIO控制RS485通讯的方向引脚,实现半双工通信的精准切换。重点讲解硬件连接与软件时序配合,确保rs485通讯稳定可靠,适用于工业控制等实际场景。 阅读详情

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

C语言中,什么都是通过传值来实现的,C++继承了这一传统并将它作为默认方式。除非明确指定,函数的形参总是通过“实参的拷贝”来初始化的,函数的调用者得到的也是函数返回值的拷贝。

正如我在本书的导言中所指出的,“通过值来传递一个对象”的具体含义是由这个对象的类的拷贝构造函数定义的。这使得传值成为一种非常昂贵的操作。例如,看下面这个(只是假想的)类的结构:

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——传值实际上会比传引用更高效。

Xilinx ISE系列教程(2):LED点灯工程、仿真、bit下载和mcs固化 bit流生成配置选项很多,支持生成bit/bin/ascii格式的bin文件,是否压缩,配置速度,配置管脚的上下拉,用户ID,数据加载速度,SPI总线宽度,是否支持Readback,程序加密等等。,在之后的文章,我们会以Xilinx Spartan-6系列FPGA XC6SLX16为例,不涉及FPGA开发知识,仅仅是工具的使用,介绍Xilinx ISE开发环境的使用教程和使用技巧。bit文件下载之后,可以看到LED按照我们设计的1s周期闪烁起来了,但是此时如果断电,重新上电程序就丢失了。... 阅读详情

相关推荐

PT32F030与MT6826S SPI通讯实战:从硬件连接到数据交互全流程解析

本文详细解析了PT32F030与MT6826S通过SPI通讯的全流程,包括硬件连接、电路设计、SPI外设初始化配置以及数据交互协议实现。通过实战案例和代码示例,帮助开发者快速掌握PT32F030与MT6826S的SPI通讯技术,实现精准的角度和速度数据采集。

weixin_29189373的博客 420

这一次,让你彻底明白Java的值传递和引用传递!

本文旨在用最通俗的语言讲述最枯燥的基本知识学过Java基础的人都知道:值传递和引用传递是初次接触Java时的一个难点,有时候记得了语法却记不得怎么实际运用,有时候会的了运...

Java后端技术 2万+

提取abaqus中节点集合的应力应变

节点是没有应力应变的,只有积分点才有,所以直接在odb文件里面用FieldOutput是整不出来的(结果是个空列表),只能用session这种让abaqus自己帮你处理。具体的代码需要根据自己操作的时候abaqus.rpy这个文件中代码的变化来更改。

蓝胖子的博客 4554

传值与传引用的区别

1、变量的引用 PHP 的引用允许你用两个变量来指向同一个内容 <?php $a="ABC"; $b =&$a; echo $a;//这里输出:ABC echo $b;//这里输出:ABC $b="EFG"; echo $a;//这里$a的值变为EFG 所以输出EFG echo $b;//这里输出EFG ?> ...

学习者小高 532

强化学习环境Gym库的介绍

Gym是一个用于进行强化学习实验的开源库,它提供了一个仿真环境,内置了多种仿真游戏,例如出租车游戏、悬崖游戏等,不同的游戏所用的网格、规则、奖励(reward)都不一样,适合为强化学习做测试。(1) 多种环境:Gym库内置了上百种实验环境,包括算法环境、简单文本环境、经典控制环境、Atari游戏环境、MuJoCo环境和机械控制环境等。(2) Gym提供多种环境供我们使用,这里以一个简单的CartPole-v0为例,首先导入gym库,然后创建一个CartPole-v0环境,通过调用gym.make方法实现。

ahah12345678的博客 2148

STM32项目实战_基于STM32F103的智能药箱设计与实现(硬件详解+软件逻辑+调试要点)

本文详细介绍了基于STM32F103的智能药箱设计与实现,涵盖硬件设计、软件逻辑和调试要点。项目核心功能包括精准提醒、药品管理和远程监控,采用STM32单片机作为主控,结合光电开关、蓝牙模块等实现智能化管理。文章还分享了电路图设计、程序优化及常见问题解决方案,为嵌入式开发爱好者提供实用参考。

weixin_28717683的博客 303

引用传递和值传递

(1)基本数据类型传值,对形参的修改不会影响实参;(2)引用类型传引用,形参和实参指向同一个内存地址(同一个对象),所以对参数的修改会影响到实际的对象; 值传递:(形式参数类型是基本数据类型):方法调用时,实际参数把它的值传递给对应的形式参数,形式参数只是用实际参数的值初始化自己的存储单元内容,是两个不同的存储单元,所以方法执行中形式参数值的改变不影响实际参数的值。 引...

weixin_30776273的博客 164

【人工智能时代】-什么是MCP?本地如何开发MCP Server

MCP(Model Context Protocol)全称模型上下文协议,是由 Anthropic 推出的一种开放标准,旨在统一大型语言模型(LLM)与外部数据源和工具之间的通信协议。掌握更多的信息控制更多的工具在没有联网搜索之前,大模型的数据来源只有训练数据的时候以及推理我们告诉他的东西。所以后来的rag、知识库都是让AI掌握更多的信息,而MCP的出现是为了让大模型控制更多的工具,赋予更多能力。在本篇文章中,基于 Python 完成了一个简单的 MCP Server 的开发。

xiaoli8748的专栏 1993

传引用与传值的区别

简单的来说: 传值:当一个变量的值赋予另外一个变量时,改变其中一个变量的值,将不会影响到另外一个变量; 变量默认是传值; 传引用:当一个变量的值赋予另外一个变量时,改动新的变量将影响原始的变量;使用引用赋值,简单的就是将&符号加到将要赋值的 变量前;相当于别名; 对象默认是传引用; 使用场景:对于较...

syy_666的博客 1617

深度学习在传感器数据分析中的创新应用

传感器是物理世界与数字世界的“接口”,其生成的高维、时序、多源数据蕴含着设备状态、环境变化、用户行为等关键信息。传统传感器数据分析依赖手工特征工程与简单机器学习模型,难以应对复杂场景下的非线性、长程依赖与实时性需求。深度学习通过自动特征提取非线性建模与端到端学习,成为传感器数据处理的革命性工具。本文从概念基础理论框架架构设计实现机制到实际应用,系统拆解深度学习与传感器数据的融合逻辑,结合工业预测维护医疗健康监测智能交通等场景,揭示其技术边界与未来演化方向。

大数据洞察的博客 568

【100分】最长子字符串的长度

题目要求在一个环形字符串中找出字符 ‘o’ 出现了偶数次的最长子字符串长度。这里的字符串是一个环形的,即首尾相连。代码统计了字符串中字符 ‘o’ 的出现次数,并根据其奇偶性返回相应的最长子字符串长度。当 ‘o’ 的数量为偶数时,直接返回字符串长度;当 ‘o’ 的数量为奇数时,返回长度减去 1。给你一个字符串 s,字符串s首尾相连成一个环形 ,请你在环中找出 ‘o’ 字符出现了偶数次最长子字符串的长度。【最长子字符串的长度】

weixin_54707168的博客 898

effective c++ 20 传引用代替传值

effective c++ 20 传引用代替传值

小x的博客 662

Pdu Router(PduR)模块

1. PduR路由基本功能 2. PduR总线网关功能 3. PduR缓存机制 4. 总结

weixin_43759043的博客 3850

Effective C++ 2e Item30

条款30: 避免这样的成员函数:其返回值是指向成员的非const指针或引用,但成员的访问级比这个函数要低使一个成员为private或protected的原因是想限制对它的访问,对吗?劳累的编译器要费九牛二虎之力来确保你设置的访问限制不被破坏,对不对?所以,写个函数来让用户随意地访问受限的成员没多大意义,对不对?如果你确实认为有意义,那么请反复阅读本段,直到你不这样认为为止。实际编程中很容易

lostmouse的专栏 1627

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

Effective C++ 2e Item50

条款50: 提高对C++的认识C++中有很多 "东西":C,重载,面向对象,模板,例外,名字空间。这么多东西,有时让人感到不知所措。怎么弄懂所有这些东西呢?C++之所以发展到现在这个样子,在于它有自己的设计目标。理解了这些设计目标,就不难弄懂所有这些东西了。C++最首要的目标在于:· 和C的兼容性。很多很多C还存在,很多很多C程序员还存在。C++利用了这一基础,并建立在 ---- 我是

lostmouse的专栏 1376

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

Effective C++ 2e Item23

条款23: 必须返回一个对象时不要试图返回一个引用据说爱因斯坦曾提出过这样的建议:尽可能地让事情简单,但不要过于简单。在C++语言中相似的说法应该是:尽可能地使程序高效,但不要过于高效。一旦程序员抓住了“传值”在效率上的把柄(参见条款22),他们会变得十分极端,恨不得挖出每一个隐藏在程序中的传值操作。岂不知,在他们不懈地追求纯粹的“传引用”的过程中,他们会不可避免地犯另一个严重的错误:传递

lostmouse的专栏 978

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

Effective C++ 2e Item46

条款46: 宁可编译和链接时出错,也不要运行时出错除了极少数情况下会使C++抛出异常(例如,内存耗尽 ---- 见条款7)外,运行时错误的概念和C++没什么关系,就象在C中一样。没有下溢,上溢,除零检查;没有数组越界检查,等等。一旦程序通过了编译和链接,你就得靠自己了 ---- 一切后果自负。这很象跳伞运动,一些人从中找到了刺激,另一些人则吓得摔成了残废。这一思想背后的动机当然在于效率:没有运

lostmouse的专栏 1167

Effective C++ 2e Item2

 条款2:尽量用而不用是的,scanf和printf很轻巧,很高效,你也早就知道怎么用它们,这我承认。但尽管他们很有用,事实上scanf和printf及其系列还可以做些改进。尤其是,他们不是类型安全的,而且没有扩展性。因为类型安全和扩展性是C++的基石,所以你也要服从这一点。另外,scanf/printf系列函数把要读写的变量和控制读写格式的信息分开来,就象古老的FORTRAN那样。是该向

lostmouse的专栏 1069

[翻译] Effective C++, 3rd Edition, 附录 B. 第二和第三版之间的 Item 映射

附录 B. 第二和第三版之间的 Item 映射作者:Scott Meyers译者:fatalerror99 (iTePubs Nirvana)发布:http://blog.youkuaiyun.com/fatalerror99/Effective C++ 的第三版在很多方面与第二版不同,其中最引人注目的是它包含很多新的信息。然而,第二版的大部分内容依然保留在第三版中,虽然经常会改变形式和位置

艺术编程 5819

Effective C++ Item 22 将成员变量声明为private

本文为senlie原创,转载请保留此地址:http://blog.youkuaiyun.com/zhengsenlie 经验1:切记将成员变量声明为private。这可赋予客户访问数据的一致性、可细微划分访问控制、允诺约束条件获得保证,并提供class作者以充分的实现弹性 经验2:protected并不比public更具封装性     从封装的角度来看,其实只有两种访问权限:private(

709

Effective c++ Item 28 不要返回指向对象内部数据(internals)的句柄(handles)

假设你正在操作一个Rectangle类。每个矩形可以通过左上角的点和右下角的点来表示。为了保证一个Rectangle对象尽可能小,你可能决定不把定义矩形范围的点存储在Rectangle类中,而是把它放入一个辅助结构体中,Rectangle中声明一个指向它的指针就可以了: 1 class Point { // class for repre

小小小程序员的博客 218

读书笔记 effective c++ Item 43 了解如何访问模板化基类中的名字

1. 问题的引入——派生类不会发现模板基类中的名字 假设我们需要写一个应用,使用它可以为不同的公司发送消息。消息可以以加密或者明文(未加密)的方式被发送。如果在编译阶段我们有足够的信息来确定哪个信息会被发送到哪个公司,我们可以使用基于模板的解决方案: 1 class CompanyA { 2 public: 3 ... 4 void sendCleartext(cons...

weixin_34406086的博客 116

读书笔记 effective c++ Item 10 让赋值运算符返回指向*this的引用

一个关于赋值的有趣的事情是你可以将它们链在一起: 1 int x, y, z; 2 3 x = y = z = 15; // chain of assignments 同样有趣的是赋值采用右结合律,所以上面的赋值链被解析成下面这个样子: 1 x = (y = (z = 15)); 在这里,15被赋值给z,然后赋值的结果(更新的z)被赋值给y,再然后赋值的结果(更新的Y)被赋值给x。 ...

HarlanC的专栏 152

【C++11新特性:22】—— C++11引用限定符的用法

c++11

holly的专栏 415
上一篇: Effective C++ 2e Item21
下一篇: Effective C++ 2e Item23
lostmouse
博客等级 码龄26年 22粉丝 63原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值