C++ 继承
在 C++ 中,继承(inheritance)是一种面向对象编程的机制,允许你在已有类(基类/父类)的基础上派生出新类(派生类/子类),从而复用、扩展或修改已有行为。下面分几个方面介绍继承的要点与用法。
1. 基本语法
// 基类
class Base {
public:
void foo() { /*…*/ }
virtual void bar() { /*…*/ }
protected:
int data_;
};
// 派生类,public 继承
class Derived : public Base {
public:
void baz() { /*…*/ }
void bar() override { /* 重写基类的 virtual bar() */ }
};
class D : public B
表示 公有继承(public inheritance),最常用。- 还可用
protected
或private
继承,分别将基类的公有和保护成员降为派生类的保护或私有成员(不常用)。
2. 访问控制与继承方式
继承方式 | 基类的 public → 派生 | 基类的 protected → 派生 | 派生对外可见性 |
---|---|---|---|
public | public | protected | 派生对外可访问 public 成员 |
protected | protected | protected | 派生的所有继承成员都 protected |
private | private | private | 派生的所有继承成员都 private |
3. 构造与析构
- 构造顺序:先调用基类构造,再调用派生类构造。
- 析构顺序:先调用派生类析构,再调用基类析构。
- 在派生类构造函数的初始化列表中,可显式调用基类构造:
class Derived : public Base { public: Derived(int x, int y) : Base(x) // 调用 Base(int) , member_(y) {} private: int member_; };
4. 多重继承与菱形继承
4.1 多重继承
class A { /*…*/ };
class B { /*…*/ };
class C : public A, public B { /*…*/ };
- C 同时继承自 A 和 B,但会分别包含两份 A、B 的子对象。
4.2 菱形继承与虚继承
当多个派生路径导致“钻石”结构,普通多重继承会引入基类多份子对象,可能造成二义性:
class A { /*…*/ };
class B : public A { /*…*/ };
class C : public A { /*…*/ };
class D : public B, public C { /*…*/ };
// D 中有两份 A 子对象:B::A 和 C::A
使用 虚继承(virtual inheritance)可保证只有一份共享的 A:
class B : virtual public A { /*…*/ };
class C : virtual public A { /*…*/ };
class D : public B, public C { /*…*/ };
5. 覆盖与重载
- 重载(overload)是指同一类中同名函数参数列表不同。
- 覆盖(override)是派生类重写基类的虚函数。使用
override
关键字可让编译器检查签名是否一致:struct Base { virtual void f(int); }; struct Derived : Base { void f(int) override; // 正确覆盖 void f(double) override; // 错误:没有对应基类重载可覆盖 };
6. 多态与虚函数
- 基类将函数声明为
virtual
,允许派生类提供不同实现。 - 通过基类指针或引用调用虚函数时,运行期(RTTI)会动态绑定到派生类版本,实现 动态多态:
Base* p = new Derived; p->bar(); // 调用 Derived::bar()
- 别忘了给基类声明虚析构函数,避免通过基类指针删除派生对象时发生资源泄漏:
struct Base { virtual ~Base() = default; };
7. 禁止继承与最终继承
- C++11 起可用
final
修饰,防止某个类被进一步派生,或某个虚函数被重写:class Base final { /*…*/ }; // Base 不能被继承 class B { virtual void f() final; // f() 不能被覆盖 };
8. 对象切片(Object Slicing)
如果用基类对象而非指针/引用来接收派生类实例,会发生对象切片,派生类的扩展部分被截断:
Derived d;
Base b = d; // 切片:b 只保留 Base 部分
- 避免将派生对象直接赋值给基类对象,使用指针或引用来保留完整多态性。
小结
- 通过 公有继承 复用并扩展基类行为,使用虚函数实现运行期多态。
- 理解 访问控制、构造/析构顺序、虚继承(解决菱形问题)、对象切片 等细节,才能设计出安全、可维护的类层次。
- 使用
override
、final
、虚析构等现代特性,可让继承体系更健壮、错误更易发现。