纯虚函数必须用=0声明且无函数体,含纯虚函数的类为抽象类不可实例化;析构函数可纯虚但须定义;接口类需虚析构、禁拷贝、无数据成员。

纯虚函数必须用 = 0 声明,且不能有函数体
纯虚函数本质是强制派生类实现的接口契约,语法上只允许声明、不允许定义。一旦在类中声明了纯虚函数,该类就自动成为抽象类,无法实例化。
常见错误是误写成 virtual void func() = 0 {} 或 virtual void func() = 0; 后又在类外定义 void Base::func() { }——这两者都会导致编译失败(前者语法非法,后者违反纯虚函数语义)。
-
= 0必须紧贴函数声明末尾,中间不能有空格或注释 - 构造函数、析构函数可以是虚函数,但构造函数不能是纯虚;析构函数可声明为纯虚,但**必须提供定义**(哪怕空实现),否则链接失败
- 纯虚函数可以有默认参数,但默认值由调用方(即派生类对象通过基类指针调用时)的静态类型决定,不是动态绑定的
抽象类里可以混用普通成员、虚函数和纯虚函数
抽象类不是“全纯虚”的接口容器,它完全可以封装共用逻辑。比如网络模块的 Connection 抽象基类,可能有纯虚的 connect() 和 send(),但共用的重连逻辑、超时计时器管理、日志前缀生成等,都可作为 protected 的普通成员函数存在。
关键判断标准不是“有没有非纯虚函数”,而是“是否存在至少一个纯虚函数”——只要有一个,就不能 new 实例。
立即学习“C++免费学习笔记(深入)”;
- 纯虚函数用于定义接口契约(如
virtual bool start() = 0;) - 普通虚函数用于提供可选重写的默认行为(如
virtual void on_error() { log("default error handler"); }) - 非虚成员函数用于封装不可覆盖的内部逻辑(如
void validate_config() const;)
C++ 没有 interface 关键字,靠纯虚类模拟接口
C++ 标准不支持 Java 那种 interface 语法,所谓“接口类”就是所有函数都是 public 纯虚、无数据成员、无构造/析构逻辑的抽象类。但它不是语言层面的独立概念,只是约定俗成的设计模式。
典型接口类写法:class ILogger { public: virtual ~ILogger() = default; virtual void log(const char* msg) = 0; };。注意两点:析构函数必须是虚的(通常设为 = default),且推荐显式加 public:,避免误用 private 继承破坏多态。
- 接口类应禁止拷贝(删掉拷贝构造/赋值):在 public 区声明
ILogger(const ILogger&) = delete; - 若需移动语义,可添加移动构造/赋值,但多数接口类只被指针/引用持有,无需移动
- 不要在接口类里放 static 成员或友元——这会污染接口纯洁性,也违背单一职责
派生类未实现所有纯虚函数会导致编译错误或链接失败
如果子类继承抽象基类但没重写全部纯虚函数,它自己仍是抽象类;只有当所有纯虚函数都被实现(且非 inline 定义在类内时需确保链接可见),才能实例化。
容易忽略的是:纯虚函数在派生类中重写时,override 关键字不是必须的,但强烈建议加上。它能防止签名不一致(比如参数类型差一个 const)导致的静默失败——编译器会报错,而不是让你以为已实现。
- 未实现的纯虚函数在链接阶段报错,错误信息类似:
undefined reference to 'Base::pure_func()' - 若派生类只实现了部分纯虚函数,而你试图 new 它,编译器会直接拒绝:
error: cannot declare variable 'x' to be of abstract type 'Derived' - 纯虚函数可以被派生类中同名函数隐藏(比如参数不同),此时不会触发重写,也不会报错——这是 C++ 名字查找规则导致的陷阱,务必核对签名是否完全匹配
抽象类的核心约束不在语法多严格,而在设计意图是否清晰:哪些必须由用户定制,哪些可以复用,哪些绝对不能碰。把纯虚函数当成 API 边界来画,比纠结“是不是够纯”更有实际意义。









