C++异常处理通过try-catch-throw实现,将错误处理与正常逻辑分离,避免资源泄露并提升代码可读性;结合RAII机制,在构造函数中获取资源、析构函数中释放,确保异常发生时能自动清理,保障程序状态安全。

C++的异常处理机制,尤其是我们常说的
try-catch
C++的
try-catch
try
throw
catch
try
try
#include <iostream>
#include <string>
#include <stdexcept> // 包含标准异常类
double divide(double numerator, double denominator) {
if (denominator == 0) {
// 当分母为0时,这是一个异常情况,我们选择抛出异常
throw std::runtime_error("Error: Division by zero is not allowed.");
}
return numerator / denominator;
}
int main() {
try {
// 尝试执行可能抛出异常的代码
double result1 = divide(10.0, 2.0);
std::cout << "10 / 2 = " << result1 << std::endl;
double result2 = divide(5.0, 0.0); // 这行代码会抛出异常
std::cout << "5 / 0 = " << result2 << std::endl; // 这行将不会被执行
}
catch (const std::runtime_error& e) {
// 捕获特定类型的异常
std::cerr << "Caught an exception: " << e.what() << std::endl;
}
catch (const std::exception& e) {
// 捕获所有标准异常的基类,更通用
std::cerr << "Caught a general standard exception: " << e.what() << std::endl;
}
catch (...) {
// 捕获任何类型的异常(包括非标准异常),通常作为最后的防线
std::cerr << "Caught an unknown exception!" << std::endl;
}
std::cout << "Program continues after exception handling." << std::endl;
return 0;
}当
divide(5.0, 0.0)
denominator == 0
throw std::runtime_error(...)
std::runtime_error
catch
catch
try
catch
catch
std::terminate
立即学习“C++免费学习笔记(深入)”;
catch
std::exception
...
说实话,以前我也习惯用错误码,函数返回个负数或者特定的枚举值来表示失败。这种方式看似直接,但用久了就会发现一堆问题,尤其是在大型项目或者深层函数调用链里。
首先,错误码很容易被“视而不见”。你调用一个函数,它返回个错误码,但如果你忘了检查,或者检查了却没做任何处理,程序就可能带着一个错误状态继续跑,直到在某个意想不到的地方彻底崩掉,那时候排查起来简直是噩梦。异常处理则不同,
throw
其次,错误码会污染你的正常业务逻辑代码。每一步操作后都得加一个
if (error_code != SUCCESS)
try
catch
再者,错误码在函数调用链中传递是个麻烦事。一个底层函数出错了,它的错误码要一层一层地往上传,每个中间函数都得负责接收、判断、再返回。这不仅增加了大量冗余代码,也使得错误信息的传递效率低下。异常则能自动地“跳过”中间层,直接传递到最近的、能处理该类型异常的
catch
最后,异常处理结合C++的RAII(Resource Acquisition Is Initialization)机制,在资源管理上有着天然的优势。当异常发生时,程序会进行“栈展开”(stack unwinding),这过程中,所有在栈上创建的对象都会被正确地析构。这意味着即使在错误发生时,像文件句柄、内存、锁等资源也能被自动释放,有效避免了资源泄露,这是错误码很难做到的。
这个问题挺关键的,用不好异常反倒会带来新的问题。我的经验是,异常应该用来处理那些真正“异常”的、非预期的、程序无法继续正常执行的情况。
适合使用异常的场景:
new
new
std::bad_alloc
std::out_of_range
std::invalid_argument
应该避免使用异常的场景:
nullptr
std::optional
std::expected
if-else
for
总的来说,异常是处理“意外事件”的,而不是“日常小插曲”。
编写异常安全的代码,目标是在异常发生时,程序的状态依然是有效的,并且所有已获取的资源都能被正确管理,不会发生泄露。这里有几个层次的“异常安全保证”:
要实现这些保证,尤其是避免资源泄露,RAII(Resource Acquisition Is Initialization)原则扮演着至关重要的角色。RAII的核心思想是:将资源的生命周期与对象的生命周期绑定。当对象被创建时(通常在构造函数中),它获取资源;当对象被销毁时(在析构函数中),它释放资源。
#include <iostream>
#include <fstream>
#include <memory> // for std::unique_ptr
#include <mutex> // for std::lock_guard
// 示例1: 传统的资源管理(容易泄露)
void process_file_old(const std::string& filename) {
std::FILE* file = std::fopen(filename.c_str(), "r");
if (!file) {
throw std::runtime_error("Could not open file.");
}
// 假设这里有一段代码可能会抛出异常
// 如果抛出异常,fclose(file) 将不会被执行,导致文件句柄泄露
// ...
std::fclose(file); // 如果前面有异常,这行代码可能永远不会执行
}
// 示例2: 使用RAII管理文件句柄
class FileHandle {
public:
FileHandle(const std::string& filename, const char* mode) {
file_ptr_ = std::fopen(filename.c_str(), mode);
if (!file_ptr_) {
throw std::runtime_error("Failed to open file: " + filename);
}
std::cout << "File '" << filename << "' opened." << std::endl;
}
~FileHandle() {
if (file_ptr_) {
std::fclose(file_ptr_);
std::cout << "File closed." << std::endl;
}
}
// 禁止拷贝,避免双重释放
FileHandle(const FileHandle&) = delete;
FileHandle& operator=(const FileHandle&) = delete;
std::FILE* get() const { return file_ptr_; }
private:
std::FILE* file_ptr_;
};
void process_file_raii(const std::string& filename) {
FileHandle file(filename, "r"); // 资源在构造时获取
// 假设这里有一段代码可能会抛出异常
// 无论是否抛出异常,当file对象离开作用域时,其析构函数都会被调用
// 从而保证文件句柄被正确关闭。
std::cout << "Processing file content..." << std::endl;
// ...
// file对象离开作用域,析构函数自动调用,文件关闭
}
// 示例3: 使用标准库的RAII工具,如std::lock_guard
std::mutex my_mutex;
void guarded_operation() {
std::lock_guard<std::mutex> lock(my_mutex); // 构造时加锁
// 临界区代码,可能抛出异常
std::cout << "Critical section entered." << std::endl;
// ...
// 无论如何,lock对象离开作用域时,析构函数会自动解锁
std::cout << "Critical section exited." << std::endl;
}
int main() {
try {
// process_file_old("non_existent.txt"); // 演示传统方式的风险
process_file_raii("example.txt"); // 假设example.txt存在
guarded_operation();
} catch (const std::exception& e) {
std::cerr << "Main caught exception: " << e.what() << std::endl;
}
return 0;
}在上面的
process_file_raii
FileHandle file(filename, "r");
file
std::fclose
std::unique_ptr
std::shared_ptr
std::lock_guard
std::unique_lock
编写异常安全代码时,还需要注意:
std::terminate
std::swap
noexcept
理解并应用RAII是构建健壮、异常安全的C++代码库的基石。它让资源管理变得自动化和可靠,大大减轻了程序员的心智负担。
以上就是c++++如何进行异常处理_c++异常处理try-catch机制详解的详细内容,更多请关注php中文网其它相关文章!
c++怎么学习?c++怎么入门?c++在哪学?c++怎么学才快?不用担心,这里为大家提供了c++速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号