# 如何禁止一个类被继承
# 简要回答
在C++中,禁止类被继承的核心方法是使用final关键字(C++11及以上标准)。
当final修饰类时,该类成为“最终类”,任何尝试继承它的派生类都会导致编译错误。
此外,C++11之前可通过基类构造函数私有化+虚继承+友元类的组合实现类似效果,但这种方式复杂且易出错,现已被final取代。
# 详细回答
- 使用final关键字(C++11及以上)
final是C++11引入的关键字,专门用于限制类的继承或虚函数的重写。
当final用于修饰类时,编译器会将该类标记为**“不可继承”**,若派生类尝试继承它,编译器会直接报错(如“error: cannot derive from ‘final’ base class”)。
语法:class ClassName final { ... };
class Base final { /* 类定义 */ };
class Derived : public Base { /* 编译错误:Base是final类 */ };
2
3
原理:final通过编译器的静态检查机制实现,编译器在解析继承关系时,若发现派生类试图继承final类,会立即终止编译并报错,无需运行时开销。
这种方式语义明确、安全,是现代C++中禁止继承的首选方案。
- C++11之前的替代方案(不推荐)
在C++11之前,没有final关键字,需通过基类构造函数私有化+虚继承+友元类的组合实现:
步骤1:将基类的构造函数(包括默认构造函数、拷贝构造函数)声明为private,防止外部直接创建基类对象或派生类通过public/protected继承调用基类构造函数。
步骤2:基类通过virtual继承派生类(反向继承),强制派生类直接调用基类的构造函数(虚继承的特性)。
步骤3:将派生类声明为基类的friend(友元),允许派生类访问基类的私有构造函数。
示例:
class MakeFinal {
private:
MakeFinal() {} // 私有构造函数
friend class Derived; // 声明派生类为友元
};
class Derived : virtual public MakeFinal { // 虚继承
public:
Derived() {} // 必须调用基类构造函数(友元允许)
};
class FurtherDerived : public Derived { /* 编译错误:无法访问MakeFinal的私有构造函数 */ };
2
3
4
5
6
7
8
9
10
原理:虚继承要求派生类直接调用虚基类的构造函数,而基类构造函数为private且仅友元派生类可访问,因此非友元派生类无法继承,间接实现“禁止继承”。这种方式依赖复杂的继承关系和访问控制,易引入维护问题,已被final取代。
# 代码示例
#include <iostream>
class FinalClass final { // 声明为final类
public:
void print() { std::cout << "This is a final class." << std::endl; }
};
// 尝试继承FinalClass(编译错误)
// class Derived : public FinalClass {};
int main() {
FinalClass obj;
obj.print(); // 输出:This is a final class.
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
替代方案代码示例(c+11之前,现在不推荐这种写法)
#include <iostream>
class MakeFinal {
private:
MakeFinal() { std::cout << "MakeFinal constructor." << std::endl; }
friend class Derived; // 声明Derived为友元
};
class Derived : virtual public MakeFinal { // 虚继承
public:
Derived() { std::cout << "Derived constructor." << std::endl; }
};
class FurtherDerived : public Derived { // 尝试继承Derived(编译错误)
public:
FurtherDerived() { std::cout << "FurtherDerived constructor." << std::endl; }
};
int main() {
Derived obj; // 输出:MakeFinal constructor. Derived constructor.
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 知识拓展
- 知识图解

- 适用场景:
工具类/辅助类:例如一个只包含静态数学函数的 MathUtils 类,它只有行为没有状态,继承它没有意义。
策略类:一个完成了特定算法实现的策略类,通常通过组合而非继承来使用。
表示特定实体的类:例如一个 FileDescriptor 类,它只是对系统资源的一个句柄,继承它可能破坏资源管理。
涉及系统级操作的类:其内部状态非常关键,不允许子类以不可控的方式修改。
性能关键类:使用 final 可以帮助编译器进行去虚拟化优化,减少间接调用的开销。
- 面试官可能追问
Q: final 关键字在性能上有什么好处吗?
A: 有。编译器如果知道一个类是 final 的,或者一个虚函数是 final 的,它就可以进行去虚拟化 (Devirtualization) 优化。
这意味着编译器可以在编译时确定调用的是哪个具体函数,从而将虚函数调用(通过虚表指针间接调用)优化为普通的直接函数调用,甚至内联调用。
这消除了间接跳转的开销,对性能关键代码非常有益。
评论
验证登录状态...