纯虚函数必须用=0声明且无函数体,含纯虚函数的类为抽象类不可实例化;派生类须重写全部纯虚函数才可实例化;纯虚函数可类外定义但不会被动态绑定调用。

纯虚函数必须用 = 0 声明,不能有函数体
纯虚函数本质是接口契约,只承诺存在,不提供实现。C++ 要求它在声明时以 = 0 结尾,且不能写花括号(哪怕空的)。一旦写了函数体,就不是纯虚函数了——编译器不会报错,但类可能意外变成可实例化的,破坏抽象意图。
-
virtual void draw() = 0;✅ 正确纯虚函数 -
virtual void draw() { }❌ 是普通虚函数,子类不重写也能实例化基类 -
virtual void draw() = 0 { }❌ 语法错误,= 0和函数体互斥
含纯虚函数的类自动成为抽象类,无法直接 new
只要类中至少有一个纯虚函数,该类就是抽象类。抽象类不能被实例化,连 new Base() 或 Base obj; 都会触发编译错误,比如:error: cannot declare variable 'obj' to be of abstract type 'Base'。这不是限制,而是设计信号:你得通过继承 + 实现所有纯虚函数,才能得到具体类型。
- 抽象类可以有构造函数、成员变量、普通/虚函数,甚至带实现的纯虚函数(见下一条)
- 派生类只有重写了全部纯虚函数,才不再是抽象类;漏一个,仍是抽象类
- 抽象类指针或引用(如
Base* p = new Derived();)完全合法,且是多态常用方式
纯虚函数可以有定义(但极少需要),仅用于被 :: 显式调用
绝大多数时候纯虚函数没函数体,但 C++ 允许你在类外为它提供定义,例如:void Base::draw() { /* 默认绘制逻辑 */ }。这不会让基类变可实例化,但允许派生类在重写时显式调用它:Base::draw();。实际项目中用得少,因为容易让人误以为它是“默认实现”——而它根本不会被动态绑定自动调用。
- 定义必须在类外写,类内只留
= 0声明 - 派生类重写后,
obj.draw()永远调用派生版本,绝不会自动 fallback 到基类定义 - 除非明确写
Base::draw(),否则这个定义形同虚设
抽象类不是接口,但可用作接口模拟;别和 interface 混淆
C++ 没有 interface 关键字,所以常把只含纯虚函数的类当作接口。但这只是惯用法,不是语言约束:抽象类完全可以有数据成员、非虚函数、构造函数,甚至私有纯虚函数(用于强制派生类实现某些内部协议)。真正要注意的是语义一致性——如果真想模拟 Java/C# 的接口,就别加字段和非纯虚函数,否则使用者会困惑。
立即学习“C++免费学习笔记(深入)”;
- 带数据成员的抽象类 ≠ 接口,它更接近“不可实例化的基类”
- 多个抽象基类继承(如
class Widget : public Drawable, public Serializable)是常见且安全的 - 虚继承解决菱形继承问题,和抽象类无关;是否虚继承取决于继承关系图,不因“抽象”而自动启用
纯虚函数和抽象类的边界很清晰,但最容易出错的是:把带函数体的虚函数误当纯虚函数,或者以为纯虚函数定义能被多态调用。盯住 = 0 和能否 new 这两个点,基本不会跑偏。











