当前位置: 首页 > news >正文

Item22--将成员变量声明为 private

1. 语法一致性 (Syntactic Consistency)

这是最浅层的理由,但对使用者体验很重要。

  • 如果变量是 public 的:客户端访问时不需要括号,如 obj.length
  • 如果变量是 private 的(通过函数访问):客户端访问需要括号,如 obj.length()

如果你的类混杂了 public 变量和 private 变量,调用者在使用时就必须时刻记忆:“length 是变量还是函数?我需不需要加 () ?”

将所有成员变量设为 private,强制通过函数(Getters/Setters)访问,可以让调用者形成统一的认知:访问类的任何属性,都要用函数调用


2. 精细的访问控制 (Precise Control)

这是比语法更实际的好处。如果你将变量设为 public,实际上你就只有一种访问权限:任何人都可以读,任何人都可以写

通过将变量隐藏在 private 背后,并提供访问函数,你可以实现精细的控制:

class AccessControl {
public:// 1. 只读访问 (Read-only)int getID() const { return id_; }// 2. 只写访问 (Write-only) - 较少见,但有时有用void setInternalState(int s) { state_ = s; }// 3. 读写访问 (Read-Write)string getName() const { return name_; }void setName(const string& n) { if (n.empty()) return; // 可以加入验证逻辑!name_ = n; }private:int id_;int state_;string name_;
};

关键点:使用 Setter 函数不仅仅是为了赋值,你还可以在这里加入验证逻辑(Validation)线程同步锁、或者触发其他事件(如通知 UI 更新)。如果直接暴露变量,这些都做不到。


3. 封装性 (Encapsulation) —— 核心理由!

这是 Item 22 的灵魂所在。

什么是封装? 封装不仅仅是把数据藏起来,而是将“具体的实现细节”与“对外提供的接口”分离开来

场景假设:网速监测器

假设你正在写一个监测网速的类。

方案 A:Public 成员变量(无封装)

class SpeedMonitor {
public:vector<double> speedSamples;double averageSpeed; // 这是一个成员变量
};

调用者:

cout << monitor.averageSpeed;

方案 B:Private 成员 + 函数(有封装)

class SpeedMonitor {
public:double averageSpeed() const { return averageSpeed_; }
private:vector<double> speedSamples;double averageSpeed_;
};

此时,需求变了

假设你的应用内存非常紧张,不能为了存一个 averageSpeed 专门浪费 8 个字节。或者你的 PM 要求 averageSpeed 必须是实时计算的,不能存缓存值。

  • 对于方案 A (Public):你完蛋了。你不能把变量 double averageSpeed 删掉换成一个函数,因为所有写了 monitor.averageSpeed(没括号)的代码都会编译失败。你必须修改所有调用者的代码,重新编译整个项目。
  • 对于方案 B (Private):你很轻松。你只需要修改类内部的实现:
class SpeedMonitor {
public:// 接口没变,调用者完全不知情double averageSpeed() const { if (speedSamples.empty()) return 0;// 实时计算,不再返回缓存变量return std::accumulate(speedSamples.begin(), speedSamples.end(), 0.0) / speedSamples.size();}
private:vector<double> speedSamples;// double averageSpeed_; // 删掉这个变量,没人受伤
};

结论:只有将数据封装起来,你保留了日后改变主意的权利(改为计算式、改为查数据库、优化内存等)。如果暴露数据,你就失去了这种弹性。


4. 这里的陷阱:Protected 成员

很多 C++ 程序员认为访问权限是这样的:

  • Public: 谁都能用(完全没封装)。
  • Private: 只有我能用(完美封装)。
  • Protected: 只有我和我的子类能用(这也算封装吧?)。

Item 22 提出了一个震撼的观点:Protected 成员变量的封装性几乎等于零(和 Public 一样差)。

为什么?

衡量封装性的指标是:当这个成员变量改变(被删除或修改类型)时,有多少代码会被破坏?

  • Public 变量:所有使用了该类的客户代码(Client Code)都会坏掉。数量 = 无限大
  • Protected 变量:所有继承了该类的派生类代码(Derived Classes)都会坏掉。在大型项目中,派生类的数量往往也是巨大的。数量 = 无限大

因此,protected 变量和 public 变量一样,都缺乏封装性

结论:成员变量的访问级别只有两种选择:private(封装了)和其他(没封装)。


5. 现代 C++ 视角的补充

Item 22 在现代 C++ 中依然是铁律,但有两个常见的“例外”或相关概念需要注意:

A. struct 和 POD (Plain Old Data)

如果你定义的是一个纯粹的数据包,没有行为,不需要维持不变量(Invariants),例如用于 C API 交互的结构体:

struct Point {double x;double y;
};

在现代 C++ 指导原则(如 C++ Core Guidelines)中,这种只有数据没有逻辑的 struct 允许 public 成员。但只要类中有逻辑(Logic)或不变量(比如 denominator 不能为 0),就必须用 class 且数据必须 private

B. std::pairstd::tuple

STL 中的 std::pair 拥有 public 的 firstsecond。这是因为它的设计目的就是通用的数据容器,没有任何特定的业务逻辑需要封装。


总结 (Summary)

  1. 切记:将成员变量声明为 private
  2. 优势
    • 一致性:客户只通过函数访问。
    • 控制力:可实现只读、只写、验证逻辑。
    • 封装性最重要的一点。允许你将来改变实现细节(如从变量存储改为计算得出),而无需重新编译客户代码。
  3. 警惕:不要用 protected 成员变量,它的封装性约等于 public
http://www.gsyq.cn/news/127913.html

相关文章:

  • basic_regex
  • Item19--设计 class 犹如设计 type
  • item14--谨慎考虑资源管理类的拷贝行为
  • Miloco 深度打通 Home Assistant,实现设备级精准控制
  • item11--在 operator= 中处理“自我赋值
  • 【零基础精通】Python 字符串全解析:从字符序列到不可变对象的深度构建
  • 日记12,19
  • 圆形石子合并问题
  • set_value
  • function的类型擦除
  • Item10--令赋值操作符返回一个
  • 日记12.18
  • python django flask考研互助交流平台_c62p51fu--论文
  • Ubuntu上使用VScode创建Maven项目
  • 方达炬〖发明超新技术〗:冰堆技术;冷极冰堆建筑技术;
  • 线程(2)
  • Item9--绝不在构造和析构过程中调用虚函数
  • Item5--了解 C++ 默默编写并调用了哪些函数
  • 日记1217
  • 本地私有知识库新选择:访答软件真实体验分享
  • 日记12,15
  • string_view
  • 比话降AI靠谱吗?比话能降知网AI率吗? - 还在做实验的师兄
  • 好用做老房换新实用门窗品牌精选指南的机构
  • 快去尝试单尺作图内接正257边形吧
  • nginx安装步骤详解 - 教程
  • C020基于博途西门子1200PLC鸡饲料生产线控制系统仿真
  • 第9章 顺序容器
  • 基于SpringBoot+Vue的乡镇农村建设用地管理系统的设计与实现
  • Git 与 SVN 区别 - 详解