764 字
2 分钟
【CPP】继承与多态
2026-03-26

类之间的关系#

关系英文含义
继承is-a派生类是一种基类
组合/聚合has-a整体包含部分
依赖/关联uses-a一个类使用另一个类
实现implements类实现接口(抽象类/纯虚)

另有 instance-of:对象是类的实例。

Liskov 替换原则 (LSP)#

子类型必须能够替换其基类型,且程序行为不变。

经典反例:从 Rectangle 派生 Square,若基类允许独立修改宽和高,正方形约束被破坏,行为上不能替换矩形。

实践

  • 继承应基于可替换的行为,不仅是分类直觉。
  • 子类扩展基类,不要破坏基类契约。

继承方式与访问#

class Base {
public:
int pub;
protected:
int prot;
private:
int priv;
};
class D_pub : public Base {}; // is-a,最常用
class D_prot : protected Base {};
class D_priv : private Base {};
继承方式基类 public基类 protected基类 private
publicpublicprotected不可访问
protectedprotectedprotected不可访问
privateprivateprivate不可访问

只有 public 继承才保持典型的 is-a 语义,适合多态。

成员继承与构造/析构#

成员/函数是否继承说明
普通成员函数子类同名函数隐藏基类所有重载(可用 using Base::f 引入)
虚函数override(C++11 建议显式写 override
静态成员函数子类同名同样隐藏基类同名
构造函数子类须在初始化列表调用基类构造
析构函数部分子类析构自动调用基类析构
友元友元不传递

注意:若基类没有默认构造且子类未在初始化列表中调用基类合适构造,子类的默认构造会被 = delete

class Base { public: Base(int) {} };
class Derived : public Base {
public:
Derived() : Base(0) {} // 必须显式调用
};

C++11:using Base::Base; 可继承构造函数。

多态与虚函数#

编译期多态:函数重载、模板。
运行期多态:通过基类指针/引用调用 virtual 函数,根据对象实际类型分派。

struct Animal {
virtual void speak() const { std::cout << "?\n"; }
virtual ~Animal() = default; // 多态基类必备
};
struct Dog : Animal {
void speak() const override { std::cout << "woof\n"; }
};
void call(const Animal& a) { a.speak(); } // 运行时决定

实现上,含虚函数的类常有 虚表指针 (vptr) 指向虚函数表;因此对象大小可能多一个指针。

纯虚函数与抽象类#

class Shape {
public:
virtual double area() const = 0; // 纯虚
virtual ~Shape() = default;
};
// Shape s; // 错误:不能实例化抽象类
class Circle : public Shape {
public:
double area() const override { return 3.14 * r * r; }
private:
double r;
};
  • 至少一个纯虚函数的类为抽象类
  • 派生类必须实现所有纯虚函数才能实例化(除非自己也成为抽象类)。
  • 接口类:抽象类中全部为纯虚函数。

overridefinal#

void f() override; // 编译期检查是否真的覆盖
void g() final; // 禁止进一步 override

多继承与菱形继承#

class A { public: int x; };
class B : public A {};
class C : public A {};
class D : public B, public C {}; // D 中有两份 A::x

问题D 对象含两份 A 子对象 → 二义性、浪费。

解决:虚继承

class B : virtual public A {};
class C : virtual public A {};
class D : public B, public C {}; // 仅一份 A

虚继承增加实现复杂度,应谨慎使用;优先组合往往更清晰。

指针/引用的转换#

转换方向安全性
上行派生 → 基类安全(隐式)
下行基类 → 派生不安全,需 dynamic_cast
Dog d;
Animal* pa = &d; // OK
Animal* pa = /* ... */;
Dog* pd = dynamic_cast<Dog*>(pa); // 失败返回 nullptr(指针形式)

重写 vs 重载(原笔记疑问)#

重载 (overload)重写/覆盖 (override)
作用域同一作用域基类与派生类
函数签名参数不同参数相同(协变返回类型例外)
绑定编译期运行期(虚函数)
关键字virtual / override
分享

如果这篇文章对你有帮助,欢迎分享给更多人!

【CPP】继承与多态
https://lysj.work/posts/studynotes/cpp/cpp继承与多态/
作者
Sekiro
发布于
2026-03-26
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时

目录