C++: 异常处理

最后更新:2026-08-26

程序运行时,总会遇到意外:文件不存在、内存不足、网络断开……

如果不管,程序就会崩溃。

异常处理就是程序的"安全气囊"——出问题时,优雅地应对,而不是直接崩溃。


1. 异常处理基础

(1) 1.1 什么是异常?

异常是程序运行时出现的错误或意外情况

常见异常:


(2) 1.2 异常处理三要素

C++异常处理用三个关键字:

关键字 功能
try 监控可能出错的代码
catch 捕获并处理异常
throw 抛出异常

(3) 1.3 基本示例

示例:除以零异常(难度⭐)

▶ 示例 1:代码示例(难度⭐)

CPP
#include <iostream>

int divide(int a, int b) {
 if (b == 0) {
 throw "除数不能为0!"; // 抛出异常
 }
 return a / b;
}

int main() {
 try {
 std::cout << divide(10, 2) << std::endl; // 正常
 std::cout << divide(10, 0) << std::endl; // 抛出异常
 }
 catch (const char* msg) { // 捕获异常
 std::cerr << "错误:" << msg << std::endl;
 }
 
 std::cout << "程序继续运行" << std::endl;
 return 0;
}
▶ 试一试

输出:

TEXT 📖 仅展示
5
错误:除数不能为0!
程序继续运行

运行结果:

TEXT 📖 仅展示
5
错误:除数不能为0!
程序继续运行

💡 提示:



2. 异常类型

(1) 2.1 抛出的类型

throw 可以抛出任何类型

类型 示例
基本类型 throw 42;
字符串 throw "error";
标准异常 throw std::runtime_error("error");
自定义异常 throw MyException();

(2) 2.2 捕获多个异常

示例:捕获不同类型的异常(难度⭐⭐)

CPP
#include <iostream>
### ▶ 示例 2:代码示例(难度⭐)

#include <string>

void process(int value) {
 if (value == 0) {
 throw 0; // 抛出int
 }
 if (value < 0) {
 throw std::string("负数"); // 抛出string
 }
}

int main() {
 try {
 process(-5);
 }
 catch (int e) {
 std::cerr << "捕获int异常:" << e << std::endl;
 }
 catch (const std::string& e) {
 std::cerr << "捕获string异常:" << e << std::endl;
 }
 
 return 0;
}


3. 标准异常

(1) 3.1 std::exception层次

C++标准库定义了一套异常类,都在 stdexcept 中:

CPP
std::exception
 ├── std::logic_error
 │ ├── std::invalid_argument
 │ ├── std::out_of_range
 │ └── std::length_error
 └── std::runtime_error
 ├── std::overflow_error
 └── std::underflow_error

(2) 3.2 常用标准异常

异常类 说明
std::invalid_argument 参数非法
std::out_of_range 越界
std::runtime_error 运行时错误
std::bad_alloc 内存分配失败(new失败)

示例:使用标准异常(难度⭐⭐)

CPP
#include <iostream>
#include <stdexcept>
#include <vector>

int main() {
 std::vector<int> v = {1, 2, 3};
 
 try {
 v.at(10); // 越界访问
 }
 catch (const std::out_of_range& e) {
 std::cerr << "捕获异常:" << e.what() << std::endl;
 }
 
 return 0;
}


4. 自定义异常

(1) 4.1 继承std::exception

最佳实践: 自定义异常类继承 std::exception

示例:自定义异常类(难度⭐⭐⭐)

CPP
#include <iostream>
#include <exception>
#include <string>

// 自定义异常类
class MyException : public std::exception {
private:
 std::string msg;
 
public:
 MyException(const std::string& msg) : msg(msg) {}
 
 const char* what() const noexcept override {
 return msg.c_str();
 }
};

int main() {
 try {
 throw MyException("自定义异常");
 }
 catch (const MyException& e) {
 std::cerr << "捕获自定义异常:" << e.what() << std::endl;
 }
 
 return 0;
}


5. 异常安全

(1) 5.1 什么是异常安全?

异常安全是指:即使抛出异常,程序也能保持一致状态(不泄漏资源、不破坏数据)。

三个保证层次:

  1. 基本保证:程序状态一致,但可能变化
  2. 强保证:操作要么成功,要么完全回滚(像没发生过)
  3. 不抛异常保证:函数绝不抛出异常

(2) 5.2 RAII——资源管理的利器

RAII(Resource Acquisition Is Initialization):用对象管理资源,析构函数自动释放。

示例:用智能指针保证异常安全(难度⭐⭐⭐)

CPP
#include <iostream>
#include <memory>

void process() {
 // 用unique_ptr管理内存,即使抛出异常也会自动释放
 std::unique_ptr<int> p(new int(42));
 
 // ... 可能抛出异常的代码 ...
 
 std::cout << *p << std::endl;
} // p自动释放

int main() {
 try {
 process();
 }
 catch (...) {
 std::cerr << "捕获异常" << std::endl;
 }
 return 0;
}


6. noexcept关键字

(1) 6.1 基本用法

C++11引入 noexcept,声明函数不抛出异常

示例:声明不抛异常(难度⭐)

CPP
void safeFunction() noexcept {
 // 这个函数保证不抛出异常
}

void unsafeFunction() {
 // 可能抛出异常
}

💡 提示:


❓ 常见问题

Q:什么时候用异常? A:用于真正意外的情况,不用于正常控制流。


Q:catch(...)是什么? A:捕获所有异常,通常用于清理资源后重新抛出。

CPP
try {
 // ...
}
catch (...) {
 // 清理资源
 throw; // 重新抛出
}

Q:构造函数能抛异常吗? A:可以,但要小心内存泄漏。用智能指针或RAII管理资源。


▶ 示例 3:捕获异常(难度⭐)

CPP
#include <iostream>
#include <stdexcept>

double divide(double a, double b) {
    if (b == 0) {
        throw std::runtime_error("除数不能为0");
    }
    return a / b;
}

int main() {
    try {
        std::cout << divide(10, 2) << std::endl;
        std::cout << divide(10, 0) << std::endl;
    } catch (const std::exception& e) {
        std::cout << "错误:" << e.what() << std::endl;
    }

    return 0;
}
▶ 试一试

输出:

TEXT 📖 仅展示
错误:除数不能为0
💡 提示try-catch 捕获异常,throw 抛出异常。what() 返回错误信息。


知识点 要点
try-catch-throw 异常处理三要素
标准异常 std::runtime_error
自定义异常 继承 std::exception
异常安全 RAII保证资源不泄漏
noexcept 声明函数不抛异常

📖 小节

📝 作业

  1. **基础题 (Difficulty ⭐):写一个除法函数,当除数为 0 时抛出 std::runtime_error,在 main 中用 try/catch 捕获并输出错误信息。

  2. **进阶题 (Difficulty ⭐⭐):自定义一个异常类(继承 std::exception),包含错误码和错误描述两个字段,throw 后在 catch 中获取。

  3. **挑战题 (Difficulty ⭐⭐⭐):实现一个"资源守卫"类,构造函数获取资源,析构函数释放资源。即使中间抛出异常,资源也能正确释放(RAII 原理)。



下一课:文件操作进阶(#41)

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏