在C++11标准之前,开发者想要限制某个类被继承或者某个虚函数被重写,只能通过间接的设计手段实现,操作繁琐且不够直观。final关键字的出现很好地解决了这个问题,它可以直接作用于类和虚函数,明确表达设计意图,让代码的约束性更强。

final关键字的基本语法
final关键字是C++11新增的上下文关键字,它只能用在类声明或者虚函数声明的末尾,不能单独作为变量名或者其他用途使用。它的核心作用是标记被修饰的对象不可被进一步扩展或者重写。
修饰类的语法
当需要限制一个类不能被其他类继承时,可以在类名后面的冒号之前添加final关键字。语法格式如下:
// 定义不能被继承的基类
class BaseClass final {
public:
BaseClass() = default;
~BaseClass() = default;
void showInfo() {
std::cout << "这是基类的方法" << std::endl;
}
};
// 尝试继承BaseClass会编译报错
// class DerivedClass : public BaseClass { // 错误:不能继承final类
// };
修饰虚函数的语法
当需要限制某个虚函数在派生类中被重写时,可以在虚函数声明的末尾添加final关键字。需要注意的是,被final修饰的虚函数必须是原本就可以被重写的,也就是它所在的类本身可以被继承,或者它是从基类继承来的虚函数。
#include <iostream>
class Parent {
public:
// 可以被重写的虚函数
virtual void print() {
std::cout << "父类打印方法" << std::endl;
}
// 不能被重写的虚函数
virtual void display() final {
std::cout << "父类显示方法,不可重写" << std::endl;
}
virtual ~Parent() = default;
};
class Child : public Parent {
public:
// 重写父类的print方法,合法
void print() override {
std::cout << "子类重写后的打印方法" << std::endl;
}
// 尝试重写父类的display方法会编译报错
// void display() override { // 错误:不能重写final虚函数
// }
};
final关键字的使用场景
限制类继承的场景
当某个类的设计初衷就是作为最终的实现类,不需要被扩展时,可以使用final修饰。比如工具类、包含特定资源管理的类,或者设计上不允许有派生类的场景。这样可以避免其他开发者误继承该类,导致资源管理混乱或者逻辑错误。
限制虚函数重写的场景
当某个虚函数的实现已经是最优方案,不需要也不允许派生类修改其逻辑时,可以使用final修饰该虚函数。比如在基类中实现了线程安全的资源释放方法,不希望派生类重写后破坏线程安全逻辑,就可以用final标记该方法。
使用final关键字的注意事项
- final关键字只能用于类或者虚函数,不能用于普通函数、成员变量或者非虚函数。
- 被final修饰的虚函数仍然是虚函数,仍然可以通过基类指针或引用调用派生类的其他可重写虚函数,只是它本身不能被重写。
- 如果类被final修饰,那么该类的所有虚函数即使没有显式标记final,也不能被重写,因为类本身不能被继承。
- final和override关键字可以配合使用,不过final本身已经表达不可重写的含义,通常不需要和override同时出现。
实际代码示例
下面是一个完整的示例,展示final关键字在类和虚函数上的综合使用:
#include <iostream>
#include <string>
// final类,不能被继承
class Logger final {
public:
void log(const std::string& msg) {
std::cout << "[日志] " << msg << std::endl;
}
};
// 普通基类
class Shape {
public:
// 可重写的虚函数
virtual double getArea() const {
return 0.0;
}
// final虚函数,不可重写
virtual void draw() const final {
std::cout << "绘制基础图形" << std::endl;
}
virtual ~Shape() = default;
};
class Circle : public Shape {
public:
Circle(double r) : radius(r) {}
// 重写getArea方法
double getArea() const override {
return 3.14 * radius * radius;
}
// 尝试重写draw方法会报错
// void draw() const override {}
private:
double radius;
};
int main() {
// Logger lg; // 如果尝试继承Logger会编译失败
Circle c(5.0);
std::cout << "圆的面积: " << c.getArea() << std::endl;
c.draw();
Shape* shape = &c;
shape->draw(); // 调用的是Shape的draw方法,因为不能被重写
return 0;
}
通过上述示例可以看到,final关键字能够清晰地表达代码的设计约束,让代码的意图更加明确,减少不必要的继承带来的问题。在实际开发中,合理使用final关键字可以提升代码的可读性和可维护性。