[翻译] Effective C++, 3rd Edition, Item 21: 当你必须返回一个 object(对象)时不要试图返回一个 reference(引用)(上)

线性变换与基变换的本质区别:矩阵是坐标系绑定的快照 线性变换描述向量空间中的几何动作(如旋转、缩放),而基变换仅改变向量在不同坐标系下的数值表示;二者虽均通过矩阵实现,但物理意义截然不同——矩阵本身并非变换,而是特定基下对变换的数字化记录。理解这一区分是掌握相似变换、特征值不变性、PCA降维及SVD分解的前提:例如PCA本质是将数据协方差矩阵在特征向量基下对角化,而SVD则分别优化输入与输出空间的观测基。工程实践中,混淆二者将直接导致坐标系错位、降维失效或神经网络调试失败。 阅读详情

Item 21: 当你必须返回一个 object(对象)时不要试图返回一个 reference(引用)

作者:Scott Meyers

译者:fatalerror99 (iTePub's Nirvana)

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

一旦程序员抓住 objects pass-by-value(传值)的效率隐忧(参见 Item 20),很多人就会成为狂热的圣战分子,誓要根除 pass-by-value(传值)的罪恶,无论它隐藏多深。他们不屈不挠地追求 pass-by-reference(传引用)的纯度,但他们全都犯了一个致命的错误:他们开始传递引向并不存在的 objects 的引用。这可不是什么好事。

考虑一个代表 rational numbers(有理数数字)的 class,包含一个将两个 rationals(有理数)相乘的函数:

class Rational {
public:
  Rational(int numerator = 0,               // see Item 24 for why this
           int denominator = 1);            // ctor isn't declared explicit

  ...

private:
  int n, d;                                 // numerator and denominator

friend
   const Rational                           // see Item 3 for why the
     operator*(const Rational& lhs,         // return type is const
               const Rational& rhs);
};

operator* 的这个版本以 by value(传值)方式返回它的结果,而且如果你没有担心那个 object 的构造和析构的成本,你就是在推卸你的专业职责。如果你不是迫不得已,你并不想为这样的一个 object 付出成本。所以问题就在这里:你是迫不得已吗?

哦,如果你能用返回一个 reference(引用)来作为代替,你就不是迫不得已。但是,请记住一个 reference(引用)仅仅是一个 name(名字),一个 existing(实际存在)的 object 的 name(名字)。无论何时只要你看到一个 reference(引用)的 declaration(声明),你应该立刻问自己它是什么东西的另一个名字,因为它必定是 something(某物)的另一个名字。在这个 operator* 的情况下,如果函数返回一个 reference(引用),它必须返回某个已存在的而且其中包含两个 objects 相乘的产物的 Rational object 的 reference(引用)。

当然没有什么理由期望这样一个 object(对象)在调用 operator* 之前就存在。也就是说,如果你有

Rational a(1, 2);                        // a = 1/2
Rational b(3, 5);                        // b = 3/5

Rational c = a * b;                      // c should be 3/10

似乎没有理由期望那里碰巧已经存在一个值为十分之三的有理数。不是这样的,如果 operator* 返回这样一个数的 reference(引用),它必须自己创建那个数字 object。

一个函数创建一个新 object 仅有两种方法:在 stack(栈)上或者在 heap(堆)上。stack(栈)上的生成物通过定义一个 local variable(局部变量)而生成。使用这个策略,你可以用这种方法试写 operator*:

const Rational& operator*(const Rational& lhs,   // warning! bad code!
                          const Rational& rhs)
{
  Rational result(lhs.n * rhs.n, lhs.d * rhs.d);
  return result;
}

你可以立即否决这种方法,因为你的目标是避免一个 constructor(构造函数)调用,而 result 正像任何其它 object 一样必须被构造。一个更严重的问题是这个函数返回一个引向 result 的 reference(引用),但是 result 是一个 local object(局部对象),而 local objects(局部对象)在函数退出时被销毁。那么,这个 operator* 的版本不会返回引向一个 Rational 的 reference(引用)——它返回引向一个 ex-Rational(前 Rational);一个 former Rational(曾经的 Rational);一个空洞的、恶臭的、腐败的,从前是一个 Rational 但永不会再是的尸体的 reference(引用),因为它已经被销毁了。任何调用者甚至于没有来得及匆匆看一眼这个函数的返回值就立刻进入了未定义行为的领地。事实就是,任何返回一个 reference to a local object(引向局部对象的引用)的函数都是错误的。(对于任何返回一个 pointer to a local object(指向局部对象的指针)的函数同样成立。)

那么,让我们考虑一下在 heap(堆)上构造一个 object 并返回引向它的 reference(引用)的可能性。heap-based objects(基于堆的对象)通过使用 new 而开始存在,所以你可以像这样写一个 heap-based(基于堆)的 operator*:

const Rational& operator*(const Rational& lhs,   // warning! more bad
                          const Rational& rhs)   // code!
{
  Rational *result = new Rational(lhs.n * rhs.n, lhs.d * rhs.d);
  return *result;
}

哦,你还是必须要付出一个 constructor(构造函数)调用的成本,因为通过 new 分配的内存要通过调用一个适当的 constructor(构造函数)进行初始化,但是现在你有另一个问题:谁适合 delete 你用 new 做出来的 object?

即使调用者尽职尽责且一心向善,为了在下面这样合理地使用的情境中避免泄漏,它们也做不了什么:

Rational w, x, y, z;

w = x * y * z;                     // same as operator*(operator*(x, y), z)

这里,在同一个语句中有两个 operator* 的调用,因此 new 被使用了两次,这两次都需要使用 delete 来取消。但是 operator* 的客户没有合理的办法进行那些调用,因为他们没有合理的办法取得隐藏在通过调用 operator* 返回的 references(引用)后面的 pointers(指针)。这是一个早已注定的 resource leak(资源泄漏)。

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

HTTP 代理服务器的设计与实现 HTTP 代理服务器的设计与实现 首先当然是最重要的代码:github地址 1) 实验目的 熟悉并掌握 Socket 网络编程的过程与技术;深入理解 HTTP 协议,掌握 HTTP 代理服 务器的基本工作原理;掌握 HTTP 代理服务器设计与编程实现的基本技能。 2) 实验内容 基于 java 实现了基本功能与全部附加功能,并增加了 https 的支持 (1) 设计并实现一个基本 HTTP 代理服... 阅读详情

相关推荐

Python入门之机器学习(非常详细)篇幅拉满,一般人看不完!

什么是机器学习?监督学习 supervised learning;非监督学习 unsupervised learning;半监督学习 semi-supervised learning;强化学习 reinforcement learning;监督学习是不断向计算机提供数据(特征),并告诉计算机对应的值(标签),最后通过大量的数据,让计算机自己学会判断和识别。例如Google Photo,你在APP中输入狗,这时就会弹出与狗相关的图片,这背后使用的算法就是监督学习。

xiaolinyui的博客 2万+

Effective_C++:23、当你必须传回object时,不要尝试传回reference

23、当你必须传回object时,不要尝试传回reference 1、必须传回object 当考虑传回reference时,一定要想想这个reference的另一个名称是啥,他指的是哪个对象,这个对象是否存在于调用函数中。否则,函数无法返回一个临时对象的reference。即需要传回一个产生的临时对象时,必须传回object。 2、尝试传回reference 当需要传回临时对象的值时,尝试...

l1dian11的博客 216

新一代XMOS会议四麦方案主控芯片 XVF3800

2023年,知名的音频芯片制造厂商 XMOS 正式发布了新一代会议麦阵芯片片 XVF3800 ,这款芯片采用最先进的四麦音频算法,可在嘈杂的环境中提供高质量语音,算法支持圆形、条形阵列,可根据应用场景组成不同的产品形态。传统的麦阵解决方案,不仅需要捕捉会议中的人声,也会同时捕捉环境中的其他噪声,而 XMOS 先进的语音算法解决了这一问题。声学回声消除功能可消除麦克风音频输入的回声,同时减少远端回声,使 XVF3800 能提取清晰的语音信号。• 180度拾音条形麦克风阵列。• 360度拾音环形麦克风阵列。

PawPaw_electron的博客 785

[翻译] Effective C++, 3rd Edition, Item 21: 当你必须返回一个 object(对象)时不要试图返回一个 reference(引用)(下)

(点击此处,接上篇)但是也许你注意到无论是 on-the-stack(在栈上)的还是 on-the-heap(在堆上)的方法,为了从 operator* 返回的每一个结果都不得不容忍一次 constructor(构造函数)的调用。也许你还记得我们最初的目标是避免这样的 constructor(构造函数)调用。也许你认为你知道一种方法能避免除一次以外的几乎全部的 constructor(构造函数

艺术编程 2856

Python 使用Pygame库实现足球游戏:创建一个球员和一个足球,并使玩家能够控制球员的移动。游戏将包括碰撞检测、简单的物理模拟,以及基本的用户输入处理

本文将介绍如何使用Python和Pygame库创建一个简单的足球游戏。我们将搭建一个基本的游戏环境,创建一个球员和一个足球,并使玩家能够控制球员的移动。游戏将包括碰撞检测、简单的物理模拟,以及基本的用户输入处理。这个简单的足球游戏展示了Pygame的一些基本功能。虽然这是一个简单的游戏,但它包含了创建游戏循环、用户输入处理、物理模拟、碰撞检测等重要概念。

程序熊的博客 743

逆向分析PerimeterX px3:从混淆JS到生成有效令牌的完整实战

在Web安全与数据采集领域,反自动化防护是保障服务稳定和数据安全的关键技术。其核心原理是通过收集浏览器环境指纹和用户行为特征,动态生成加密令牌来区分真人用户与自动化脚本。这项技术的工程价值在于平衡安全性与用户体验,广泛应用于电商、金融、社交等需要防止恶意爬虫和自动化攻击的场景。本文聚焦于业界广泛部署的PerimeterX px3防护方案,通过JavaScript逆向工程,深入剖析其令牌生成机制。我们将探讨如何运用动态调试、反混淆工具和算法还原等关键技术,突破px3的防护壁垒,为自动化测试和数据采集提供稳定可

diankui6178的博客 538

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

(先进后出)

xys_678的博客 254

6层高速PCB设计实战:BGA布局与阻抗匹配关键解析

本文深入解析6层高速PCB设计中BGA布局与阻抗匹配的关键技术。通过实战经验分享,详细介绍了六层PCB层叠结构的选择与优化、BGA器件布局技巧、阻抗匹配的实战经验以及高速布线技巧,帮助工程师提升PCB设计水平,确保信号完整性和电源完整性。

weixin_28481133的博客 369

C++移动构造函数详解

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

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

Jetson-nano 编译实时内核并烧录

前言 之前一篇文章介绍了正常的烧录过程,使用的是默认内核,实际工作中需要对内核打补丁升级为实时内核,并对其重编译。 一、准备内容 官网大法好 官方内核下载链接 官方交叉编译器下载链接 官方文档 ubuntu主机一台 根据官网选择下载对应的文件, 1、L4TDriver Package (BSP) source jetson内核文件。 2、官方交叉编译器 因为我们不是在jetson上直接编译,而jetson是arm平台,所以要进行交叉编译。 二、使用步骤 1.解压 内核 tar -xjf public_s

ldl371122的博客 3288

在ROS中使用行为树

在ROS中使用行为树 我提供的工程示例文件: demo链接 文章目录在ROS中使用行为树0、 学习本教程的前提1、 behaviorTree 库安装(ROS使用下的安装方式)2、 处理 behaviortree 库并准备好 service 和 ros action2.1 对ROS 的 service 和 action 进行封装,使其在 behaviortree 上使用更加方便。2.2 创建一个 service 和一个 action2.3 创建与决策树相关的数据类型2.4 修改`package.xml`文件2

qq_37416258的博客 6379

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

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

Maxwell_Newton的博客 349

Matlab 普通kriging(克里金)插值

Matlab 普通kriging(克里金)插值

dayuhaitang1的博客 4571

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

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

fpcc的专栏 88

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

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

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

2602_95684935的博客 495

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

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

C++ 函数模版

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

2501_94041724的博客 307

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

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

头歌 数据结构——C&C++线性表实训

本系列任务围绕线性表的链式实现,依次完成顺序构建、逆序构建、排序插入、查找、删除、求长度及栈、队列、集合等应用。通过动态内存分配与指针操作,掌握单链表的基本操作与实际应用,涵盖插入、删除、遍历、搜索等核心功能,最终实现数据结构在典型场景中的灵活运用。

一名敲代码的苦13程序员 244
上一篇: [翻译] Effective C++, 3rd Edition, Item 20: 用 pass-by-reference-to-const(传 const 引用)取代 pass-by-value(传值)(下)
下一篇: [翻译] Effective C++, 3rd Edition, Item 21: 当你必须返回一个 object(对象)时不要试图返回一个 reference(引用)(下)
fatalerror99
博客等级 码龄24年 166粉丝 20原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值