Effective C++ 条款22: 将成员变量声明为private

Effective C++ 条款22:将成员变量声明为private


核心思想始终将成员变量声明为private,通过函数接口控制访问,提供封装弹性、精确访问控制和一致性维护点。

⚠️ 1. 公开成员的致命缺陷

数据一致性破坏

class AccessPoint {
public:int x;  // 公开成员int y;double signalStrength;
};AccessPoint ap;
ap.x = 100;   // 直接修改
ap.y = 200;   // 无任何校验
// 坐标改变但signalStrength未更新 → 数据不一致

接口僵化问题

class Thermostat {
public:double currentTemp;  // 公开成员double targetTemp;
};// 客户端代码直接访问成员
if (thermostat.currentTemp < thermostat.targetTemp) {startHeating();
}// 需求变更:温度单位转换 → 必须修改所有客户端代码

🚨 2. 解决方案:严格private封装

受控访问接口

class SecureThermostat {
public:// 设置温度带校验void setTargetTemp(double temp) {if (temp < MIN_TEMP || temp > MAX_TEMP) throw InvalidTemp();targetTemp_ = temp;updateSystemState();}// 获取温度带单位转换double getCurrentTempCelsius() const { return currentTemp_; }double getCurrentTempFahrenheit() const {return (currentTemp_ * 9/5) + 32;}private:double currentTemp_;   // 私有成员double targetTemp_;void updateSystemState() { /* 状态同步逻辑 */ }
};

⚖️ 3. 封装优势与访问控制
封装级别访问控制可维护性弹性
public完全开放脆弱(直接耦合)零(修改破坏客户端)
protected派生类可见中度脆弱(影响派生类)低(修改影响继承体系)
private仅类内和友元可见强健(接口隔离)高(内部可自由修改)

封装性带来的核心优势

  1. 访问精确控制

    • 只读访问:仅提供const getter
    const std::string& getName() const { return name_; }
    
    • 条件写访问:带校验的setter
    void setAge(int age) {if (age < 0) throw InvalidAge();age_ = age;
    }
    
  2. 实现弹性

    class DataMonitor {
    public:// 接口保持不变int getValue() const { // 可无缝切换实现return useCache_ ? cachedValue_ : readHardware();}
    private:bool useCache_ = false;int cachedValue_;int readHardware() const;
    };
    
  3. 不变式维护

    class Account {
    public:void deposit(double amount) {if (amount <= 0) throw InvalidAmount();balance_ += amount;logTransaction();  // 自动记录}
    private:double balance_ = 0;void logTransaction();
    };
    

💡 关键原则总结

  1. 绝对封装原则
    • 所有数据成员必须private
    • 禁用public/protected数据成员
  2. 访问接口最小化
    • 提供必要getter/setter
    • 避免暴露实现细节
  3. 一致性维护点
    • 在setter中执行校验
    • 在getter中执行计算/转换
  4. protected的伪封装性
    • protected成员 ≈ public(对派生类)
    • 修改protected成员影响所有派生类

危险设计重现

class Config {
public:// 公共数据成员std::string serverIP;int port;bool useEncryption;
};// 客户端代码
Config cfg;
cfg.serverIP = "192.168.1.1";
cfg.port = 8080;
cfg.useEncryption = "true";  // 错误!类型不匹配但编译通过

安全重构方案

class SecureConfig {
public:// 安全设置接口void setServerIP(const std::string& ip) {if (!isValidIP(ip)) throw InvalidIP();serverIP_ = ip;}void setPort(int port) {if (port < 1 || port > 65535) throw InvalidPort();port_ = port;}void setEncryption(bool enable) {useEncryption_ = enable;updateSecurityContext();}// 只读访问const std::string& getServerIP() const { return serverIP_; }int getPort() const noexcept { return port_; }bool encryptionEnabled() const { return useEncryption_; }private:std::string serverIP_;int port_ = 80;  // 默认值bool useEncryption_ = false;
};// 使用示例
SecureConfig cfg;
cfg.setPort(8080);  // 安全设置
// cfg.setEncryption("true");  // 编译错误!类型安全

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/2716.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java基础-斗地主游戏

目录 案例要求&#xff1a;​ 实现思路&#xff1a; 代码&#xff1a; Main启动类&#xff1a; Card实体类&#xff1a; Room操作类&#xff1a; 总结&#xff1a; 案例要求&#xff1a; 实现思路&#xff1a; 1构造卡牌,细节&#xff1a;实体类另设一个int类型变量表示…

基于Java的AI/机器学习库(Smile、Weka、DeepLearning4J)的实用

基于Java和AI技术处理动漫视频 以下是一些基于Java和AI技术处理动漫视频(如《亚久斗》)的实用案例和实现方法,涵盖视频分析、风格转换、角色识别等方向。每个案例均提供技术思路和关键代码片段。 视频关键帧提取 使用OpenCV提取动漫视频中的关键帧,保存为图片供后续分析…

Qt 自动无法加载数据库为空

解决方式:main() 中设置QDir::setCurrent(QCoreApplication::applicationDirPath());即可 1、开机自启 void setAutoStart(bool enable) {QSettings settings("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run", QSettings::NativeFormat);QS…

vue3指定设置了dom元素的ref但是为null问题

目录 问题场景 ​编辑问题原因 解决方案 问题场景 可以看到我指定了元素的ref&#xff0c;正常来说在组件挂载完毕后可以通过ref.value正常获取到dom元素 但是实际打印出来为null 问题原因 根本原因就是v-if指令的问题&#xff0c;v-if指令能够控制元素是否渲染&#xff0…

控制建模matlab练习08:根轨迹

此练习主要是&#xff1a;在matlab中绘制根轨迹的方法。 一、在matlab中建立对应系统 1、例如&#xff0c;对于如图的反馈系统。 2、其中开环传递函数G(s)、闭环传递函数Gcl(s)。3、因此&#xff0c;其闭环传递函数的根轨迹&#xff0c;就可以直接在matlab中绘制出来。 4、直接…

深度学习中的三种Embedding技术详解

提纲背景介绍特征类型与Embedding方法1. ID类特征的Embedding处理1.1 标准Embedding方法1.2 IdHashEmbedding方法2. 数值型特征的Embedding处理2.1 RawEmbedding方法三种Embedding方法对比总结实践建议总结背景介绍 在深度学习领域&#xff0c;Embedding&#xff08;嵌入&…

前端开发(HTML,CSS,VUE,JS)从入门到精通!第四天(DOM编程和AJAX异步交互)

八、DOM 编程1&#xff0e;DOM&#xff08;Document Object Model&#xff09;,文档对象模型&#xff1a;将 HTM L文档进行模型化处理&#xff0c;形成一颗结构化的文档树&#xff0c;从而提供访问&#xff0c;修改文档的统一编程接口&#xff08;API&#xff09;&#xff0c;一…

Spring Boot 的事务注解 @Transactional 失效的几种情况

开发中我们经常会用到 Spring Boot 的事务注解&#xff0c;为含有多种操作的方法添加事务&#xff0c;做到如果某一个环节出错&#xff0c;全部回滚的效果。但是在开发中可能会因为不了解事务机制&#xff0c;而导致我们的方法使用了 Transactional 注解但是没有生效的情况&…

RabbitMQ面试精讲 Day 8:死信队列与延迟队列实现

【RabbitMQ面试精讲 Day 8】死信队列与延迟队列实现 文章标签 RabbitMQ,消息队列,死信队列,延迟队列,面试技巧,分布式系统 文章简述 本文是"RabbitMQ面试精讲"系列第8天&#xff0c;深入讲解死信队列与延迟队列的实现原理与实战应用。文章详细解析死信队列的触发…

快速掌握Python编程基础

干货分享&#xff0c;感谢您的阅读&#xff01;备注&#xff1a;本博客将自己初步学习Python的总结进行分享&#xff0c;希望大家通过本博客可以在短时间内快速掌握Python的基本程序编码能力&#xff0c;如有错误请留言指正&#xff0c;谢谢&#xff01;&#xff08;持续更新&a…

Redis数据库存储键值对的底层原理

前言Redis可以简单理解为是一个存储键值对的内存结构下面我们来看一下Redis使用什么数据结构来存储键值对的叭Redis键值对的存储原理Redis存储键值对的数据结构是哈希表存储键值对的运行机制因为Redis的数据存储类型是多种多样的,所以管理键值对的哈希表只是存储这个数据的地址…

全球化 2.0 | 中国香港教育机构通过云轴科技ZStack实现VMware替代

中国香港教育机构是非营利性组织。随着智慧教育升级与业务量激增&#xff0c;客户面临VMware持续的授权和维护成本带来总体拥有成本压力&#xff1b;部分业务仍运行在性能与扩展性不足的老旧物理服务器和 VMware 架构上&#xff0c;存在单点故障风险&#xff1b;跨校区物理机与…