C++ 继承方式:公有、保护、私有,到底在控制什么

继承方式不决定基类成员能否使用,而是决定其在派生类中的访问级别。公有继承保持接口与类型关系(is-a),支持多态;保护继承隐藏外部接口但允许家族内访问;私有继承实现复用且断绝上下级关系(has-a)。基类private成员始终不可访问。组合优于私有继承,除非需重写虚函数或访问protected成员。显式声明继承方式,避免默认规则陷阱。

一句话定调:继承方式不决定派生类"能不能用"基类成员,而是决定"基类成员在派生类里被降级到什么访问级别"。

基类成员本身有访问级别:public / protected / private

继承方式也有级别:public / protected / private

最终在派生类中呈现的访问性,取两者中更严格的那个:

复制代码
public   >  protected  >  private

一、核心矩阵

复制代码
class Base {
public:
    int pub;
protected:
    int prot;
private:
    int priv;
};
基类成员 \ 继承方式 public 继承 protected 继承 private 继承
public public protected private
protected protected protected private
private 不可访问 不可访问 不可访问

基类 private成员在任何继承方式下,派生类都不能直接访问------它存在但编译器不让碰。


二、公有继承:is-a,接口继承

复制代码
class Derived : public Base {
    void internal() {
        pub = 1;   // public → public,类内可访问
        prot = 1;  // protected → protected,派生类内部可访问
        // priv = 1; // 编译错误:基类 private 成员,派生类永远不能直接碰
    }
};

类外:

复制代码
Derived d;
d.pub = 1;    // public 成员,类外可访问
// d.prot = 1; // 编译错误:protected 成员,类外禁止访问(protected 语义本身限制,与继承方式无关)

语义:Derived is a Base

复制代码
Base* p = new Derived; // 只有 public 继承支持向上转型

设计含义:基类定义接口契约,派生类提供特化实现,这是多态的基础。


三、保护继承:对外关门,家族内部留接口

复制代码
class Derived : protected Base {
    void internal() {
        pub = 1;   // 降级为 protected,派生类内部可访问
        prot = 1;  // protected → protected
        // priv = 1; // 编译错误:基类 private,不可访问
    }
};

类外:

复制代码
Derived d;
// d.pub = 1; // 编译错误:已降级为 protected,类外不可访问

子孙类仍然可用:

复制代码
class MoreDerived : public Derived {
    void internal() {
        pub = 1;   // 在 Derived 中是 protected,子孙类可访问
        prot = 1;  // protected,子孙类可访问
    }
};

向上转型:

复制代码
// Base* p = new Derived; // 编译错误:protected 继承,类外不允许向上转型

语义:复用 Base 能力,但不承认自己是一个 Base。

适用场景极少,多见于库或框架的中间层基类。


四、私有继承:实现复用,断绝对外和对下的接口暴露

复制代码
class Derived : private Base {
    void internal() {
        pub = 1;   // 降级为 private,派生类内部可访问
        prot = 1;  // 降级为 private
        // priv = 1; // 编译错误:基类 private,不可访问
    }
};

类外:

复制代码
Derived d;
// d.pub = 1; // 编译错误:已降级为 private,类外不可访问

子孙类也拿不到:

复制代码
class MoreDerived : public Derived {
    void internal() {
        // pub = 1;  // 编译错误:在 Derived 中已是 private,子孙类不可访问
    }
};

向上转型:

复制代码
// Base* p = new Derived; // 编译错误:private 继承,类外不允许向上转型

语义:Derived is implemented in terms of Base------不是 is-a,而是"用 Base 实现 Derived"。


五、三种继承方式对照

继承方式 建模关系 类外可访问基类 public 子孙类可访问基类接口 向上转型 Base*
public is-a 可以 可以 允许
protected 家族实现复用 不可以(降级为 protected) 可以 不允许
private 实现复用 不可以(降级为 private) 不可以 不允许

经验法则:

90% 情况用 public 继承。需要 has-a 或实现复用时,先考虑组合,再考虑 private 继承。protected 继承基本只在框架内部出现。


六、组合 vs 私有继承

私有继承能做的事,组合通常也能做且耦合更低:

复制代码
class Stopwatch {
    Timer timer;  // 组合:不引入继承关系,可持有多个,不影响派生类体系
public:
    void begin() { timer.start(); }
    void end()   { timer.stop(); }
};

私有继承仅在以下情况更有价值:

  • 需要重写基类虚函数

  • 需要访问基类 protected 成员

  • 需要 EBO(空基类优化)


七、using 可以有选择地"放出来"

复制代码
class Derived : private Base {
public:
    using Base::pub;  // 将 Base::pub 重新提升为 Derived 的 public 成员
};

限制:只能提升到继承方式允许的级别范围内,不能突破基类成员本身的访问级别。


八、默认继承方式(易踩坑)

复制代码
class Derived : Base {};     // 默认 private 继承
struct Derived : Base {};    // 默认 public 继承

工程规范:始终显式写明继承方式,不依赖默认规则。


九、一句话总结

  • public 继承:保接口、保类型关系,is-a

  • protected 继承:封外部、留家族,半隐藏

  • private 继承:封外部、断子孙,纯实现复用

  • 基类 private 成员:派生类永远不能直接访问

  • 继承方式 = 基类接口在派生类中的再封装策略

相关推荐
Wang's Blog1 小时前
Java 项目实战: 交友项目 - FastDFS:入门案例
java·开发语言·交友
apcipot_rain2 小时前
C++转Java日记(03)
java·开发语言·c++
布莱克6052 小时前
C++ 中 map 的 insert 与 emplace:key 已存在时会覆盖吗?
开发语言·数据结构·c++·算法·map
xys_6782 小时前
深挖C++:类和对象(中)
开发语言·c++
灿飞屋3 小时前
Rust 编程其实很简单
开发语言·rust
MOONICK4 小时前
QT元数据
开发语言·qt
D e re k ZH5 小时前
C++ 系统学习手记:从语法细节到工程实践
开发语言·数据结构·c++·算法
灿飞屋6 小时前
Kotlin与Java虚拟机三:控制流
android·java·开发语言·后端·python·kotlin·安卓
xiaoye-duck6 小时前
《C++ 项目:手写大模型接入 SDK》Ollama接口设计(上):初始化、全量响应实现和测试
开发语言·网络·c++·语言模型
zaizai10077 小时前
【Effective Modern C++】学习第一章
c++