C++ 继承修饰符背后隐藏着什么?
\n\n想象一下,您是一位建筑师。您有一张“建筑”蓝图(基类),并希望根据它建造三栋不同的“房子”(派生类)。一栋房子是精确的复制品,带有公共入口(public);第二栋带有封闭的内院(private);第三栋则只允许家人和朋友进入(protected)。在 C++ 中,继承修饰符正是这样工作的。但是,这种选择会影响程序的运行速度吗?剧透:既会,也不会。让我们来弄清楚如何避免搬起石头砸自己的脚,同时还能优化代码。
这个问题是 Stack Overflow 上最热门的问题之一(有数万次浏览),这并非没有原因。新手经常混淆类成员的访问级别和继承类型。而有经验的开发者知道:正确选择继承方式可以节省数小时的调试时间,甚至能略微提升程序速度。今天,在 pythonlib.ru 上,我们将一劳永逸地解决这个问题。
\n\n继承三角:public、private、protected
\n\n首先,基础知识。假设我们有一个基类 Animal:
class Animal {\npublic:\n void eat() { /* ... */ }\nprotected:\n int age;\nprivate:\n std::string secretDNA;\n};\n\n现在,让我们看看三种继承类型如何改变派生类 Dog 对这些成员的访问权限。
1. Public 继承 — “原样保留”
\n这是最常见且最直观的类型。所有访问级别保持不变:
\n- \n
public→public(对所有代码可见) \n protected→protected(对派生类可见) \n private→ 不可访问(保留在基类中) \n
当您想表达 "Dog IS-A Animal" 的关系时使用。这是经典的 OOP。
\n\n2. Protected 继承 — “仅限内部使用”
\n基类的所有 public 成员在派生类中变为 protected:
\n- \n
public→protected\n protected→protected\n private→ 不可访问 \n
当您想对外部世界隐藏实现细节,但允许“孙子类”访问时使用。例如,Boost 库在其内部机制中积极使用 protected 继承。
\n\n3. Private 继承 — “我的,只属于我的”
\n这是最严格的模式。所有 public 和 protected 成员都变为 private:
\n- \n
public→private\n protected→private\n private→ 不可访问 \n
这表示 "Dog 是通过 Animal 实现的" 关系。它常被用来替代组合以实现优化(详见下文)。
\n\n性能:关键点在哪里?
\n\n现在,我们来看看这个引人入胜的核心问题:继承类型会影响性能吗?简而言之:直接来说,不会;但间接来说,非常会。
\n\n误区:private 继承比 public 更快
\n有些人认为 private 继承“消除了多余的检查”。这是错误的。访问修饰符(public、protected、private)是纯粹的编译期概念。编译器在构建阶段检查它们,并生成相同的机器码。运行时没有区别。
现实:通过空基类优化 (EBO) 实现优化
\n这才是 private 继承真正能加速程序的地方!如果基类不包含任何数据(空类),那么在 public 或 protected 继承下,编译器可能(但不是必须)会添加一个额外的字节用于对齐。而在 private 继承下,空基类优化 (Empty Base Optimization) 会生效。