纯虚函数类和抽象类

纯虚函数类和抽象类

纯虚函数
在声明处写 = 0 的虚函数,表示“此处不提供最终实现,派生类必须(通常应)重写”。
语法:virtual 返回类型 函数名(参数) = 0;

抽象类
含有至少一个纯虚函数(或从基类继承而未覆盖的纯虚函数)的类。抽象类不能被实例化,只能作为基类使用。

接口类

成员函数全是虚函数,析构函数也是虚函数。


抽象类实现代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct Shape {                     // 抽象类:含纯虚函数
virtual ~Shape() = default; // 建议虚析构
virtual double area() const = 0; // 纯虚
virtual void draw() const = 0; // 纯虚
};

struct Circle : Shape {
double r {1.0};
double area() const override { return 3.14159 * r * r; }
void draw() const override { /* ... */ }
};

// Shape s; // ❌ 错:抽象类不能实例化
Circle c; // ✅

注:纯虚析构函数必须定义函数体

原因:

销毁顺序决定了基类析构一定会被调用
删除一个派生对象时,顺序是:先调派生类析构 ~D(),然后一定会调基类析构 ~B() 完成善后。既然会调用 ~B(),链接器/运行时就需要找到它的函数体。如果你只声明了纯虚而没定义函数体,就找不到实现——于是报错。

“纯虚”只让类变抽象,不代表“不会被调用”
把析构设为纯虚是为了让类不可实例化(抽象类),但析构函数本身仍要有可执行的定义,因为销毁派生对象时要走到它。

测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
struct B {
virtual ~B() = 0; // 纯虚析构 —— 声明
};
B::~B() {}//如果删掉就无法调用

struct D : B {
~D() override {} // 正常的派生析构
};

int main() {
B* p = new D();
delete p; // 这里需要调用 B::~B()
}