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 | 仅类内和友元可见 | 强健(接口隔离) | 高(内部可自由修改) |
封装性带来的核心优势:
-
访问精确控制
- 只读访问:仅提供const getter
const std::string& getName() const { return name_; }
- 条件写访问:带校验的setter
void setAge(int age) {if (age < 0) throw InvalidAge();age_ = age; }
-
实现弹性
class DataMonitor { public:// 接口保持不变int getValue() const { // 可无缝切换实现return useCache_ ? cachedValue_ : readHardware();} private:bool useCache_ = false;int cachedValue_;int readHardware() const; };
-
不变式维护
class Account { public:void deposit(double amount) {if (amount <= 0) throw InvalidAmount();balance_ += amount;logTransaction(); // 自动记录} private:double balance_ = 0;void logTransaction(); };
💡 关键原则总结
- 绝对封装原则
- 所有数据成员必须private
- 禁用public/protected数据成员
- 访问接口最小化
- 提供必要getter/setter
- 避免暴露实现细节
- 一致性维护点
- 在setter中执行校验
- 在getter中执行计算/转换
- 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"); // 编译错误!类型安全