
C++ final 关键字详解:从虚函数到类继承的完整指南
- [一、final 关键字详解](#一、final 关键字详解)
-
- [1、 引言](#1、 引言)
- [2、final 关键字概述](#2、final 关键字概述)
- [3、final 修饰虚函数](#3、final 修饰虚函数)
-
- [3.1、 基本语法](#3.1、 基本语法)
- [3.2、 代码示例](#3.2、 代码示例)
- [3.3、 与 override 的配合使用](#3.3、 与 override 的配合使用)
- [4、 final 修饰类](#4、 final 修饰类)
-
- [4.1、 基本语法](#4.1、 基本语法)
- [4.2、 代码示例](#4.2、 代码示例)
- [5、 final 与 override 的区别](#5、 final 与 override 的区别)
- [6、final 的实际应用场景](#6、final 的实际应用场景)
-
- [6.1、 防止意外重写](#6.1、 防止意外重写)
- [6.2 、明确设计意图](#6.2 、明确设计意图)
- [6.3 、优化性能](#6.3 、优化性能)
- [7、 注意事项](#7、 注意事项)
- [8、 总结](#8、 总结)
- 二、代码示例
一、final 关键字详解
1、 引言
在 C++ 的面向对象编程中,final 关键字是一个重要的控制修饰符。它允许开发者明确地限制类继承和虚函数重写的行为,从而增强代码的安全性和可维护性。本文将深入探讨 final 关键字的两种主要用途------修饰虚函数和修饰类,并通过丰富的代码示例帮助读者全面掌握其用法。
2、final 关键字概述
final 是 C++11 标准引入的关键字,用于在继承体系中施加约束。它的核心作用可以概括为两点:
- 阻止虚函数被进一步重写 :当在虚函数声明后加上
final,派生类将无法再对该函数进行重写。 - 阻止类被继续继承 :当在类名后加上
final,该类将不能作为基类被其他类继承。
这两个特性为开发者提供了更精细的类设计控制手段,有助于构建更稳定、更安全的继承体系。
3、final 修饰虚函数
3.1、 基本语法
在虚函数的声明末尾添加 final 关键字,即可禁止该函数在后续派生类中被重写:
cpp
class Base {
public:
virtual void func() final {
// 函数实现
}
};
3.2、 代码示例
cpp
#include <iostream>
class Base {
public:
virtual void show() final {
std::cout << "Base::show()" << std::endl;
}
};
class Derived : public Base {
public:
// 错误!无法重写 final 函数
// void show() override {
// std::cout << "Derived::show()" << std::endl;
// }
};
int main() {
Derived d;
d.show(); // 输出: Base::show()
return 0;
}
在上面的示例中,Base::show() 被声明为 final,因此 Derived 类中尝试重写该函数的代码会导致编译错误。如果注释掉 Derived 中的重写代码,程序将正常调用基类的 show() 函数。
3.3、 与 override 的配合使用
final 和 override 可以同时使用,以明确表达「重写基类虚函数,且不允许后续再重写」的意图:
cpp
class Base {
public:
virtual void process() {
std::cout << "Base::process()" << std::endl;
}
};
class Derived : public Base {
public:
void process() override final {
std::cout << "Derived::process()" << std::endl;
}
};
class Derived2 : public Derived {
public:
// 错误!Derived::process() 是 final 的
// void process() override {
// std::cout << "Derived2::process()" << std::endl;
// }
};
这里 Derived::process() 使用 override 表明它重写了基类的虚函数,同时使用 final 禁止 Derived2 再次重写。
4、 final 修饰类
4.1、 基本语法
在类名之后添加 final 关键字,即可禁止该类被继承:
cpp
class FinalClass final {
// 类定义
};
4.2、 代码示例
cpp
class Base final {
public:
void hello() {
std::cout << "Hello from Base" << std::endl;
}
};
// 错误!无法继承 final 类
// class Derived : public Base {
// };
int main() {
Base b;
b.hello(); // 输出: Hello from Base
return 0;
}
当尝试继承一个 final 类时,编译器会报错,从而在编译阶段就阻止了不合理的继承设计。
5、 final 与 override 的区别
final 和 override 虽然经常一起出现,但它们的职责完全不同:
| 关键字 | 作用 | 使用位置 |
|---|---|---|
override |
声明该函数重写了基类的虚函数,编译器会检查是否确实存在可重写的基类虚函数 | 虚函数声明末尾 |
final |
禁止该虚函数被进一步重写,或禁止该类被继承 | 虚函数声明末尾 / 类名之后 |
简单来说,override 是「向下确认」,final 是「向上封锁」。两者可以独立使用,也可以组合使用。
6、final 的实际应用场景
6.1、 防止意外重写
在大型项目中,某些核心虚函数可能承载关键逻辑,不希望被派生类意外修改。使用 final 可以保护这些函数的实现不被破坏。
6.2 、明确设计意图
final 向代码阅读者传达了明确的设计意图:这个类或函数是继承体系的终点。这有助于团队成员理解类的设计边界,避免误用。
6.3 、优化性能
在某些编译器和优化场景下,final 可以帮助编译器进行去虚拟化(devirtualization)优化。当编译器确定某个虚函数不会被重写时,可以直接调用而非通过虚函数表间接调用,从而提升运行效率。
7、 注意事项
final只能用于虚函数,不能用于非虚成员函数。final不能与override混淆,二者语义不同但可以共存。- 使用
final修饰类后,该类不能作为基类,但可以正常实例化对象。 final是 C++11 标准引入的,使用前请确保编译器支持 C++11 或更高版本。
8、 总结
final 关键字是 C++ 继承体系中的重要控制工具。通过阻止虚函数重写和类继承,它帮助开发者构建更安全、更清晰的类层次结构。在实际开发中,合理使用 final 可以提升代码的可维护性和运行效率,是每个 C++ 开发者都应掌握的关键特性。
二、代码示例
cpp
#include <iostream>
// 父类
class Base
{
public:
virtual void hello()
{
std::cout << "Base hello\n";
}
virtual void setup()
{
std::cout << "Base setup\n";
}
};
// ========== 用法1:函数加final,类仍可继承,但该虚函数禁止重写 ==========
class Son : public Base
{
public:
// override:重写父类虚函数;final:后代不能再重写hello
void hello() override final
{
std::cout << "Son hello\n";
}
// setup 没有final,子类可以继续重写
void setup() override
{
std::cout << "Son setup\n";
}
};
// 尝试继承 Son
class GrandSon : public Son
{
public:
// ❌ 编译报错:Son::hello 标记final,不能重写!
// void hello() override {}
// ✅ setup没有final,可以正常重写
void setup() override
{
std::cout << "GrandSon setup\n";
}
};
// ========== 用法2:类加final,整个类禁止被继承(推荐你的驱动场景) ==========
class FinalClass final : public Base
{
public:
void hello() override
{
std::cout << "FinalClass hello\n";
}
};
// ❌ 编译报错:FinalClass 是final,禁止继承
// class TryInherit : public FinalClass {};
int main()
{
GrandSon gs;
gs.hello();
gs.setup();
FinalClass fc;
fc.hello();
return 0;
}

