一、 继承的基本概念
- 核心思想:继承是面向对象编程(OOP)的三大特性之一,允许派生类(子类) 复用基类(父类) 的属性和方法,同时可以扩展或修改原有功能,实现代码复用和层次化设计。
- 基类与派生类关系
- 基类:被继承的类,包含通用的属性和方法。
- 派生类:继承基类的类,可新增成员,也可重写基类成员。
- 语法格式:
cpp
class 派生类名 : 继承方式 基类名 {
// 派生类成员
};
二、 三种继承方式
继承方式决定了基类成员在派生类中的访问权限,核心规则如下:
继承方式 基类公有成员 基类保护成员 基类私有成员 特点总结
public 公有继承 派生类公有 派生类保护 不可访问 基类公有/保护成员权限不变,私有成员始终不可直接访问
protected 保护继承 派生类保护 派生类保护 不可访问 基类公有成员降级为保护权限
private 私有继承 派生类私有 派生类私有 不可访问 基类公有/保护成员全部降级为私有权限
关键注意点:
- 基类的 private 成员无论哪种继承方式,派生类都无法直接访问,只能通过基类的 public/protected 成员函数间接访问。
- 继承方式默认为 private ( class 定义的类), struct 默认继承方式为 public 。
三、 派生类的构造与析构
派生类对象的创建和销毁遵循固定顺序,核心规则如下:
- 构造函数执行顺序
- 先调用 基类的构造函数(若基类有多个构造函数,需在派生类构造函数初始化列表中指定)
- 再调用 成员对象的构造函数(按成员对象在派生类中的声明顺序)
- 最后调用 派生类自身的构造函数体
- 语法示例:
cpp
class Base {
public:
Base(int a) : a(a) {} // 基类带参构造
private:
int a;
};
class Derived : public Base {
public:
// 派生类构造函数初始化列表:先基类,再成员对象,最后自身
Derived(int a, int b) : Base(a), b(b) {}
private:
int b;
};
- 析构函数执行顺序
- 与构造顺序完全相反
- 先调用 派生类析构函数体
- 再调用 成员对象的析构函数
- 最后调用 基类的析构函数
四、 成员的隐藏与重写
- 成员隐藏(同名覆盖)
- 当派生类定义了与基类同名的成员变量或成员函数(参数列表可以不同),派生类成员会隐藏基类成员,直接访问时默认调用派生类成员。
- 若要访问基类被隐藏的成员,需使用 基类名::成员名 显式指定。
- 示例:
cpp
class Base {
public:
void show() { cout << "Base show" << endl; }
};
class Derived : public Base {
public:
void show() { cout << "Derived show" << endl; } // 隐藏基类show
};
int main() {
Derived d;
d.show(); // 调用派生类show
d.Base::show(); // 显式调用基类show
return 0;
}
- 成员重写(覆盖,为多态做准备)
- 派生类定义与基类同名、同参数列表、同返回值的虚函数,称为重写(override)。
- 重写是实现多态的前提,与成员隐藏的核心区别是基类函数必须加 virtual 关键字。
五、 继承中的特殊情况
- 继承的访问控制延伸
- 派生类的子类(孙子类)能否访问基类成员,取决于两层继承的方式。
- 例如:基类 Base 公有继承给 Derived1 , Derived1 保护继承给 Derived2 ,则 Base 的公有成员在 Derived2 中为保护权限。
- 不可继承的成员
- 基类的构造函数、析构函数、赋值运算符重载函数不能被继承,派生类需要自己定义(或使用编译器默认生成的)。
- 基类的 static 成员可以被继承,属于整个类族共享,访问方式: 基类名::成员名 或 派生类名::成员名 。
- 菱形继承与二义性
- 菱形继承:两个派生类继承同一个基类,又有一个类同时继承这两个派生类,导致基类成员在最终派生类中存在两份副本,引发访问二义性。
- 解决方法:使用虚继承(在继承时加 virtual 关键字),让最终派生类只保留一份基类成员。
- 虚继承语法:
cpp
class A {};
class B : virtual public A {}; // 虚继承
class C : virtual public A {}; // 虚继承
class D : public B, public C {}; // 无二义性
六、 继承的优缺点
- 优点
- 代码复用:减少重复代码,基类的通用功能只需编写一次。
- 层次化设计:清晰体现类之间的从属关系(如 Animal → Dog → Husky ),符合现实逻辑。
- 便于扩展:派生类可在基类基础上新增功能,不修改基类代码,符合开闭原则。
- 缺点
- 增加类之间的耦合度,基类的修改可能影响所有派生类。
- 菱形继承等场景容易引发二义性,需额外处理。