
继承的概念
所谓继承,就是从"先辈"处获得特性,它是客观世界事物之间的一种重要关系。例如,脊椎动物和无脊椎动物都属于动物,在程序中便可以描述为:脊椎动物和无脊椎动物继承自动物;同时,哺乳动物和两栖动物继承自脊椎动物,而节肢动物和软体动物继承自无脊椎动物。这些动物之间会形成一个继承体系,如下图4-1所示。

在C++中,继承就是在原有类的基础上产生出新类,新类会继承原有类的所有属性和方法。原有的类称为基类或父类,新类称为派生类或子类。派生类同样可以作为基类派生出新类。在多层次继承结构中,派生类上一层的基类称为直接基类,隔层次的基类称为间接基类。例如在图4-1中,脊椎动物是哺乳动物的直接基类,动物是哺乳动物的间接基类。
在C++中,声明一个类继承另一个类的格式如下所示:
class 派生类名称:继承方式 基类名称
{
派生类成员声明
};
从上述格式可以看出,派生类的定义方法与普通类基本相同,只是在派生类名称后添加冒号":"、继承方式和基类名称。
其中:
- 派生类(子类):新定义的类,继承自基类。
- 基类(父类):被继承的类。
- 继承方式 :控制基类成员在派生类中的访问权限,包括
public、protected、private。
继承的基本特点
在类的继承中,有以下几点需要注意。
- 构造函数与析构函数不可继承:派生类必须定义自己的构造函数和析构函数,可调用基类的构造函数进行初始化。
- 继承全部成员:除构造与析构函数外,派生类继承基类的所有成员(包括私有成员,但访问受限)。
- 可扩展性:派生类可新增成员,也可重写(override)基类的成员函数。
- 多层次继承:派生类可作为基类继续派生,形成继承链。
- 多继承支持:一个派生类可同时继承多个基类(C++支持多继承)。
通过继承,基类中的所有成员(构造函数和析构函数除外)被派生类继承,成为派生类成员。在此基础上,派生类还可以增加新的成员。基类和派生类之间的关系如图4-2所示。

继承的底层原理
- 内存布局:派生类对象包含基类子对象和自身新增成员。基类部分位于派生类对象内存的起始位置。
- 成员访问:通过继承,派生类内部可访问基类的公有和保护成员,私有成员虽被继承但不可直接访问(成为"不可访问成员")。
- 类型关系:公有继承下,派生类对象可视为基类对象,支持赋值兼容(类型兼容)。
继承方式
在继承中,派生类会继承基类除构造函数、析构函数之外的全部成员。从基类继承的成员,其访问属性除了成员自身的访问属性,还受继承方式的影响。类的继承方式主要有三种:public(公有继承)、protected(保护继承)和private(私有继承)。不同的继承方式会影响基类成员在派生类中的访问权限。
public(公有继承)
特点 :基类的 public 和 protected 成员在派生类中保持原有访问权限;private 成员在派生类中不可访问。
语义 :派生类"是一个"基类(is-a关系),强调接口继承。
适用场景:大多数继承场景,尤其是需要实现多态和类型兼容的情况。
| 基类成员访问权限 | 在派生类中的访问权限 |
|---|---|
| public | public |
| protected | protected |
| private | 不可访问 |
CPP
#include <iostream>
using namespace std;
class Base {
public:
int publicVar;
protected:
int protectedVar;
private:
int privateVar;
};
class PublicDerived : public Base {
public:
void accessTest() {
publicVar = 10; // √ 可访问,仍然是public
protectedVar = 20; // √ 可访问,仍然是protected
// privateVar = 30; // × 编译错误:不可访问
}
};
int main() {
PublicDerived obj;
obj.publicVar = 100; // √ 外部可访问
// obj.protectedVar = 200; // × 外部不可访问
// obj.privateVar = 300; // × 外部不可访问
cout << "公有继承示例:" << endl;
cout << "publicVar: " << obj.publicVar << endl;
return 0;
}
protected(保护继承)
特点 :基类的 public 和 protected 成员在派生类中变为 protected;private 成员不可访问。
语义 :派生类"以受保护方式继承"基类实现,强调派生类及其子类可访问基类功能,但对外隐藏。
适用场景:设计中间类,既想复用基类实现,又不希望对外暴露基类接口。
| 基类成员访问权限 | 在派生类中的访问权限 |
|---|---|
| public | protected |
| protected | protected |
| private | 不可访问 |
CPP
#include <iostream>
using namespace std;
class Base {
public:
int publicVar;
protected:
int protectedVar;
private:
int privateVar;
};
class ProtectedDerived : protected Base {
public:
void accessTest() {
publicVar = 10; // √ 可访问,变为protected
protectedVar = 20; // √ 可访问,仍然是protected
// privateVar = 30; // × 编译错误:不可访问
}
void show() {
cout << "publicVar: " << publicVar << endl;
}
};
int main() {
ProtectedDerived obj;
// obj.publicVar = 100; // × 外部不可访问,已变为protected
// obj.protectedVar = 200; // × 外部不可访问
// obj.privateVar = 300; // × 外部不可访问
obj.show(); // √ 通过公有成员函数间接访问
return 0;
}
private(私有继承)
特点 :基类的 public 和 protected 成员在派生类中变为 private;private 成员不可访问。
语义 :派生类"以私有方式继承"基类实现(has-a关系),仅当前类可使用基类功能,后续派生类无法继续访问。
适用场景:实现组合(composition)的另一种方式,强调实现复用而非接口继承。
| 基类成员访问权限 | 在派生类中的访问权限 |
|---|---|
| public | private |
| protected | private |
| private | 不可访问 |
| 注意: |
- 不可访问成员:指在类内和类外均无法直接访问的成员,通常由私有继承或私有成员在派生类中形成。
- 保护继承 vs 私有继承 :在直接派生类中二者效果类似,但在进一步派生时,保护继承的成员仍为
protected,私有继承的成员变为不可访问。
CPP
#include <iostream>
using namespace std;
class Base {
public:
int publicVar;
protected:
int protectedVar;
private:
int privateVar;
};
class PrivateDerived : private Base {
public:
void accessTest() {
publicVar = 10; // √ 可访问,变为private
protectedVar = 20; // √ 可访问,变为private
// privateVar = 30; // × 编译错误:不可访问
}
void show() {
cout << "publicVar: " << publicVar << endl;
}
};
// 进一步派生类
class FurtherDerived : public PrivateDerived {
public:
void furtherTest() {
// publicVar = 100; // × 不可访问(在PrivateDerived中是private)
// protectedVar = 200; // × 不可访问(在PrivateDerived中是private)
}
};
int main() {
PrivateDerived obj;
// obj.publicVar = 100; // × 外部不可访问,已变为private
// obj.protectedVar = 200; // × 外部不可访问
// obj.privateVar = 300; // × 外部不可访问
obj.show(); // √ 通过公有成员函数间接访问
return 0;
}
类型兼容
类型兼容(也称赋值兼容)指在公有继承体系中,派生类对象可替代基类对象使用。这是C++多态性的基础之一。
兼容规则
若 Derived 公有继承自 Base,则:
派生类对象可赋值给基类对象(发生对象切片)
cpp
Derived d;
Base b = d; // 仅复制Base部分
派生类对象可初始化基类引用
cpp
Derived d;
Base& br = d;
派生类指针可赋值给基类指针
cpp
Derived* pd = new Derived();
Base* pb = pd;
派生类对象可作为基类类型参数传递
cpp
void func(Base b);
Derived d;
func(d); // 传递派生类对象
底层原理与注意事项
- 对象切片(Object Slicing):当派生类对象赋值给基类对象时,仅基类部分被复制,派生类新增成员被"切割",可能导致数据丢失。
- 动态绑定:通过基类指针或引用调用虚函数时,实际执行的是派生类重写的版本(多态)。
- 访问限制:通过基类指针/引用只能访问基类中定义的成员,无法直接访问派生类新增成员。
典型应用场景
- 函数参数泛化:使用基类类型作为参数,可接受任意派生类对象。
- 容器存储:使用基类指针容器(如
vector<Base*>) 统一管理不同派生类对象。 - 多态调用:通过虚函数机制实现运行时行为动态确定。