
怎么用 virtual 和 = 0 定义纯虚函数
纯虚函数不是“写个空函数”,而是用 virtual 声明 + = 0 强制派生类实现。它不提供定义,连函数体都不能写(哪怕写成 {} 都是错的)。
- 正确写法:
virtual void draw() = 0;—— 分号结尾,无花括号 - 错误写法:
virtual void draw() { }或virtual void draw() = 0 {} - 访问控制不影响抽象性:
protected:或private:下的virtual ... = 0仍能让类变成抽象类 - 纯虚函数可以有默认实现(少见但合法),但调用必须显式通过作用域:
Base::func();,派生类仍需重写才能实例化
为什么加了纯虚函数的类不能 new 实例
只要类里有一个(或多个)纯虚函数,编译器就把它标记为抽象类,禁止直接构造对象。这不是语法限制,而是语义强制:你声明了“这个行为必须由子类决定”,那当前类就不该有完整行为。
-
Base b;或new Base会触发编译错误:cannot declare variable 'b' to be of abstract type 'Base' - 允许指针/引用指向抽象类:
Base* p = new Derived;—— 这才是抽象类的正确使用姿势 - 注意:析构函数如果是纯虚的(
virtual ~Base() = 0;),必须提供定义(哪怕空实现),否则链接失败
纯虚函数和普通虚函数混用时要注意什么
一个类可以同时有纯虚函数和普通虚函数,但只要有一个纯虚函数,它就是抽象类。关键区别在于“是否强制重写”。
- 纯虚函数:
virtual int get_id() = 0;→ 派生类不实现就无法实例化 - 普通虚函数:
virtual void log() { std::cout → 派生类可选择是否重写 - 别误以为“虚函数=可选重写”,纯虚函数才是真正的契约;普通虚函数只是提供默认行为的便利
- 性能上没差别:都是虚表调用。但纯虚函数在编译期就卡住错误,比运行时报错更早暴露设计问题
抽象类继承链中容易漏掉的实现点
多层继承时,纯虚函数的“实现责任”不会自动下传。如果中间类没重写,它自己也变成抽象类,下游仍需补全。
立即学习“C++免费学习笔记(深入)”;
- 例如:
Base有virtual void run() = 0;→Middle继承Base但没实现run()→Middle仍是抽象类 -
Final继承Middle,必须实现run()才能实例化,不能指望Middle代劳 - 编译器不会提示“你忘了在
Middle里实现”,只会报错Final无法实例化 —— 很容易误以为是Final的问题,其实断点在中间层 - 检查方法:用 IDE 跳转到基类虚函数声明,看哪些派生类没覆盖;或者编译失败时,从错误信息里的类型名一层层往上查继承关系
抽象类本身不难写,难的是判断“哪里该抽象、哪里该留默认、哪一层该堵住实例化”。接口不是贴标签,是划清责任边界的动作。漏掉一个 = 0 或多写一个 {},整个继承体系的行为就可能跑偏。










