[翻译] Effective C++, 3rd Edition, Item 22: 将 data members(数据成员)声明为 private

地图之家总目录 地图之家总目录 cesium示例 leaflet示例 阅读详情

Item 22: 将 data members(数据成员)声明为 private

作者:Scott Meyers

译者:fatalerror99 (iTePub's Nirvana)

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

好了,先公布一下计划。首先,我们将看看 data members(数据成员)为什么不应该是 public(公有)的。然后,我们将看到所有反对 public data members(公有数据成员)的理由同样适用于 protected(被保护)的。这就导出了 data members(数据成员)应该是 private(私有)的结论,至此,我们就结束了。

那么,public data members(公有数据成员),为什么不呢?

我们从先从语法一致性开始(参见 Item 18)。如果 data members(数据成员)不是 public(公有的),客户访问一个 object 的唯一方法就是通过 member functions(成员函数)。如果在 public interface(公有接口)中的每件东西都是一个函数,客户就不必挠着脑袋试图想起当他们要访问一个 class 的成员时是否需要使用圆括号。他们只要使用就可以了,因为每件东西都是一个函数。一生坚持这一方针,能节省很多挠头的时间。

但是也许你不认为一致性的理由是强制性的。使用函数可以让你更加精确地控制 data members(数据成员)的可存取性的事实又怎么样呢?如果你让一个 data member(数据成员)为 public(公有)的,每一个人都可以 read-write access(读写访问)它,但是如果你使用函数去得到和设置它的值,你就能实现 no access(禁止访问),read-only access(只读访问)和 read-write access(读写访问)。更进一步,如果你需要,你甚至可以实现 write-only access(只写访问):

class AccessLevels {
public:
  ...
  int getReadOnly() const        { return readOnly; }

  void setReadWrite(int value)   { readWrite = value; }
  int getReadWrite() const       { return readWrite; }

  void setWriteOnly(int value)   { writeOnly = value; }

private:
  int noAccess;                         // no access to this int

  int readOnly;                         // read-only access to this int

  int readWrite;                        // read-write access to this int

  int writeOnly;                        // write-only access to this int
};

这种条分缕析的 access control(访问控制)很重要,因为很多 data members(数据成员)should(应该)被隐藏。每一个 data member(数据成员)都需要一个 getter 和 setter 的情况是很罕见的。

还不相信吗?那么该拿出一门重炮了:encapsulation(封装)。如果你通过一个函数实现对一个 data member(数据成员)的访问,你可以在以后用一个计算来替换这个 data member(数据成员),使用你的 class 的人不会有任何察觉。

例如,假设你为一个自动设备写一个应用程序用于监视通过的汽车的速度。每通过一辆汽车,它的速度就被算出,并把那个值加入到迄今为止收集到的所有速度数据的集合中:

class SpeedDataCollection {

  ...
public:
  void addValue(int speed);          // add a new data value

  double averageSoFar() const;       // return average speed

  ...
};

现在考虑 member function(成员函数)averageSoFar 的实现:实现它的办法之一是在 class 中用一个 data member(数据成员)来实时变化迄今为止收集到的所有速度数据的平均值。无论何时 averageSoFar 被调用,它只是返回那个 data member(数据成员)的值。另一个不同的方法是在每次调用 averageSoFar 时重新计算它的值,通过检验集合中每一个数据值它能做成这些事情。

第一种方法(保持一个实时变化的平均值)使每一个 SpeedDataCollection object 都比较大,因为你必须为持有实时变化的平均值,累计的和以及数据点的数量的 data members(数据成员)分配空间。然而,averageSoFar 可以现得非常高效,它仅仅是一个返回实时变化的平均值的 inline 函数(参见 Item 30)。反过来,无论何时被请求都要计算平均值使得 averageSoFar 的运行比较慢,但是每一个 SpeedDataCollection object 都比较小。

谁能说哪一个最好?在内存非常紧张的机器(例如,一个嵌入式路边设备)上,以及在一个很少需要平均值的应用程序中,每次都计算平均值可能是较好的解决方案。在一个频繁需要平均值的应用程序中,速度是基本的要求,而且内存不成问题,保持一个实时变化的平均值更为可取。这里的重点在于通过一个 member function(成员函数)访问平均值(也就是说,通过将它封装),你能互换这两个不同的实现(也包括其他你可能想到的),对于客户,最多也就是必须重新编译。(你可以用在后面的 Item 31 中记述的技术来消除这个麻烦。)

将 data members(数据成员)隐藏在 functional interfaces(函数化接口)之后能为各种实现提供机动性。例如,它可以在 data members(数据成员)被读或者写的时候很简单地通报其它 objects,可以验证 class invariants(类的不变量)以及函数的前置和后置条件,可以在多线程环境中执行同步任务,等等。从类似 Delphi 和 C# 的语言来到 C++ 的程序员会认同这种类似那些语言中的 "properties"(“属性”)的等价物的功能,虽然需要附加一对额外的圆括号。

关于 encapsulation(封装)这一点来说可能比它最初显现出来的更加重要。如果你对你的客户隐藏你的 data members(数据成员)(也就是说,封装它们),你就能确保 class invariants(类的不变量)总能被维持,因为只有 member functions(成员函数)能影响它们。此外,你预留了以后改变你的实现决策的权力。如果你不隐藏这样的决策,你将很快发现,即使你拥有一个 class 的源代码,你改变任何一个 public 的东西的能力也是极端有限的,因为有太多的客户代码将被破坏。public 意味着未被封装,而实际上可以说,未被封装意味着不可改变,尤其是被广泛使用的 classes。但是仍然被广泛使用的 classes 大多数都是使用了封装的,因为它们可以最大限度地受益于用一种更好的实现替换现有实现的能力。

反对 protected data members(被保护数据成员)的理由是类似的。实际上,它是一样的,虽然起先看起来似乎不那么像。很显然,关于语法一致性和条分缕析的访问控制的论证就像用于 public 一样可以应用于 protected data,但是关于 encapsulation(封装)又如何呢?难道 protected data members(被保护数据成员)不比 public(公有)的更具有封装性吗?实话实说,令人惊讶的答案是它们不。

Item 23 阐释了如果某物发生了变化,某物的 encapsulation(封装)与可能被破坏的代码数量成反比。于是,如果一个 data member(数据成员)发生了变化,例如,如果它被从 class 中移除(可能是为了支持计算,就像在上面的 averageSoFar 中),这个 data member(数据成员)的 encapsulatedness(封装性)与可能被破坏的代码数量成反比。

假设我们有一个 public data member(公有数据成员),随后我们排除了它。有多少代码会被破坏呢?所有使用了它的客户代码,其数量通常 unknowably large(大得难以置信)。因此 public data members(公有数据成员)就是完全未被封装的。但是,假设我们有一个 protected data member(被保护数据成员),随后我们排除了它。现在有多少代码会被破坏呢?所有使用了它的 derived classes(派生类),典型情况下,代码的数量还是 unknowably large(大得难以置信)。因此 protected data member(被保护数据成员)就像 public(公有)的一样没有被封装,因为在这两种情况下,如果 data members(数据成员)发生变化,被破坏的客户代码的数量都 unknowably large(大得难以置信)。这是不符合直觉的,但是富有经验的库实现者会告诉你,这是千真万确的。一旦你声明一个 data member(数据成员)为 public 或 protected,而且客户开始使用它,就很难再改变与这个 data member(数据成员)有关的任何事情。有太多的代码不得不被重写,重测试,重文档化,或重编译。从 encapsulation(封装)的观点来看,实际只有两个访问层次:private(提供了 encapsulation(封装))与所有其它(没有提供)。

Things to Remember

  • 声明 data members(数据成员)为 private。它为客户提供了访问数据的语法上的一致,提供条分缕析的访问控制,允许不变量被强制,而且为 class 的作者提供了实现上的机动性。
  • protected 并不比 public 的封装性强。
RT-DETR改进策略【模型轻量化】| 替换骨干网络为 2024轻量化网络MoblieNetV4:移动生态系统的通用模型 本文记录的是基于MobileNet V4的RT-DETR目标检测轻量化改进方法研究。通过整合UIBMobile MQA以及优化的NAS策略能够在在不降低性能指标的前提下,降低计算成本。本文配置了原论文中和五种模型,以满足不同的需求。 阅读详情

相关推荐

保姆级教程:手把手教你为裕泰微YT8521 PHY芯片编写Linux设备树(DTS)节点与驱动

本文提供了一份详细的保姆级教程,手把手教你为裕泰微YT8521 PHY芯片编写Linux设备树(DTS)节点与驱动。从基础配置到RGMII接口的特殊设置,再到驱动开发的关键步骤和调试技巧,帮助开发者快速掌握PHY芯片的移植与开发。

weixin_33734785的博客 334

C++中的private, public, protected

Private, Public, and Protected in C++

scott198512的博客 1万+

加湿器方案-DLT8M04TS-杰力科创

基本组成: 一个触摸按键、一组RGB灯、一个雾化片、一个充电IC。 产品功能: 第一次上电光机状态,RGB红/绿/红灯依次闪烁灭; 2、按键功能:短按开关机和三档喷雾工作模式,第一次短按持续喷雾,短按第二次喷3S,停3S,短按第三次关闭喷雾模式,依次循环; 在喷雾或是不喷雾的状态下长按1.5S RGB灯渐变,一直循环,第二次长按当前灯光闪烁一下常亮,第三次长按关机; 3、持续喷雾定时5小时自动断电,间歇喷雾定时10小时自动断电,若在间歇工作时有切换为持续喷雾,那间歇喷雾的...

JLKCsz的博客 513

深度探索C++对象模型

member function 虽然含在class声明之内,却不出现在object之中。每一个non-inline member funct

469

微带线贴片天线尺寸的计算,利用Matlab计算+常见的RF计算在线公式

微带线贴片天线尺寸的计算,利用Malatb计算 这几天在使用hfss对矩形天线仿真,每次改变频率和实验的材料,都要重新计算一遍,太麻烦了,所以用matlab进行计算。 知道天线的工作频率:f_r 介质板的介电常数:epsilon_r 介质板厚度:h 就可以计算出矩形天线的长:L 矩形天线的宽:W 对于下面公式的单位要注意,公式以及化简了,要注意单位 由于matlab中的变量不能用希腊字母代替,所以用了该字母的读音 代码如下: f_r=2.45; %工作频率,单位GHz epsilon_r=3.5;

weixin_43404609的博客 2万+

【局部规划模块轨迹优化算法】Teb_planner规划算法

认知有限,望大家多多包涵,有什么问题也希望能够与大家多交流,共同成长!本文先对****做个简单的介绍,具体内容后续再更,其他模块可以参考去我其他文章提示:以下是本篇文章正文内容由于ROS中的局部路径规划器比较老了,所以我推荐大家使用比较新的局部路径规划器teb_local_planner。teb_local_planner是基于弹性时间带碰撞约束的算法,算法将动态障碍物、运行时效、路径平滑性等约束做综合考虑,在复杂环境下有更优秀的表现。

qq_35635374的博客 952

《深度探索C++对象模型》读书笔记

https://www.cnblogs.com/wickedpriest/p/6527181.html https://www.cnblogs.com/wickedpriest/p/6580134.html 前言 今年中下旬就要找工作了,我计划从现在就开始准备一些面试中会问到的基础知识,包括C++、操作系统、计算机网络、算法和数据结构等。C++就先从这本《深度探索C++对象模型》开始。不同于...

$firecat利白的代码足迹$ 920

24、企业治理、风险与合规:精益思维的应用与实践

本文探讨了如何运用精益思维优化企业治理、风险与合规(GRC)流程,分析了职责分离与合规流程的误区,提出了预防性与检测性控制措施的合理搭配,并通过价值流映射、减少合规反馈循环等方法实现流程优化。以Etsy的PCI-DSS实施为例,展示了如何在满足法规要求的同时保持创新活力。文章还强调了构建协作型GRC文化的重要性,并展望了未来GRC的发展趋势,包括数字化转型、全球化风险管理及与可持续发展的结合。

solidity8miner的博客 104

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

(先进后出)

xys_678的博客 254

18、社区多能源管理与点对点能源交易优化调度策略研究

本文研究了社区多能源管理与点对点(P2P)能源交易的优化调度策略,构建了适用于单一社区多个产消者的P2P交易模型,以用户生产成本最小化为目标,设计了基于ADMM算法的迭代优化方法。通过电热联合调度与能源共享机制,结合光伏、热电联产(CHP)和储能系统(ESS),实现了用户侧能源的高效利用。仿真结果表明,引入P2P交易和CHP设备显著降低了用户能源成本,提高了电力自给率和天然气利用率,减少了对主电网的依赖。研究验证了ADMM算法在分布式优化中的有效性,为未来社区能源系统的智能化管理提供了理论支持和技术路径。

git9versioner的博客 71

Vue3 - 实现内网使用天地图离线地图,详解项目加载天地图的离线地图服务器部署与使用教程示例源码,vue3如何实现天地图的离线加载部署在内网访问(天地图资源下载/瓦片等流程教学,实现天地图各种功能)

Vue3中加载天地图的离线地图,天地图——内网使用离线地图,vue3+leaflet实现天地图离线访问,vue3项目中利用leaflet使用天地图,vue3项目中使用天地图离线地图,天地图离线瓦片的打包与发布(GeoServer)天地图离线瓦片数据下载及本地化部署,在内网中无法直接使用外网天地图资源怎么办,在vue3中openLayers加载地图 -使用天地图的瓦片数据。VUE3天地图离线怎么使用,内网部署天地图,天地图离线瓦片,部分瓦片数据,天地图如何离线,无需网络调用天地图API,vue天地图离线地图,

🏆 前端领域优质创作者 1万+

C++移动构造函数详解

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

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

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

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

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

Maxwell_Newton的博客 349

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

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

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

fpcc的专栏 88

Statistics and Machine Learning Toolbox

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

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

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

2602_95684935的博客 497

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

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

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

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

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值