什么是override和final?

109 阅读4分钟

什么是override和final?

  • override:告诉编译器“我这函数是重写基类的虚函数”,编译器会帮你检查签名是否完全匹配,避免你写错函数名、参数或遗漏virtual导致的“假重写”问题。
  • final:告诉编译器“这个虚函数不能再被子类重写了”,或者“这个类不能被继承了”,用来防止后续派生类修改行为,保证设计意图。
    本文首发于【讳疾忌医-note】公众号,未经授权,不得转载。
    个人教程网站内容更丰富:(www.1217zy.vip/)
    (加入我的知识星球,免费获取账号,解锁所有文章。t.zsxq.com/VZxX7)

传统写法的问题

在C++98/03中,虚函数重写靠程序员自觉,常见错误包括:


    
    
    
  struct Base {
    virtual void foo() const;
};

struct Derived : Base {
    void foo();  // 忘写const,没重写,只是新函数,静默错误
};

这段代码编译没错,但Derived::foo并没有真正重写Base::foo,而是定义了一个新的函数,导致多态失效,难以察觉。

C++11写法示例对比

传统写法(易错)


    
    
    
  struct Base {
    virtual void print() const;
};

struct Derived : Base {
    void print();  // 忘写const,没重写,导致多态失效
};

C++11写法(安全)


    
    
    
  struct Base {
    virtual void print() const;
};

struct Derived : Base {
    void print() const override;  // 编译器检查,确保正确重写
};

如果Derived::print签名不匹配,编译器会报错,帮你及时发现问题。

final关键字示例


    
    
    
  struct Base {
    virtual void foo();
};

struct Derived : Base {
    void foo() final;  // 不允许再被Derived的子类重写
};

struct MoreDerived : Derived {
    void foo();  // 编译错误,尝试重写final函数
};

final还能用在类上,禁止继承:


    
    
    
  struct NoMoreDerived final : Base {
    // 不能被继承
};

struct Attempt : NoMoreDerived {  // 编译错误
};

设计哲学:为什么引入override和final?

  • 明确意图:让程序员显式声明“这是重写”或“禁止重写”,代码意图清晰,减少误用。
  • 编译期检查:早发现隐藏的多态错误,避免运行时难以调试的bug。
  • 防止滥用继承final帮助设计者锁定类或函数行为,避免子类破坏基类契约。
  • 性能优化潜力:编译器知道某函数不可重写,可能做内联优化或去虚调用。

最佳使用场景

override

每次重写基类虚函数都应使用,防止签名错误,提升代码健壮性。

final

  • • 对于设计为不可再改写的虚函数,明确标记final
  • • 对于不希望被继承的类,标记final防止扩展。
  • • 性能关键代码中,利用final帮助编译器优化。

优缺点总结

优点缺点
override防止虚函数重写错误,提升代码安全性需要程序员养成习惯,初期可能增加代码书写量
final明确设计意图,防止滥用继承和重写过度使用final限制扩展性,降低代码灵活度
编译器可据此做优化,提升性能某些编译器对final优化支持不一,需具体测试
代码可读性和维护性大幅提升旧代码迁移时需逐步添加,存在兼容性挑战

常见误用及后果

  • 忘用override导致“假重写” :函数签名不匹配却未报错,运行时多态失效,难以发现。
  • 错误使用final限制类或函数继承:设计时未预见未来扩展需求,导致后续维护困难。
  • 混淆overridefinal用法override是声明重写,final是禁止重写,二者语义不同。
  • 滥用final导致代码僵化:过早锁定类层次结构,限制灵活设计和单元测试。

代码示例:override与final综合运用


    
    
    
  #include <iostream>
using namespace std;

struct Animal {
    virtual void speak() const {
        cout << "Animal speaks" << endl;
    }
    virtual void move() const {
        cout << "Animal moves" << endl;
    }
};

struct Dog : Animal {
    void speak() const override {  // 明确重写
        cout << "Dog barks" << endl;
    }
    void move() const final {  // 禁止再重写
        cout << "Dog runs" << endl;
    }
};

struct Puppy : Dog {
    void speak() const override {  // 允许重写
        cout << "Puppy yips" << endl;
    }
    // void move() const override {  // 编译错误,move已final
    //     cout << "Puppy crawls" << endl;
    // }
};

int main() {
    Animal* a = new Puppy();
    a->speak();  // Puppy yips
    a->move();   // Dog runs
    delete a;
    return 0;
}

总结

overridefinal的引入不仅是语言层面对虚函数机制的补强,更是现代C++设计哲学的体现--代码意图明确、错误早发现、设计更健壮。它们鼓励程序员以更严谨的态度对待继承和多态,避免“隐式行为”带来的陷阱。

尤其是override,我认为它应成为每个虚函数重写的标配,绝无例外。final则是设计者表达“这就是最终实现”的利器,既是保护机制,也是性能优化的潜在助力。

合理运用这两个关键字,能让你的代码不仅更安全,也更易于阅读和维护,是现代C++不可或缺的良好实践。