纯虚函数必须写=0且不可有函数体,本质是强制派生类重写;漏写=0则变为普通虚函数,失去接口约束力;抽象类可混用纯虚、虚和普通函数,析构函数应声明为virtual,多继承需用虚继承避免菱形问题。

纯虚函数必须写 = 0,不实现且不能有函数体
纯虚函数的本质是强制派生类重写——编译器靠 = 0 标记它“不提供默认行为”,不是语法糖也不是宏。漏写 = 0 就变成普通虚函数,接口约束立刻失效。
- 错误写法:
virtual void draw();→ 编译通过,但子类不重写也能实例化基类,失去接口意义 - 正确写法:
virtual void draw() = 0;→ 基类变抽象类,无法直接new,子类不实现draw()就编译报错 - 注意:哪怕写了空函数体(如
virtual void draw() = 0 { })也会编译失败——= 0和函数体互斥
抽象类里可以混用纯虚函数、虚函数和普通成员函数
接口 ≠ 全是纯虚函数。实际设计中,常把共用逻辑(比如日志、校验、资源包装)放在抽象基类的非纯虚函数里,由子类复用;只把行为差异点设为纯虚。
-
virtual void init() { log("init"); ... }→ 子类自动继承,无需重写 -
virtual void render() = 0;→ 每个子类必须自己实现渲染逻辑 - 构造函数/析构函数可以是虚的,但析构函数建议声明为
virtual ~Shape() = default;,否则delete派生类指针时可能不调用子类析构
多继承接口时,重复继承同一基类会引发菱形问题
用 class Drawable : public Shape, public Serializable 很常见,但如果两个接口都继承自 Object,又没用虚继承,子类对象里就会出现两份 Object 子对象——导致 static_cast 失败、sizeof 异常、甚至调用错函数。
- 接口类本身应尽量无数据成员、无非虚函数,降低虚继承负担
- 若必须共享基类,显式用
class Shape : virtual public Object和class Serializable : virtual public Object - 检查方式:打印
sizeof子类,或尝试dynamic_cast<Object*>(ptr)是否唯一成功
纯虚函数不能是 static、template 或 inline
纯虚函数要被动态绑定,而 static 属于类作用域、template 实例化在编译期、inline 是优化提示——三者都与运行时多态机制冲突。
立即学习“C++免费学习笔记(深入)”;
- 错误:
virtual static void create() = 0;→ 编译报错:static member function cannot be virtual - 错误:
template<typename T> virtual void process(T) = 0;→ 编译报错:templates cannot be virtual - 替代方案:用非虚的
static工厂函数 + 纯虚的实例方法组合;模板部分移到派生类内部实现
接口真正难的不是语法,是判断哪些行为该收进基类、哪些该放飞给子类——边界模糊时,宁可多拆一个中间抽象层,也别让某个子类被迫实现一堆 { /<em> do nothing </em>/ }。









