public 和 private 是接口与实现的分界线,而非访问权限开关;类内部总能直接访问所有成员,private 仅限制类外通过对象名或指针的形式访问。

public 和 private 不是“访问权限开关”,而是接口与实现的分界线
它们不决定“能不能访问”,而决定“谁有资格去访问”——类内部代码总能直接读写所有成员,private 拦不住 this->x,只拦住类外通过对象名或指针调用的形式(比如 obj.x 或 p->x)。
常见错误现象:private 成员在友元函数里能直接访问,但有人误以为“加了 private 就绝对安全”,结果在序列化、调试打印时硬写 obj._data,编译失败才意识到它根本不可见。
-
public成员暴露给所有使用者,适合构造函数、常用操作接口(如size()、push_back()) -
private成员只对本类和友元开放,适合缓存、状态标记、临时计算字段(如_cached_hash、_is_valid) - 不要为了“看起来封装了”而把所有字段塞进
private却配一堆get_*/set_*——这等于没封,还增加了调用开销和维护负担
封装不是藏数据,是控制修改入口
真正需要封装的,往往不是“值本身”,而是“值怎么变”。比如一个 std::vector 的 capacity 是 private 的,但你不能直接改它;你要调用 reserve() 或 shrink_to_fit(),这些函数内部会校验、重分配、更新计数——这才是封装的价值。
使用场景:当某个字段变更会引发副作用(比如触发重绘、通知监听器、破坏不变量),就必须收口到函数里,而不是放开 public 成员变量。
立即学习“C++免费学习笔记(深入)”;
- 把
int _count设为private,提供increment()而非set_count(int),避免非法负值 - 如果字段只是配置项且无约束(如
timeout_ms),可考虑public+ 文档说明,比强制走 setter 更轻量 - C++20 起,
private成员仍可能被反射或序列化库绕过(如boost::pfr),别依赖它做安全隔离
继承时 public/private 对派生类的影响很具体
很多人以为 private 继承就是“完全隐藏基类”,其实它只影响访问级别:基类的 public 和 protected 成员在派生类中变成 private,但依然存在、可调用(只要不是 private 的基类成员)。
错误现象:写 class D : private B 后,在 D 里调用 B::func() 报错,其实是 B::func() 本身是 private,跟继承方式无关;或者误以为 private 继承后基类对象无法转型,其实可以,只是不能隐式转(需显式 static_cast)。
- 想复用实现但不暴露基类接口 → 用
private继承或组合(优先组合) - 想表达 “是一个” 关系且公开接口 → 用
public继承,并确保基类有虚析构 -
protected继承极少用,容易混淆;多数时候不如把想共享的成员设为protected,然后public继承
const 成员函数和 mutable 是打破封装的合法出口
mutable 允许在 const 成员函数里修改某字段,典型用于缓存、引用计数、日志标记——它不破坏逻辑 const 性,但绕过了 private 的“只读”表象。
容易踩的坑:把 mutable std::mutex 放进类里,以为加锁就线程安全了,结果忘了 const 函数也能被多线程并发调用,而 mutable 不自动加锁;或者滥用 mutable 存储业务状态,让 const 接口悄悄改变对象语义。
- 缓存计算结果(如
mutable double _cached_result)是mutable的正当用途 - 不要用
mutable去规避设计问题,比如该用非 const 接口却硬写成 const + mutable -
const成员函数里调non-const成员函数会编译失败,除非用const_cast(不推荐)或把目标函数也标为const
封装最难的部分,从来不是语法上加个 private,而是判断哪些变化必须受控、哪些可以放开、以及当别人绕过你的接口时,系统是否还能稳住。C++ 不阻止你写 friend class TestHelper,但得想清楚:这个 TestHelper 真的只读不写?它的存在会不会让后续重构不敢动那个 private 字段?









