为什么C++基类的virtual虚函数必须定义?与Java抽象方法差异对比
问题核心原因
你遇到的链接错误是混淆了C中普通虚函数和纯虚函数的用法导致的,C对两类虚函数的实现要求完全不同:
1. 普通虚函数必须提供定义的原因
你代码中virtual void speak();属于普通虚函数,C++标准要求所有非纯虚的虚函数必须提供实现,原因有两点:
- 虚函数的地址需要被写入对应类的虚函数表(vtable),编译器构造
Animal类的虚表时,必须拿到Animal::speak的有效地址,没有定义的话链接阶段就会找不到符号,抛出LNK2001错误。 - 你没有将
Animal标记为抽象类,C++默认允许直接实例化Animal对象,也允许子类中显式调用基类的虚函数实现(比如在Cat::speak中调用Animal::speak()),编译器必须预留这些调用的实现入口。
如果你不想为基类虚函数编写空实现,正确的抽象基类写法是将虚函数声明为纯虚函数,只需要在声明末尾加=0即可:
#include <iostream> using namespace std; class Animal { public: virtual void speak() = 0; // 纯虚函数,不需要提供定义 }; class Cat : public Animal{ public: void speak() override { cout << "Meow!"; } }; int main() { Cat cat; Animal* base = &cat; base->speak(); }
此时Animal会被标记为抽象类,不允许直接实例化,虚表中该函数的位置会被标记为纯虚,不需要查找对应实现,你的代码无需添加空函数体就能正常编译运行。
和Java的差异说明
你写的Java示例中abstract void speak()本质等价于C++的纯虚函数,不是普通虚函数:
- Java的
abstract方法强制子类实现、抽象类不可实例化,和C++纯虚函数的设计目标完全一致。 - Java并非不支持基类可重写方法的默认实现:你完全可以在Java抽象类中编写带方法体的非抽象可重写方法,这和C++普通虚函数的逻辑是一致的。
- 你觉得Java的逻辑更符合预期,本质是你本来的需求就是实现抽象基类,应该直接使用C++的纯虚函数语法,而非先声明普通虚函数又不提供实现。
内容的提问来源于stack exchange,提问作者kandi
相关产品推荐
相关产品推荐

