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

  • 面向对象

  • STL 与容器

  • 内存管理

  • C++11 与现代 C++

    • C++11中的新特性有哪些
    • C++11中的多线程编程
    • 左值引用和右值引用的区别
    • 移动语义有什么作用,原理是什么
    • 完美转发的作用及实现
    • 说一下c++中stdmove与stdforward的区别
    • 说一下lambda函数
    • 仿函数与lambda性能对比
    • c++中 STL中仿函数与lambda表达式的性能对比
    • C++中的RAII机制
    • C++中的异常处理机制
      • 简要回答
      • 详细回答
      • 代码示例
      • 知识拓展
    • C++中的协程概念及实现
  • 智能指针

  • 并发与 I/O

# C++中的异常处理机制

面试时没有回答好问题是很正常的,被面试官质疑时,要有今天不会明天会的底气,面后做好复盘,不在同一道题目上跌倒第二次,今天就从 C++中的异常处理机制 背起来

# 简要回答

C++异常处理基于try-throw-catch机制,提供结构化的错误处理方式。

try块定义监控范围,throw抛出异常对象,catch捕获并处理异常。

支持异常类型匹配、栈展开和资源自动清理,通过RAII保证异常安全。

# 详细回答

C++异常处理核心组件:

1.异常抛出(throw),使用throw表达式抛出异常对象),可以抛出任意类型的对象(内置类型、自定义类、标准异常等),抛出异常会立即终止当前函数执行

2.异常捕获(catch),catch块按顺序匹配异常类型,支持异常类型继承层次中的多态捕获,可以使用catch(...)捕获所有异常

3.栈展开(Stack Unwinding),从抛出点开始向上查找匹配的catch块,自动调用局部对象的析构函数,保证资源正确释放

4.异常规格(Exception Specifications),C++11引入noexcept说明符,替代旧的动态异常规格,提供编译期异常保证

# 代码示例

基本的异常处理示例

#include <iostream>
#include <stdexcept>
#include <string>

// 自定义异常类
class MyException : public std::exception {
private:
    std::string message;

public:
    MyException(const std::string& msg) : message(msg) {}

    const char* what() const noexcept override {
        return message.c_str();
    }
};

double divide(double a, double b) {
    if (b == 0) {
        throw std::invalid_argument("Division by zero!");
    }
    return a / b;
}

void process_data(int value) {
    if (value < 0) {
        throw MyException("Negative value not allowed: " + std::to_string(value));
    }
    if (value > 100) {
        throw std::out_of_range("Value too large: " + std::to_string(value));
    }
    std::cout << "Processing value: " << value << std::endl;
}

void basic_exception_demo() {
    try {
        std::cout << "10 / 2 = " << divide(10, 2) << std::endl;
        std::cout << "10 / 0 = " << divide(10, 0) << std::endl;  // 这里会抛出异常
    }
    catch (const std::invalid_argument& e) {
        std::cout << "Caught invalid_argument: " << e.what() << std::endl;
    }

    try {
        process_data(50);   // 正常
        process_data(-5);   // 抛出MyException
        process_data(200);  // 不会执行到这里
    }
    catch (const MyException& e) {
        std::cout << "Caught MyException: " << e.what() << std::endl;
    }
    catch (const std::exception& e) {
        std::cout << "Caught std::exception: " << e.what() << std::endl;
    }
    catch (...) {
        std::cout << "Caught unknown exception" << std::endl;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

文件管理中的RAII noexcept和异常的安全保证

#include <iostream>
#include <vector>

class NoExceptDemo {
public:
    // 基本不抛出异常的函数
    int get_value() const noexcept {
        return 42;
    }

    // 可能抛出异常的函数
    void risky_operation() {
        throw std::runtime_error("This might fail");
    }

    // 移动构造函数通常标记为noexcept
    NoExceptDemo(NoExceptDemo&& other) noexcept {
        std::cout << "Move constructor (noexcept)" << std::endl;
    }

    // 析构函数不应该抛出异常
    ~NoExceptDemo() noexcept {
        std::cout << "Destructor (noexcept)" << std::endl;
    }
};

void noexcept_usage() {
    NoExceptDemo demo;

    // noexcept运算符检查表达式是否可能抛出异常
    std::cout << "get_value is noexcept: " << noexcept(demo.get_value()) << std::endl;
    std::cout << "risky_operation is noexcept: " << noexcept(demo.risky_operation()) << std::endl;

    // 在vector重新分配时,noexcept移动构造函数更高效
    std::vector<NoExceptDemo> items;
    items.push_back(NoExceptDemo());  // 优先使用移动构造
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

# 知识拓展

  • 知识图解

image

  • 适用场景

不可恢复错误 - 内存分配失败、硬件错误等

构造函数失败 - 对象构造无法完成时

资源获取失败 - 文件打开、网络连接等

逻辑错误 - 违反前置条件、无效参数等

跨多层调用错误处理 - 错误需要向上传递多级时

  • 面试官很能追问

Q1: 异常处理相比返回错误码有什么优势?

A1:分离错误处理:正常逻辑和错误处理代码分离,提高可读性

自动传播:异常自动向上传播,无需每层检查错误码

类型安全:异常是类型安全的,编译器可以检查类型匹配

无法忽略:未处理的异常会导致程序终止,错误不会被静默忽略

构造函数支持:构造函数无法返回错误码,只能使用异常报告失败

Q2: 什么是栈展开?在异常处理中起什么作用?

A2: 栈展开是从异常抛出点开始,沿着调用链向上查找匹配catch块的过程。在这个过程中:

自动调用局部对象的析构函数

释放自动存储期的资源

维护调用栈的完整性

保证资源不泄漏(结合RAII) 栈展开确保了即使发生异常,程序也能正确地清理资源。

Q3: 为什么应该按引用捕获异常?

A3: 按引用捕获异常有多个优势:

避免切片:捕获派生类异常时保持多态性

避免拷贝:不需要复制异常对象,提高性能

保持原始异常:能够重新抛出原始异常对象

一致性:与标准库异常处理方式一致

Q4: 在析构函数中抛出异常会有什么问题?

A4:在析构函数中抛出异常是危险的,因为:

双重异常:如果栈展开过程中析构函数抛出异常,会立即调用terminate

资源泄漏:可能中断其他资源的清理过程

未定义行为:C++标准规定这种情况会导致程序终止

最佳实践:析构函数应该用noexcept声明,并吞掉所有异常

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

← C++中的RAII机制 C++中的协程概念及实现 →

评论

验证登录状态...

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