[翻译] Effective C++, 3rd Edition, Item 20: 用 pass-by-reference-to-const(传给 const 引用)取代 pass-by-value(传值)(上)

h5页面调用微信JS-SDK分享到微信好友或者朋友圈的配置 1.引入js <script type="text/javascript" src="http://res.wx.qq.com/open/js/jweixin-1.2.0.js"></script> 2.在页面添加配置 <script type="text/javascript"> var url = location.href.. 阅读详情

Item 20: 用 pass-by-reference-to-const(传 const 引用)取代 pass-by-value(传值)

作者:Scott Meyers

译者:fatalerror99 (iTePub's Nirvana)

发布:http://blog.youkuaiyun.com/fatalerror99/

缺省情况下,C++ 以 by value(传值)方式将 objects 传入或传出函数(这是一个从 C 继承来的特性)。除非你特别指定其它方式,否则 function parameters(函数形参)就会以 copies of the actual arguments(当前实参的拷贝)进行初始化,而且函数的调用者会取回函数返回值的一个 copy(拷贝)。这些拷贝由 objects 的 copy constructors(拷贝构造函数)生成。这就使得 pass-by-value(传值)成为一个代价不菲的操作。例如,考虑如下 class hierarchy(类继承体系):

class Person {
public:
  Person();                          // parameters omitted for simplicity
  virtual ~Person();                 // see Item 7 for why this is virtual
  ...

private:
  std::string name;
  std::string address;
};

class Student: public Person {
public:
  Student();                         // parameters again omitted
  virtual ~Student();
  ...

private:
  std::string schoolName;
  std::string schoolAddress;
};

(原文并没有显式声明 Student destructor 为 virtual,根据作者网站勘误修改——译者注。)

现在,考虑以下代码,在此我们调用一个函数 validateStudent,它得到一个 Student argument(实参)(以 by value(传值)方式),并返回它是否有效的结果:

bool validateStudent(Student s);           // function taking a Student
                                           // by value

Student plato;                             // Plato studied under Socrates

bool platoIsOK = validateStudent(plato);   // call the function

当这个函数被调用时会发生什么呢?

很明显,Student copy constructor(拷贝构造函数)被调用,用 plato 来初始化 parameter(形参)s。同样明显的是,当 validateStudent 返回时,s 就会被销毁。所以这个函数的 parameter-passing cost(参数传递成本)是一次 Student copy constructor(拷贝构造函数)的调用和一次 Student destructor(析构函数)的调用。

但这还不是全部。一个 Student object 内部包含两个 string objects,所以每次你构造一个 Student object 的时候,你也必须构造两个 string objects。一个 Student object 还要从一个 Person object 继承,所以每次你构造一个 Student object 的时候,你也必须构造一个 Person object。一个 Person object 内部又包含另外两个 string objects,所以每个 Person 的构造也承担着另外两个 string 的构造。最终,以 by value(传值)方式传递一个 Student object 的后果就是导致一次 Student copy constructor(拷贝构造函数)的调用,一次 Person copy constructor(拷贝构造函数)的调用,以及四次 string copy constructor(拷贝构造函数)调用。当 Student object 的拷贝被销毁时,每一次 constructor call(构造函数调用)都有一次 destructor call(析构函数调用)对应,所以以 by value(传值)方式传递一个 Student 的全部成本是六次 constructors(构造函数)和六次 destructors(析构函数)!

好了,这是正确的和值得的行为。毕竟,你想要你的所有 objects 都得到可靠的初始化和销毁。尽管如此,如果有一种办法可以绕过所有这些构造和析构过程,那就更好了,这就是:pass by reference-to-const(传 const 引用):

bool validateStudent(const Student& s);

这样做非常有效:没有 constructors(构造函数)和 destructors(析构函数)被调用,因为没有新的 objects 被创建。被修改的 parameter declaration(形参声明)中的 const 是非常重要的。validateStudent 的最初版本以 by value(传值)方式取得一个 Student parameter(形参),所以调用者知道它们屏蔽了函数对它们传入的 Student 的任何可能的改变;validateStudent 只能改变它的一个 copy(拷贝)。现在 Student 以 by reference(传引用)方式传递,将它声明为 const 也是必要的,否则调用者必然担心 validateStudent 改变了它们传入的 Student。

以 by reference(传引用)方式传递 parameter(形参)还可以避免 slicing problem(切断问题)。当一个 derived class object(派生类对象)作为一个 base class object(基类对象)(以传值)方式被传递,base class copy constructor(基类拷贝构造函数)被调用,而那些使得 object 的行为像一个 derived class object(派生类对象)的特殊特性被 "sliced"(“切断”)了。只给你留下一个 simple base class object(纯粹基类对象)——这没什么可吃惊的,因为是一个 base class constructor(基类构造函数)创建了它。这几乎绝不是你想要的。例如,假设你在一组实现一个图形窗口系统的 classes 上工作:

class Window {
public:
  ...
  std::string name() const;           // return name of window
  virtual void display() const;       // draw window and contents
};

class WindowWithScrollBars: public Window {
public:
  ...
  virtual void display() const;
};

所有 Window objects 都有一个名字,你能通过 name 函数得到它,而且所有的窗口都可以显示,你可一个通过调用 display 函数来做到这一点。display 为 virtual(虚拟)的的事实告诉你:一个 simple base class(纯粹基类)的 Window objects 的显示方法有可能不同于专门的 WindowWithScrollBars objects 的显示方法(参见 Items 34 和 36)。

现在,假设你想写一个函数打印出一个窗口的名字,并随后显示这个窗口。以下这个函数的写法是错误的:

void printNameAndDisplay(Window w)         // incorrect! parameter
{                                          // may be sliced!
  std::cout << w.name();
  w.display();
}

考虑当你用一个 WindowWithScrollBars object 调用这个函数时会发生什么:

WindowWithScrollBars wwsb;

printNameAndDisplay(wwsb);

parameter(形参)w 将被作为一个 Window object 构造——它是以 by value(传值)方式被传递的,记得吗?而且使 wwsb 表现得像一个 WindowWithScrollBars object 的特殊信息都被切断了。在 printNameAndDisplay 中,全然不顾传递给函数的那个 object 的类型,w 将始终表现得像一个 class Window 的 object(因为它就是一个 class Window 的 object)。特别是,在 printNameAndDisplay 中对 display 的调用将 always(总是)调用 Window::display,绝不会是 WindowWithScrollBars::display。

绕过 slicing problem(切断问题)的方法就是以 by reference-to-const(传 const 引用)方式传递 w:

void printNameAndDisplay(const Window& w)   // fine, parameter won't
{                                           // be sliced
  std::cout << w.name();
  w.display();
}

现在 w 将表现得像实际传入的那种窗口。

(本篇未完,点击此处,接下篇)

从零构建AI辅助逆向分析环境:JADX-MCP与LLM的实战集成指南 本文详细介绍了如何从零构建AI辅助逆向分析环境,通过集成JADX-MCP与LLM技术,提升Android应用逆向工程效率。文章涵盖环境搭建、插件安装、MCP服务器配置及实战案例分析,帮助开发者快速掌握AI在代码理解、漏洞挖掘和自动化文档生成中的应用。 阅读详情

相关推荐

芯片测试中的DC与AC模式详解:如何选择最适合你的测试方案?

本文深入解析了芯片测试中DC(直流)与AC(交流)模式的核心差异与互补性。DC测试关注静态电气特性,如固定型故障;而AC测试则聚焦于芯片在额定工作速度下的动态时序行为。文章从VLSI设计、电路模块、成本约束、可测试性设计和应用场景五大维度,提供了如何根据工艺节点、工作频率及具体需求,在testMode配置、时钟复位策略和输入输出接口设计中,平衡两种测试模式以制定最优方案的决策框架。

bb456的博客 1088

Item 20:传递常量引用比传值更好 Effective C++笔记

Item 20: Prefer pass-by-reference-to-const to pass-by-value C++函数的参数和返回值默认采用传值的方式,这一特性是继承自C语言的。如果不特殊指定, 函数参数将会初始化为实参的拷贝,调用者得到的也是返回值的一个副本。 这些拷贝是通过调用对象的拷贝构造函数完成的,正是这一方法的调用使得拷贝的代价可能会很高。 通常来讲,传递常量

分享技术,共建开放平台 1310

Python深度学习基于Tensorflow(17)基于Transformer的图像处理实例VIT和Swin-T

定义窗口注意力机制,与普通的注意力机制不同,其是在各个窗口中执行注意力机制。从中可以看到,其创新点主要是将图片进行拆分作为序列数据带入。差不多,主要实现的是一个下采样的算法过程;相同位置的像素绑定到一起构成一张新的。模型定义完毕后,初始化模型并开始训练。由于层之间重复性出现,可以定义一个。中,这里先实现拆分图片类。进行操作时我们定义三个类。定义一个多头注意力机制类。的数据,导入数据如下。导入数据,这里同样用。

bigcrab的博客 1635

C++ 传值,传引用和传指针-参考Effective C++ 第三版Item20~21

拷贝构造函数 copy construtor  为什么有copy construtor? 函数的传参数有三种方式:传值,传引用和传指针。传值会产生对象的副本,而后两者是传的是对象的别名和对象的指针。 问题来了: 如果传值方式一个比较大的对象时候,在产生对象副本的时候一般会触发对象的构造函数。 一般情况下构造函数会完成一些初始化工作,但是很有可能传进来的对象已经被改变了,如果调用构造函数

在路上 的专栏 1431

智能车竞赛环岛处理全解析:从图像识别到状态机控制

在嵌入式系统与自动控制领域,状态机是管理复杂系统行为、实现清晰逻辑流的核心设计模式。其原理在于定义一组有限的状态,以及触发状态迁移的事件和条件,从而将连续、模糊的过程分解为离散、确定的阶段。这种技术在机器人导航、工业自动化等场景中价值显著,尤其适用于需要应对多种场景、做出序列化决策的场合。在智能车竞赛中,赛道元素识别与路径跟踪是典型应用,其中环岛作为连续闭环的复杂场景,对系统的鲁棒性和决策连贯性提出了更高要求。通过图像处理提取赛道边线、宽度和曲率等特征,结合陀螺仪等传感器数据,可以构建一个专门处理环岛的状态

weixin_34033624的博客 564

SUMO使用教程(一)

本人的sumo视频入门课程,希望支持哦。https://edu.youkuaiyun.com/course/detail/25834 SUMO是一款交通仿真软件,其余可自行百度。教程一主要展示一下如何运行一个仿真实例。当然,这只是实现方法中的一种。 准备: 1.SUMO软件 2.osm地图文件 SUMO可去官网下载,解压后就可以使用,图形界面软是在解压后bin文件夹下的sumo-gui.exe。...

lyx的专栏 3万+

[翻译] Effective C++, 3rd Edition, Item 20: 用 pass-by-reference-to-const(传 const 引用)取代 pass-by-value(传值)(下)

(点击此处,接上篇)如如果你掀开编译器的盖头偷看一下,你会发现 references(引用)一般是作为 pointers(指针)实现的,所以以 by reference(传引用)方式传递某物实际上通常意味着传递一个 pointer(指针)。由此可以得出结论,如果你有一个 built-in typ(内建类型)的 object(例如,一个 int),以 by value(传值)方式传递它常常比 b

艺术编程 2704

解密Android相机框架:从HAL到应用层的全链路解析

本文深入解析Android相机框架的全链路架构,从应用层到硬件抽象层(HAL)再到驱动层,详细介绍了CamX-CHI架构的设计哲学与实战应用。通过HAL3接口、V4L2交互机制及Camera2 API的最佳实践,帮助开发者优化相机性能,解决多摄切换、预览卡顿等常见问题,提升成像效率与稳定性。

weixin_29238147的博客 408

深挖C++:类和对象(中)

(先进后出)

xys_678的博客 254

USB 基础学习:USB 电气信号介绍

USB电气信号技术解析 USB通信的核心是差分信号传输技术,通过D+和D-两根线传输相位相反的电压信号实现数据通信。USB信号系统包含三类关键信号:差分数据信号(抗干扰性强)、单端状态信号(用于设备识别)和电源信号(提供5V供电)。USB2.0采用400mV电压摆幅和NRZI编码,支持低速(1.5Mbps)至高速(480Mbps)传输;USB3.0则新增专用差分对,数据速率可达20Gbps。物理层通过上拉/下拉电阻检测设备连接,高速模式需特殊握手协议。抗干扰设计的关键在于保持差分线等长、阻抗匹配和使用共模扼

浩瀚之水的专栏 1784

HTML满屏跳动的爱心(完整代码)

HTML语言实现满屏跳动爱心的完整代码。

AIGC 12万+

ThunderKittens 深度解析:一个不基于 MLIR 的 C++ 模板 GPU DSL

Triton 是"让更多人能写 GPU kernel",ThunderKittens 是"让会写的人写得更爽"。前者用 MLIR 换易用性,后者用 C++ 模板换可控性。它们不是替代关系,而是覆盖了 GPU 编程光谱的两端。ThunderKittens 的出现,本质上是对"GPU DSL 必须基于 MLIR"这一默认假设的挑战。编译期类型系统本身就可以是一个强大的 DSL 基础设施,不需要中间 IR,不需要黑盒 pass,也能实现 tile 级抽象和零运行时开销。

Maxwell_Newton的博客 349

YOLOv10改进策略【Neck】| arXiv 2025 分层多头注意力HMHA 子空间拆分提多样 + 通道重排降冗余,强化目标检测精度

HMHA(Hierarchical Multi-Head Attention,分层多头注意力)旨在解决传统多头注意力(MHA)在图像恢复任务中的核心缺陷——特征冗余。传统MHA将通道均匀拆分给各个头,导致不同头学习的子空间信息相似,进而出现多头关注同一区域、忽略退化区域修复的问题,限制了模型的特征表达能力。HMHA通过“分层子空间拆分+通道重排序”策略,让每个头学习差异化、互补的上下文特征,从根源上缓解冗余,提升图像恢复任务中对复杂退化场景的特征捕捉能力。

Limiiiing的博客 106

C++移动构造函数详解

移动构造函数是C++11引入的一种特殊构造函数,用于从临时对象(右值)"窃取"资源来初始化新对象,而不是进行昂贵的深拷贝。char* data;public:// 移动构造函数:参数是右值引用 &&// 关键:把源对象的资源置空。

拒绝AI文,努力在AI时代写出感动人心的好文章 394

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

跟我学c++中级篇—类型定义的方式

本文系统阐述了编程中类型定义的重要性及c++中的实现方式。重点对比了宏定义、typedef与modern using三种机制,指出using在可读性、灵活性和模板支持上的优势,推荐优先使用。同时提醒开发者注意别名滥用、类型歧义、错误信息冗长等问题,强调统一规范与合理设计。最终结论:在现代c++开发中,应以using为主,兼顾简洁性与可维护性,避免陷入命名混乱与编译困境。

fpcc的专栏 88

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

【C++——类和对象(上)】筑基篇:以封装奠定面向对象之基

本文系统梳理C++类与对象的核心知识点,涵盖类的定义、访问限定符(public/private)、类作用域、对象实例化及内存布局。重点解析封装思想、this指针机制、空类大小为1字节的原因,以及类与对象在内存中的存储差异。通过对比C语言结构体与C++类,揭示面向对象设计的优势:数据保护、接口统一、命名隔离与代码可维护性提升。内容由浅入深,适合作为学习笔记与复习参考。

2602_95684935的博客 492

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

C++ 函数模版

模板参数:● template ,T 就是类型占位符,名字可以自定义,习惯用T● 占位符可以在函数返回值、形参、函数内部当作类型使用使用要点模板代码一般写在头文件:编译器需要看到完整模板源码,才能在每个编译单元完成实例化全局函数、类内成员函数都可以写成模板实例化发生在编译阶段,不是运行时隐式实例化:调用时编译器自动推导实参类型,生成对应版本函数。

2501_94041724的博客 307

C++ 运行时多态与虚函数机制全面剖析【面试重点】

构成C++ 运行时多态必须是基类的指针或者引用调用虚函数。被调用的函数必须是虚函数,并且派生类必须对该虚函数完成重写/覆盖(Override)。只有基类的指针或引用才能既指向基类对象,又指向派生类对象(切片机制);而只有完成了虚函数重写,派生类与基类才具备不同的函数行为,达到“指向谁就调用谁”的效果。在类成员函数的前面加上virtual关键字修饰,该成员函数就成为了虚函数。public:// 成员函数前加 virtual 修饰,声明为虚函数。

Fuyo_0209的博客 450

#SQLite 数据库笔记一:(概念 / 终端指令 / SQL 语句 / C 编程 / 回调函数)

数据库 = 数据的仓库,假设数据是海量。能力说明存储数据持久化保存,程序退出数据还在统计数据、分组、排序数据管理增、删、改、查(CRUD)嵌入式设备上也会用数据库(单片机网关、路由器、工业 HMI 都常见),因为 sqlite 体积小、无需服务进程、单文件即可。数据库 = 库 > 表 > 记录(行) > 字段(列)sqlite3 = 开源、C 写的、单文件、免安装、最多 2T 的嵌入式关系型数据库。

lwq20051110的博客 251

1-10 PostgreSQL中多表连接全解:inner/left/right/full/cross join

本文基于 PostgreSQL 15+ 完整拆解 5 种主流连接:内连接、左连接、右连接、全连接、笛卡尔连接。结合可落地的测试数据,演示每种连接的执行逻辑、返回结果,重点剖析 NULL 值对 JOIN 匹配的核心影响,彻底解决多表连接数据丢失、匹配异常、结果错乱的问题。

zhangjw34的专栏 105

【C++ 基础入门】C++继承(下):问题、底层原理与组合

C++继承(菱形继承的问题、虚继承原理、继承与组合、构造顺序)

2601_96587588的博客 802

蓝桥杯·全国青少年信息素养大赛·电子学会Python/C++考级 备考:莫队算法离线区间查询详解

备赛进行时:第十八届蓝桥杯软件赛报名已于 2026-10-08 10:00 开启、12-18 14:00 截止,省赛定在 2027-04-10(C/C++·Python 程序设计 9:00–13:00);中国电子学会 2026 年 12 月软件编程(C/C++ 一级至十级、Python 一级至六级)考级报名 9/23–12/2 16:00、统考 12-12;全国青少年信息素养大赛 2026—2027 赛季算法应用主题赛也已进入复盘练兵窗口。

IT_Scratch的博客 170

c++ explicit关键字详解

explicit。

拒绝AI文,努力在AI时代写出感动人心的好文章 151

825 mW 顶上 212 °C:ASM491S 的 105 °C/W、250 mA 短路电流与热关断

25 °C 环境里结温 34.1 °C,125 °C 环境里结温 134.1 °C——后者已经离 150 °C 只有 16 °C 了,但还在范围内。换个角度看同一件事:从 25 °C 和从 125 °C 起算,温升都是 86.6 °C,但一个到 111.6 °C(还在范围内),一个到 211.6 °C(远远超限)。这个算例里 20 °C/W 是我假设的,实际值取决于机壳材料、密封程度、有没有风扇,跨度可以从 5 °C/W 到 50 °C/W 以上。外面 50 °C 的机箱,盒内就是 63.9 °C。

ANSILIC的博客 393

《C++ 项目:手写大模型接入 SDK》Gemini接口设计(上):初始化、全量响应实现和测试

本文系统完成了Gemini模型通过OpenAI兼容API的接入开发,基于Apifox验证接口规范,设计并实现GeminiProvider类,涵盖初始化、状态检测、全量响应等核心功能。重点处理了代理配置强制校验、Bearer鉴权、JSON序列化与响应解析等工程细节,并通过GTest与CMake完成单元测试,验证链路可用性。项目遵循策略模式,保持与DeepSeek、ChatGPT等模型的统一抽象接口,显著提升SDK可扩展性。

2501_91275995的博客 398

【容斥原理计数】题解:P9489 ZHY的表示法_容斥原理_计数问题_前缀和问题转化_算法竞赛C++

给定 $ n $ 个正整数,求区间 $[l,r]$ 中有多少正整数 $ x $ 可表示为 $ \sum \lfloor \frac{y}{x_i} \rfloor $。通过二分确定最大 $ y $,转化为求 $ 1 \sim H $ 中是某些 $ x_i $ 倍数的数的个数,利用容斥原理与 DFS 枚举子集计算,结合 LCM 预判剪枝及去重优化,时间复杂度 $ O(2^n \log V) $,可高效处理 $ r \le 10^9 $ 的数据。

YLCHUP的博客 106
上一篇: [翻译] Effective C++, 3rd Edition, Item 19: 视 class design(类设计)为 type design(类型设计)
下一篇: [翻译] Effective C++, 3rd Edition, Item 20: 用 pass-by-reference-to-const(传 const 引用)取代 pass-by-value(传值)(下)
fatalerror99
博客等级 码龄24年 166粉丝 20原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值