protected成员专为继承链内部协作设计,仅在派生类成员函数体内合法访问,类外(含对象点号调用)一律编译失败,是纯编译期访问控制。

protected 成员不是“半公开”,而是专为继承链内部协作设计的访问层级;它在类外(包括派生类对象)完全不可见,只在派生类的成员函数体内合法可用。
为什么 m_protected_data 在派生类里能用,但在 obj.m_protected_data 就编译失败
关键看代码写在哪:只要访问语句出现在派生类自身的成员函数体内(比如 void Derived::calc()),m_protected_data 就是合法的;一旦出现在 main()、全局函数、或通过对象点号调用(哪怕 Derived obj;),编译器立刻拒绝——这不是运行时检查,而是纯编译期的访问控制。
常见错误现象:error C2248: 'Base::m_protected_data': cannot access private member。注意这个提示常把 protected 误标为 private,本质是访问位置非法触发的统一拒访机制。
- 派生类成员函数内:可直接读写
m_protected_data,无需 getter/setter - 派生类构造函数初始化列表中:可以访问基类
protected成员(如: Base(m_val)中若m_val是 protected,需确保它是基类 public/protected 构造参数) - 派生类友元函数中:不能访问基类
protected成员,友元权限不继承 - 类外通过指针/引用调用:哪怕
Base* p = new Derived,p->m_protected_data仍非法
class D : protected B 和 protected int x 是两回事
这是两个独立维度:一个是成员访问修饰符(protected 修饰变量或函数),另一个是继承方式(class D : protected B 中的 protected)。前者决定“谁能在哪访问这个成员”,后者决定“基类成员在派生类中降级后的最高权限”。
立即学习“C++免费学习笔记(深入)”;
例如:
class B {
public: int pub;
protected: int prot;
private: int priv;
};
<p>class D1 : public B { /<em> pub→public, prot→protected, priv→不可访问 </em>/ };
class D2 : protected B { /<em> pub→protected, prot→protected, priv→不可访问 </em>/ };
class D3 : private B { /<em> pub→private, prot→private, priv→不可访问 </em>/ };
-
D2中pub变成protected,意味着D2对象无法隐式转为B*,破坏 is-a 关系 -
D2的子类(如D4 : public D2)将无法访问B::pub,因为它在D2中已降为protected,而D4不是B的直接派生类 - 滥用
protected继承会导致下游接口调用直接编译失败,比如依赖B*参数的函数无法传入D2对象
什么时候该用 protected 而不是 private 或 public
选 protected 的唯一合理动机:你明确预期这个成员会被子类依赖(比如共享缓存、中间计算状态、配置标志),且不希望它暴露在类的公有接口中。它不是为了“留个后门”,而是为继承链提供可控的扩展点。
- 适合
protected:几何类中的m_radius(Circle和Sphere都需复用)、容器类中的m_capacity(子类可能需重写扩容逻辑) - 不该用
protected:内部缓冲区指针m_buffer、状态机当前状态m_state——这些属于实现细节,暴露出去会让所有子类绑定到具体实现,一改全崩 - 反模式示例:把本该
private的数据设为protected,等于向未来所有子类发放“修改许可证”;反过来,把本该protected的中间量设成private,子类只能重复实现或加friend,反而破坏设计清晰度
性能与兼容性影响几乎为零,但设计意图必须清晰
protected 是纯编译期检查,无任何运行时开销。但它真正难的地方不在语法,而在判断:这个成员是否真的需要被子类直接操作?还是应该封装成虚函数接口?
容易被忽略的一点是:一旦声明为 protected,你就承诺了它的存在、类型和语义稳定性。后续修改(比如改成 private 并加虚函数包装)会强制所有子类重写,代价远高于初期多写几个受控的 protected 成员。


















