虚函数支持多态且可不重写,基类提供默认实现;纯虚函数强制派生类实现,含纯虚函数的类为抽象类不可实例化;析构函数必须virtual以确保安全释放资源。

虚函数和纯虚函数不是“能不能多态”的区别,而是“要不要强制实现”的设计选择。用错会导致编译失败、对象无法创建,或运行时调用到意外的默认行为。
虚函数必须带 virtual,但可以不重写
只要在基类中用 virtual 声明,该函数就支持运行时多态——哪怕派生类完全不碰它,也能通过基类指针调用基类版本。
- 基类提供默认实现,派生类可选覆盖(用
override显式标注更安全) - 类仍可被实例化:
Base b;合法 - 常见于“有合理默认行为”的场景,比如
log()、cleanup()、serialize() - 注意:构造函数里调用虚函数不会动态绑定,永远走当前类的版本
纯虚函数声明必须写 = 0,且不能有函数体
virtual void draw() = 0; 这种写法直接宣告:这个函数只定义接口,不许有实现(哪怕空花括号也不行),否则编译报错。
- 含纯虚函数的类自动变成抽象类,
Shape s;编译不过 - 派生类若未实现所有纯虚函数,它自己也成抽象类,照样不能实例化
- 适合定义契约:比如图形库要求每个子类都得有
draw()和area(),但基类根本不知道怎么画圆还是方 - 例外:纯虚函数可以有定义(极少用),但只能在类外显式提供,且只能被派生类显式调用,比如
Base::func();
析构函数加 virtual 是硬性习惯,和是不是纯虚无关
哪怕类里全是纯虚函数,只要打算用基类指针管理派生类对象,virtual ~Base() = default; 就得写上。否则 delete ptr; 只会调用基类析构,派生类资源不释放。
- 纯虚析构函数写法是:
virtual ~Base() = 0;,但必须在类外定义(哪怕空实现) - 虚析构函数可以是默认的、自定义的,甚至
= default,但绝不能省略virtual - 普通成员函数是否
virtual不影响析构行为;只有析构函数的虚性决定 delete 是否安全
混合使用很常见,但别混淆“接口”和“扩展点”
一个抽象基类经常同时包含纯虚函数(强制实现)和虚函数(提供可选增强):
class Renderer {
public:
virtual ~Renderer() = default;
virtual void setup() {} // 虚函数:多数渲染器无需额外初始化
virtual void teardown() {} // 虚函数:清理可选
virtual void render() = 0; // 纯虚函数:必须知道怎么渲染
virtual bool supports_feature(const char*) = 0; // 纯虚函数:能力查询不可妥协
};
- 把“必须做”的事全设为纯虚,把“做了更好、不做也行”的设为虚函数
- 一旦某个虚函数在所有派生类中都被重写,说明它其实该升格为纯虚——这是接口演化的信号
- 别为了“看起来统一”而给纯虚函数加空实现,或把虚函数强行改成纯虚却留着基类默认逻辑
最易忽略的点:纯虚函数的强制性只在**实例化时检查**。如果你只定义派生类、从不 new 它,编译器可能不报错;但一加上 new Derived(),立刻暴露缺失实现。所以测试一定要走到对象构造环节。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











