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 成员:派生类永远不能直接访问

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

相关推荐
丰色木夕24 分钟前
Ubuntu22.04下源码编译安装USD-Brep
c++·ubuntu·nvidia·usd-brep
benchmark_cc28 分钟前
Python 量化系统如何设计统一的数据访问接口:从策略解耦到数据层架构
开发语言·python·数据分析·pandas·量化交易·股票数据·quantdash
const_qiu41 分钟前
第四章 00:开篇 —— 从「会聊天」到「记得住、会干活、能协作」
java·开发语言·人工智能·python
Wang's Blog1 小时前
Java 项目实战: 交友项目 - 个人资料管理的更新接口实现与坑
java·开发语言·交友
CoderYanger1 小时前
Java EE 进阶:4.Spring Web MVC 入门(第一弹)
java·开发语言·前端·spring·面试·java-ee·mvc
估值探索者1 小时前
【Python量化选股流水线 #04】选股器好不好用?「昨日候选→今日表现」回看验收
开发语言·python·股票数据·股票数据api接口
爱喝热水的呀哈喽2 小时前
HEJIAN RESEARCH
开发语言·matlab
白露与泡影3 小时前
后端 Java 面试核心八股汇总|高频面试题 + 解析,覆盖基础到进阶
java·开发语言·面试
xifangge20253 小时前
基于 JavaScript 实现 CNC 数控切削参数与螺纹公差纯前端核算引擎
开发语言·前端·javascript
“AI国潮设计-小江”3 小时前
【AIGC实战】Python+SDXL批量生成「潮汕英歌舞冰淇淋蛋糕&红桃粿咖啡」IP,固定角色形象批量出甜品商业效果图
开发语言·人工智能·python·prompt·aigc