C++接口(抽象类)
前言
这是C++面向对象的最后一节内容,本节内容将会用到前面类的相关知识以及C++基础教程以及C++类&对象,需要你具备前面的知识,如果你还有不会的或者说不熟悉的地方,请重新温习一下
C++ 接口(抽象类)
在 C++ 中,接口是一种“规定一类事物应该具备什么功能”的方式。可以将接口比作“合同”或“蓝图”,它描述了类的功能和行为,但不关心具体如何实现。
在 C++ 中,接口通常通过抽象类(Abstract Class)来实现。抽象类就像建筑的“蓝图”:它定义了应该做什么,但不告诉你怎么做。
目录
什么是抽象类?
抽象类是一种不能直接用来创建对象的特殊类,主要用来给其他类提供标准或模板。
关键特性:
- 至少包含一个纯虚函数:抽象类中至少有一个函数是“纯虚函数”。
- 纯虚函数的定义:纯虚函数是一种特殊的函数,它只有声明,没有实现,写法是
= 0。
举个例子
1 | class Shape { |
上面的类Shape就是一个抽象类,getArea()函数只是告诉其他类“你必须提供计算面积的功能”,但并没有实现具体的计算方法。
为什么需要抽象类?
抽象类的主要目的是定义接口,而不是实现细节。它告诉子类“你必须做这些事情,但怎么做由你决定”。这样可以:
- 统一标准:确保不同的子类都有一致的功能。
- 代码灵活:子类可以用自己的方式实现这些功能。
- 避免错误:如果某个子类没有实现必要的功能,编译器会报错。
类比说明
想象一下,你是一家汽车制造厂的设计师,制定了一份“汽车设计蓝图”(抽象类),规定了汽车必须有发动机、车轮等基本部件,但具体的发动机类型和车轮设计由不同的制造部门负责。这确保了所有汽车都具备基本功能,但具体实现可以多样化。
抽象类的规则
- 至少包含一个纯虚函数:抽象类中的函数
= 0,表示这个函数是纯虚函数,必须在子类中实现。 - 不能直接创建对象:无法用抽象类创建实例,如果尝试这么做,编译器会报错。
- 必须在子类中实现纯虚函数:子类如果没有实现纯虚函数,也会被当成抽象类,不能实例化。
举个例子
1 | class Shape { |
解释:
- 抽象类
Shape定义了接口getArea(),但没有实现。 - 子类
Rectangle实现了getArea(),所以它可以创建实例并使用。 - 尝试实例化
Shape会导致编译错误,确保接口不能被滥用。
抽象类的使用场景
抽象类通常用来:
- 统一子类功能:确保所有子类都有某些共同的功能。
- 提供灵活性:允许子类根据自己的需求,用不同的方式实现这些功能。
- 扩展系统:以后可以很容易地添加新的子类,而不会影响现有代码。
实际应用示例
假设你在开发一个图形编辑软件,需要处理多种形状(如矩形、圆形、三角形等)。你可以创建一个抽象类Shape,定义所有形状必须具备的接口,如getArea()和draw(),然后各个具体形状类继承Shape并实现这些接口。
抽象类的实例
示例代码
假设我们有一个基类Shape,它代表一个形状,并定义了一个接口getArea()。然后我们用它的两个子类Rectangle(矩形)和Triangle(三角形)分别实现计算面积的功能。
1 |
|
输出结果:
1 | Rectangle Area: 35 |
解释:
- 抽象类
Shape定义了计算面积的接口getArea(),并且提供了设置宽度和高度的方法。 - 子类
Rectangle和Triangle分别实现了getArea(),用自己的公式计算面积。 - 主程序中通过接口调用
getArea(),而不需要关心子类的具体实现。
常见错误示例
为了更好地理解抽象类的限制,来看几个常见的错误示例。
1. 尝试实例化抽象类
1 | int main() { |
错误信息:
1 | error: cannot declare variable ‘shape’ to be of abstract type ‘Shape’ |
2. 子类未实现所有纯虚函数
1 | class IncompleteShape : public Shape { |
错误信息:
1 | error: cannot declare variable ‘ishape’ to be of abstract type ‘IncompleteShape’ |
抽象类与接口的比较
虽然 C++ 没有明确的“接口”关键字,但抽象类可以用来实现接口的功能。抽象类:
- 可以包含成员变量和已实现的成员函数。
- 适用于需要共享代码和数据的场景。
接口(通过纯虚类实现):
- 只包含纯虚函数,不包含成员变量。
- 适用于仅需要定义行为规范的场景。
示例:接口类
1 | class IPrintable { |
虚析构函数的重要性
在抽象类中定义虚析构函数,确保通过基类指针删除子类对象时,子类的析构函数能够被正确调用,防止资源泄漏。
示例:
1 | class Shape { |
如果Shape的析构函数不是虚函数,删除shape时只会调用Shape的析构函数,导致Rectangle的析构函数未被调用,可能引发资源泄漏。
总结
抽象类在面向对象编程中扮演着重要角色,它提供了一个统一的接口,确保所有子类实现特定的功能。通过使用抽象类,可以:
- 强制性:确保子类实现必要的功能,避免遗漏。
- 代码复用:在抽象类中实现通用功能,子类只需专注于特定部分。
- 灵活性和扩展性:方便添加新功能或子类,而不影响现有代码。
- 易于维护:外界依赖于抽象类提供的接口,内部实现可以灵活变化。


