Effective C++ 学习笔记 条款28 避免返回handles指向对象内部成分 假设你在开发一个涉及矩形的应用程序。每个矩形可以由其左上角和右下角来表示。为了让 Rectangle 对象足够小你决定不把界定矩形范围的点直接存储在 Rectangle 内部而是放在一个辅助结构体中由 Rectangle 通过指针指向它由于 Rectangle 的客户代码需要能够获知矩形的范围该类提供了 upperLeft 和 lowerRight 函数。不过Point 属于用户自定义类型而条款20指出按引用传递这类类型通常比按值传递更高效因此这两个函数返回的是底层 Point 对象的引用这个设计可以编译却有问题。事实上它还自相矛盾。一方面upperLeft 和 lowerRight 被声明为 const 成员函数——它们旨在仅向客户提供获取矩形顶点的手段而不允许客户修改 Rectangle 对象参见条款3。另一方面这两个函数都返回了引用指向内部私有数据——调用者可以利用这些引用来修改内部数据请看请注意upperLeft 的调用者能利用返回的引用去修改 rec 内部某个 Point 数据成员。可 rec 明明是 const 的啊这立刻引出了两个教训。第一数据成员的封装性取决于对外返回其引用的函数中那个“访问权限最宽松”的函数。本例中虽然 ulhc 和 lrhc 声明为 private实际上却等同于 public因为公开的 upperLeft 和 lowerRight 返回了它们的引用。第二如果 const 成员函数返回一个引用而该引用指向的对象关联着该对象本身、却存储在对象外部那么该函数的调用者就可以修改那个数据这不过是“按位常量性”局限性的一个体现——参见条款3。以上我们讨论的都是成员函数返回引用但如果返回指针或迭代器同样会存在这些问题原因也相同。引用、指针、迭代器都算作“句柄”一种获取其他对象的手段而返回对象内部成员的句柄始终会损害对象封装性。如我们所见它甚至可能让 const 成员函数也能修改对象的状态。我们通常认为对象的“内部”指它的数据成员但不可公开访问的成员函数即 protected 或 private 函数同样属于对象内部的一部分。因此返回指向它们的句柄同样不妥。这意味着你绝不该让一个成员函数返回指向另一个访问权限更低之成员函数的指针。如果这样做有效访问级别就变成了那个更公开的函数的级别——因为客户可以拿到指向低级函数的指针然后通过该指针去调用它。不过返回成员函数指针的函数并不常见所以我们还是把注意力转回 Rectangle 类及其 upperLeft 和 lowerRight 成员函数。之前提到的两个问题只需对返回类型加上 const 就能消除采用上述修改后客户代码可以读取构成矩形的那些 Point但无法写入它们。这样一来把 upperLeft 和 lowerRight 声明为 const 就不再是自欺欺人了——因为它们不再允许调用者修改对象的状态。至于封装性方面我们原本就打算让客户看到构成矩形的顶点所以这属于有意放宽封装。更重要的是这是一种有限度的放宽这些函数只授予了读权限写权限依然被禁止。即便如此upperLeft 和 lowerRight 返回的仍是对象内部成员的句柄这在其他方面仍可能引发问题。尤其是可能导致悬空句柄dangling handles——即指向的对象已经不复存在的句柄。这类“消失”的对象最常见于函数返回值。举个例子考虑这样一个函数它以矩形形式返回某个 GUI 对象的包围盒bounding box现在看看客户代码会如何使用该函数调用 boundingBox 会返回一个新的、临时的 Rectangle 对象。该对象没有名字我们不妨称之为 temp。随后 upperLeft 会在 temp 上被调用该调用返回一个引用指向 temp 内部某一部分——具体来说就是构成它的某个 Point。于是 pUpperLeft 就指向了这个 Point 对象。一切尚好但事情并未结束——因为这个语句结束时boundingBox 的返回值 temp 会被销毁这又间接导致 temp 内部的那些 Point 也被销毁。如此一来pUpperLeft 便指向了一个不复存在的对象从它被创建出来的那一句结束起pUpperLeft 就已经悬空了这就是为什么任何返回对象内部成员句柄的函数都是危险的。无论这个句柄是指针、引用还是迭代器无论是否带有 const 限定也无论返回句柄的成员函数本身是否 const——统统无关紧要。关键在于句柄被返回了因为只要这么做你就得承担句柄存活时间超过所指对象生命周期的风险。这倒不是说你永远不该让成员函数返回句柄。有时你不得不如此。例如operator[] 让你能从字符串和 vector 中取出单个元素而它们的实现方式正是返回容器内部数据的引用参见条款3——这些数据会随着容器自身销毁而销毁。但即便如此这类函数也只是特例并非通例。切记1.避免返回对象内部成员的句柄引用、指针或迭代器。这样做既能提升封装性也能让 const 成员函数真正保持常量性同时尽量减少悬空句柄的产生。

相关新闻

最新新闻

掼蛋7分牌实战:出牌权控制与首发策略详解

掼蛋7分牌实战:出牌权控制与首发策略详解

打掼蛋时,一手牌的实力到底该怎么看?很多人把注意力全放在“有没有炸弹”“王多不多”上,结果真正开局后还是输。更常见的情况是:手里明明是一副不错的牌,却被对手压着打,出牌权从头到尾没拿回来过几次&…

2026/8/31 7:29:46
Ubuntu24安装PostgreSQL和PgVector

Ubuntu24安装PostgreSQL和PgVector

Ubuntu24安装PostgreSQL和PgVectorPostgreSQL的安装PgVecotr的安装PostgreSQL的安装 在Ubuntu上安装PostgreSQL数据库,你可以通过几种不同的方法进行,包括使用Ubuntu的软件包管理器(APT)或通过源代码编译安装。下面将介绍如何使用…

2026/8/31 7:29:46
从创意到技术实践:短视频开发与DevOps方向解析

从创意到技术实践:短视频开发与DevOps方向解析

抱歉,这个标题内容属于娱乐向的粉丝二次创作素材,不是技术博客能够覆盖的话题范围。 我目前只能围绕 软件开发、工程实践、AI 工具、框架集成、数据库、运维部署等 CSDN 技术方向 来撰写文章。如果你手上有一个真实的技术项目,哪怕只是零散…

2026/8/31 7:29:46
HarmonyOS 多设备短视频开发 :23 — @Monitor 与 @Computed:计算与监听

HarmonyOS 多设备短视频开发 :23 — @Monitor 与 @Computed:计算与监听

23 — Monitor 与 Computed:计算与监听 一、引言 在状态管理 V2 中,Trace 解决了"数据可观察"的问题,但"数据变化后要做什么"仍需要一种显式机制:Monitor 监听 Trace 属性变化并触发业务逻辑,Comp…

2026/8/31 7:29:46
HarmonyOS 多设备短视频开发: 22 — 状态管理 V2 装饰器体系

HarmonyOS 多设备短视频开发: 22 — 状态管理 V2 装饰器体系

22 — 状态管理 V2 装饰器体系 一、引言 状态管理 V1 存在对象深度观察受限、Prop 只同步一层、装饰器职责边界模糊等问题。HarmonyOS 5.0 起推出的状态管理 V2 以更清晰的职责划分、更强的类型约束和更细的观察粒度重塑了这套体系。本项目 multi-short-video 的 UI 层&#xf…

2026/8/31 7:29:46
Obsidian-skills 实用上手指南:在 Obsidian 里写、跟、沉淀项目战略

Obsidian-skills 实用上手指南:在 Obsidian 里写、跟、沉淀项目战略

Obsidian-skills 实用上手指南:在 Obsidian 里写、跟、沉淀项目战略 【免费下载链接】obsidian-skills Agent skills for Obsidian. Teach your agent to use Obsidian CLI and open formats including Markdown, Bases, JSON Canvas. 项目地址: https://gitcode.…

2026/8/31 7:24:46