C++ 多态/抽象类/接口类
文章目录
一、C++ 多态
1.1 C++多态概述
- C++的**多态(Polymorphism)是面向对象编程(OOP)的核心特性之一,允许在运行时通过统一的接口调用不同派生类的函数,从而实现“同一接口,不同表现”**的能力。
- 多态分为两种:静态多态 和 动态多态。
- 静态多态也叫早绑定,是在编译时就确定调用哪个函数的行。
- 动态多态也叫晚绑定,在运行时根据对象的实际类型决定调用哪个函数,是通过**虚函数(virtual function)**实现的。
想象一下,你有一个遥控器(这就像是一个基类的指针),这个遥控器可以控制不同的电子设备(这些设备就像是派生类)。无论是电视、音响还是灯光,遥控器上的“开/关”按钮(这个按钮就像是一个虚函数)都能控制它们,但具体的操作(打开电视、播放音乐、开灯)则取决于你指向的设备。
1.2 如何实现多态
-
使用虚函数(Virtual Function):
-
我们在基类中定义一个虚函数,这个函数可以在任何派生类中被“重写”或者说“定制”。
-
使用关键字
virtual
来声明。
-
-
创建派生类并重写虚函数:
- 在派生类中,我们提供该虚函数的具体实现。这就像是告诉遥控器,“当你控制我的这个设备时,这个按钮应该这样工作”。
-
通过基类的引用或指针调用虚函数:
- 当我们使用基类类型的指针或引用来调用虚函数时,实际调用的是对象的实际类型(派生类)中的函数版本。
1.3 多态示例
#include <iostream>
using namespace std;
/* 基类 遥控器类 */
class RemoteCon{
public:
virtual void openFunc(){ // virtual 关键字使得该函数是一个虚函数,支持动态绑定(运行时绑定)
cout << "遥控器的开被按下" << endl;
}
};
/* 派生类 电视遥控器类 */
class TVRemoteCon : public RemoteCon{
public:
void openFunc() override{ // 派生类中虚函数的具体实现
cout << "电视遥控器的开被按下" << endl;
}
};
/* 派生类 音响遥控器类 */
class SoundRemoteCon : public RemoteCon{
public:
void openFunc() override{ // 派生类中虚函数的具体实现
cout << "音响遥控器的开被按下" << endl;
}
};
/* 派生类 灯光遥控器类 */
class LightRemoteCon : public RemoteCon{
public:
void openFunc() override{ // 派生类中虚函数的具体实现
cout << "灯光遥控器的开被按下" << endl;
}
};
/* 使用引用方式调用 */
void pressOpen(RemoteCon& remote)
{
remote.openFunc();
}
int main()
{
cout << "==========基类指针方式调用==========" << endl;
RemoteCon *remoteCon1 = new TVRemoteCon;
remoteCon1->openFunc();
RemoteCon *remoteCon2 = new SoundRemoteCon;
remoteCon2->openFunc();
RemoteCon *remoteCon3 = new LightRemoteCon;
remoteCon3->openFunc();
cout << "==========基类引用方式调用==========" << endl;
TVRemoteCon tv;
SoundRemoteCon sound;
LightRemoteCon light;
pressOpen(tv);
pressOpen(sound);
pressOpen(light);
return 0;
}
这段代码通过一个基类 RemoteCon
和三个派生类 TVRemoteCon
、SoundRemoteCon
、LightRemoteCon
演示了 C++ 中运行时多态的实现。基类中定义了一个虚函数 openFunc()
,各个派生类分别重写该函数。在 main()
函数中,分别使用了基类指针和基类引用两种方式调用虚函数,最终根据对象的实际类型,动态绑定到对应的函数实现上,输出不同的结果,体现了多态的核心特性——同一接口,不同行为。
1.4 为什么使用多态
- 灵活性:允许我们编写可以处理不确定类型的对象的代码。
- 可扩展性:我们可以添加新的派生类而不必修改使用基类引用或指针的代码。
- 接口与实现分离:我们可以设计一个稳定的接口,而将具体的实现留给派生类去处理。
二、C++ 抽象类
C++ 中的 抽象类(Abstract Class) 是一种不能实例化对象的类,它的主要作用是作为接口或基类,为派生类提供统一的函数接口规范。
2.1 什么是抽象类?
如果一个类中至少包含一个纯虚函数(pure virtual function),那么这个类就是抽象类。
✅ 纯虚函数语法:
virtual 返回类型 函数名(参数列表) = 0;
这个 = 0
就表示函数没有实现,派生类必须重写(override)。
2.2 抽象类的特点
-
包含至少一个纯虚函数:
-
抽象类至少有一个纯虚函数。这是一种特殊的虚函数,在抽象类中没有具体实现,而是留给派生类去实现。
-
纯虚函数的声明方式是在函数声明的末尾加上 = 0 。
-
-
不能直接实例化:
- 由于抽象类不完整,所以不能直接创建它的对象。就像你不能直接使用“交通工具”的概念去任何地方,你需要一个具体的交通工具。
-
用于提供基础结构:
- 抽象类的主要目的是为派生类提供一个共同的基础结构,确保所有派生类都有一致的接口和行为。
2.3 抽象类示例一
#include <iostream>
using namespace std;
/* 抽象类 遥控器接口类 */
class RemoteCon{
public:
virtual void openFunc() = 0; // 纯虚函数:必须由派生类实现
};
/* 派生类 电视遥控器 */
class TVRemoteCon : public RemoteCon{
public:
void openFunc() override{
cout << "电视遥控器的开被按下" << endl;
}
};
/* 派生类 音响遥控器 */
class SoundRemoteCon : public RemoteCon{
public:
void openFunc() override{
cout << "音响的遥控器开被按下" << endl;
}
};
/* 派生类 灯光遥控器 */
class LightRemoteCon : public RemoteCon{
public:
void openFunc() override{
cout << "灯光的遥控器开被按下" << endl;
}
};
/* 使用基类引用方式调用(体现多态) */
void pressOpen(RemoteCon& remote)
{
remote.openFunc();
}
int main()
{
cout << "==========基类指针方式调用(多态)==========" << endl;
RemoteCon *remoteCon1 = new TVRemoteCon;
RemoteCon *remoteCon2 = new SoundRemoteCon;
RemoteCon *remoteCon3 = new LightRemoteCon;
remoteCon1->openFunc();
remoteCon2->openFunc();
remoteCon3->openFunc();
cout << "==========基类引用方式调用(多态)==========" << endl;
TVRemoteCon tv;
SoundRemoteCon sound;
LightRemoteCon light;
pressOpen(tv);
pressOpen(sound);
pressOpen(light);
return 0;
}
这段代码演示了 C++ 中通过抽象类和虚函数实现多态的机制。基类 RemoteCon
定义了一个纯虚函数 openFunc()
,因此它是一个抽象类,不能被直接实例化。三个派生类 TVRemoteCon
、SoundRemoteCon
和 LightRemoteCon
分别继承自该抽象类,并实现了各自版本的 openFunc()
方法。main()
函数中分别使用了基类指针和基类引用来调用这些派生类的 openFunc()
,体现了运行时多态的特性:虽然通过统一的接口调用,但实际执行的是各个派生类各自实现的版本。这样设计使得代码更具扩展性和可维护性,是面向对象设计中“对接口编程”的典型范例。
2.4 抽象类示例二
#include <iostream>
using namespace std;
/* 基类 老师接口类 */
class Teacher{
public:
string name; // 名字
string shool; // 学校
string major; // 科目
// 纯虚函数,必须由派生类实现
virtual void goInClass() = 0;
virtual void startTeaching() = 0;
virtual void afterTeaching() = 0;
};
/* 派生类 英语老师 */
class EnglishTeacher : public Teacher{
public:
void goInClass() override{
cout << "英语老师进入教师" << endl;
}
void startTeaching() override{
cout << "英语老师开始教学" << endl;
}
void afterTeaching() override{
cout << "英语老师下课之后立马就回到了办公室" << endl;
}
};
/* 派生类 编程老师 */
class ProTeacher : public Teacher{
public:
void goInClass() override{
cout << "编程老师进入机房" << endl;
}
void startTeaching() override{
cout << "编程老师开始教学" << endl;
}
void afterTeaching() override{
cout << "编程老师下课之后还教学生调代码" << endl;
}
};
/* 派生类 电路老师 */
class CircuitTeacher : public Teacher{
public:
void goInClass() override{
cout << "电路老师进入实验室" << endl;
}
void startTeaching() override{
cout << "电路老师开始教学" << endl;
}
void afterTeaching() override{
cout << "电路老师下课之后还教学生画板子" << endl;
}
};
/* 使用基类引用方式调用(体现多态) */
void teacherFunc(Teacher& teacher)
{
teacher.goInClass();
teacher.startTeaching();
teacher.afterTeaching();
}
int main()
{
cout << "==========对象实例方式调用==========(静态绑定)" << endl;
EnglishTeacher e;
e.goInClass();
e.startTeaching();
e.afterTeaching();
cout << endl;
cout << "==========基类指针方式调用==========(多态/动态绑定)" << endl;
Teacher *teacher = new ProTeacher;
teacher->goInClass();
teacher->startTeaching();
teacher->afterTeaching();
cout << endl;
cout << "==========基类引用方式调用==========(多态/动态绑定)" << endl;
CircuitTeacher circuit;
teacherFunc(circuit);
cout << endl;
return 0;
}
这段代码通过一个抽象基类 Teacher
和三个派生类 EnglishTeacher
、ProTeacher
、CircuitTeacher
,系统演示了 C++ 中的抽象类、纯虚函数和多态。Teacher
类定义了三个纯虚函数,表示教师的上课流程,强制派生类必须实现这些函数,因此 Teacher
是一个抽象类,不能直接实例化。主函数中分别展示了三种调用方式:
- 对象实例调用(静态绑定):
EnglishTeacher e; e.startTeaching();
,在编译期就确定调用哪个函数,不支持多态; - 基类指针调用(动态绑定):通过
Teacher* teacher = new ProTeacher;
访问虚函数,实现运行时多态; - 基类引用调用(动态绑定):通过
teacherFunc(Teacher& teacher)
接口传入不同派生类对象,触发多态行为,调用对应类的重写函数。
整段代码体现了 C++ 中“接口编程、实现多态、提升扩展性”的设计思想。
三、C++ 接口类
在 C++ 中,**纯虚函数(pure virtual function)**和 接口类(interface class)是实现抽象和多态的重要机制,是面向对象编程中的核心概念之一。
3.1 什么是纯虚函数?
纯虚函数是在基类中声明但不提供实现,并要求所有派生类**必须重写(override)**的函数。
✅ 语法:
virtual void funcName() = 0;
这个 = 0
表示这是一个纯虚函数。
3.2 什么是接口类
在 C++ 中,一个类如果至少有一个纯虚函数,它就是一个抽象类,不能被实例化。如果一个类中所有成员函数都是纯虚函数,并且没有成员变量,我们称它为接口类。
接口类就是一种只定义“功能规范”而不实现“具体逻辑”的类。相当于告诉派生类:“你必须提供自己的实现”。
3.3 接口类作用和意义
特性 | 作用/好处 |
---|---|
强制派生类实现 | 保证所有子类都提供一致的功能实现 |
接口隔离 | 便于代码解耦和模块划分 |
支持多态 | 可以通过基类指针或引用调用派生类实现(运行时绑定) |
实现面向接口编程 | 避免依赖具体类,提高系统扩展性和可维护性 |
3.4 接口类实现步骤
一个类作为接口可以通过以下步骤来实现:
-
定义抽象类:创建一个包含纯虚函数的抽象类,这些函数构成了接口的一部分。这些函数在抽象类中只有声明而没有具体的实现。
-
派生类实现接口:派生类继承抽象类,并实现其中的纯虚函数,以具体实现接口定义的方法。
3.5 接口类示例
#include <iostream>
using namespace std;
/* 接口类 USB设备 */
class USBDevice{
public:
virtual void connect() = 0; // 纯虚函数,连接设备
virtual void disconnect() = 0; // 纯虚函数,断开设备
virtual ~USBDevice(){}; // 虚析构函数
};
/* 派生类 U盘 */
class FlashDevice : public USBDevice{
public:
void connect() override{
cout << "U盘已连接!" << endl;
}
void disconnect() override{
cout << "U盘已拔出!" << endl;
}
};
/* 派生类 打印机 */
class Printer : public USBDevice{
public:
void connect() override{
cout << "打印机已连接!" << endl;
}
void disconnect() override{
cout << "打印机已断开!" << endl;
}
};
/* 多态调用函数 */
void useUSBDevice(USBDevice& device)
{
device.connect();
cout << "正在使用设备..." << endl;
device.disconnect();
}
int main()
{
FlashDevice fd;
Printer pr;
cout << "=== 使用 U盘 ===" << endl;
useUSBDevice(fd);
cout << "\n=== 使用 打印机 ===" << endl;
useUSBDevice(pr);
return 0;
}
这段代码展示了 C++ 中 抽象类(接口类)与多态 的应用。USBDevice
是一个接口类,里面定义了两个纯虚函数 connect()
和 disconnect()
,表示所有 USB 设备都必须具备的连接和断开功能。FlashDevice
(U盘)和 Printer
(打印机)类继承了这个接口,并各自实现了这两个功能。在 main()
函数中,通过一个名为 useUSBDevice()
的函数,使用接口类引用来调用具体设备的行为,体现了多态:即使用相同的接口,可以操作不同种类的 USB 设备,提升了代码的通用性和可扩展性。整体结构清晰,非常适合作为理解接口与多态的入门示例。