纯虚函数必须声明为virtual void f() = 0且无函数体;含纯虚函数的类为抽象类,不可实例化但可定义指针/引用;接口类应仅含public纯虚函数、无数据成员和非纯虚函数;多重继承时各基类纯虚函数须分别显式实现。

纯虚函数的定义语法必须带 = 0
纯虚函数不是“没实现的虚函数”,而是**语法上强制要求子类重写**的虚函数。它的定义必须在声明末尾写 = 0,且不能有函数体(哪怕空花括号也不行):
class Shape {
public:
virtual double area() const = 0; // ✅ 正确:= 0,无实现
virtual void draw() = 0; // ✅ 同样合法,const 不是必需的
virtual void init() = 0 {} // ❌ 错误:写了 {} 就不能再写 = 0
};
常见错误是把普通虚函数和纯虚函数混淆——比如只写 virtual void f(); 而不加 = 0,这仍是可实例化的虚函数,不会让类变成抽象类。
抽象类无法实例化,但可以定义指针和引用
只要类中至少有一个纯虚函数,它就是抽象类,new Shape() 或 Shape s; 都会编译失败,报错类似:error: cannot declare variable 's' to be of abstract type 'Shape'。
但你完全可以:
立即学习“C++免费学习笔记(深入)”;
- 用
Shape*指向派生类对象(多态基础) - 用
const Shape&绑定临时对象或子类实例 - 作为函数参数或返回类型(如工厂函数返回
std::unique_ptr)
注意:抽象类的构造函数/析构函数仍会被调用,所以析构函数建议也声明为 virtual ~Shape() = default;,否则通过基类指针 delete 可能导致未定义行为。
接口类(Interface)要避免数据成员和非纯虚函数
C++ 没有 interface 关键字,所谓“接口类”是靠约定形成的规范:只含 public 纯虚函数、无数据成员、无构造函数实现、析构函数为纯虚或虚默认。
典型反例:
class BadInterface {
protected:
int id_; // ❌ 数据成员破坏接口纯洁性
public:
virtual void run() = 0;
virtual void stop() { } // ❌ 提供默认实现,就不是纯粹的契约了
};
更干净的写法:
class Drawable {
public:
virtual ~Drawable() = default;
virtual void draw() const = 0;
virtual void update() = 0;
};
如果真需要默认行为,应由派生类自己用组合或模板方式复用,而不是在接口里塞实现。
多重继承下纯虚函数的实现容易漏覆盖
当一个类从多个抽象基类继承时,每个纯虚函数都必须被显式实现,哪怕签名完全一样也不能“自动继承”:
class A { public: virtual void f() = 0; };
class B { public: virtual void f() = 0; };
class C : public A, public B {
public:
void f() override { } // ✅ 必须写,否则 C 仍是抽象类
};
若遗漏,编译器不会帮你合并或提示“已实现”,只会继续报错 C is an abstract class。更隐蔽的情况是签名看似相同但 const/volatile 限定符不一致,比如 A::f() 和 B::f() const 是两个不同函数,C 必须分别实现。
这种细节在大型模块解耦或跨团队接口对接时特别容易出问题,建议配合静态分析工具(如 clang-tidy 的 cppcoreguidelines-interfaces-global-init 类规则)做检查。









