卡码笔记
首页
计算机基础
C++
Java
面经
笔记广场 (opens new window)
代码随想录 (opens new window)
首页
计算机基础
C++
Java
面经
笔记广场 (opens new window)
代码随想录 (opens new window)
  • 基础与语法

  • 面向对象

    • C++构造函数有几种,分别什么作用?
    • 什么是构造函数和析构函数?构造函数和析构函数可以是虚函数吗?为什么?
    • C++的重载和重写,以及它们的区别和实现方式
    • C++怎么实现多态
    • C++中的虚函数和纯虚函数有什么区别?
    • 虚函数怎么实现的?
    • 虚函数表是什么
    • 多重继承的优缺点及菱形继承问题
    • 如何禁止一个类被继承
      • 简要回答
      • 详细回答
      • 代码示例
      • 知识拓展
    • 深拷贝和浅拷贝的区别?
    • this指针的原理
    • C++如何实现一个单例模式?
  • STL 与容器

  • 内存管理

  • C++11 与现代 C++

  • 智能指针

  • 并发与 I/O

# 如何禁止一个类被继承

# 简要回答

在C++中,禁止类被继承的核心方法是使用final关键字(C++11及以上标准)。

当final修饰类时,该类成为“最终类”,任何尝试继承它的派生类都会导致编译错误。

此外,C++11之前可通过基类构造函数私有化+虚继承+友元类的组合实现类似效果,但这种方式复杂且易出错,现已被final取代。

# 详细回答

    1. 使用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类 */ };
1
2
3

原理:final通过编译器的静态检查机制实现,编译器在解析继承关系时,若发现派生类试图继承final类,会立即终止编译并报错,无需运行时开销。

这种方式语义明确、安全,是现代C++中禁止继承的首选方案。

    1. 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的私有构造函数 */ };
1
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;
}
1
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;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 知识拓展

  • 知识图解 image
  • 适用场景:
  1. 工具类/辅助类:例如一个只包含静态数学函数的 MathUtils 类,它只有行为没有状态,继承它没有意义。

  2. 策略类:一个完成了特定算法实现的策略类,通常通过组合而非继承来使用。

  3. 表示特定实体的类:例如一个 FileDescriptor 类,它只是对系统资源的一个句柄,继承它可能破坏资源管理。

  4. 涉及系统级操作的类:其内部状态非常关键,不允许子类以不可控的方式修改。

  5. 性能关键类:使用 final 可以帮助编译器进行去虚拟化优化,减少间接调用的开销。

  • 面试官可能追问

Q: final 关键字在性能上有什么好处吗?

A: 有。编译器如果知道一个类是 final 的,或者一个虚函数是 final 的,它就可以进行去虚拟化 (Devirtualization) 优化。

这意味着编译器可以在编译时确定调用的是哪个具体函数,从而将虚函数调用(通过虚表指针间接调用)优化为普通的直接函数调用,甚至内联调用。

这消除了间接跳转的开销,对性能关键代码非常有益。

Last Updated: 3/10/2026, 6:08:48 PM

← 多重继承的优缺点及菱形继承问题 深拷贝和浅拷贝的区别? →

评论

验证登录状态...

侧边栏
夜间
卡码简历
代码随想录
卡码投递表🔥
2026群
添加客服微信 PS:通过微信后,请发送姓名-学校-年级-2026实习/校招
支持卡码笔记
鼓励/支持/赞赏Carl
1. 如果感觉本站对你很有帮助,也可以请Carl喝杯奶茶,金额大小不重要,心意已经收下
2. 希望大家都能梦想成真,有好的前程,加油💪