C++中throw表达式应优先抛出继承自std::exception的类对象,因其支持多态和丰富错误信息;通过try-catch按引用捕获异常,遵循具体到通用的顺序,并利用RAII确保资源安全,虽异常抛出时有性能开销,但无异常时不影响性能。

在C++中,
throw
throw
catch
throw
throw expression;
expression
std::exception
当你执行
throw
catch
catch
例如,一个最简单的
throw
立即学习“C++免费学习笔记(深入)”;
#include <iostream>
#include <stdexcept> // 包含std::runtime_error
void mightFail(int value) {
if (value < 0) {
// 抛出一个std::runtime_error对象,携带错误信息
throw std::runtime_error("输入值不能为负数!");
}
std::cout << "处理值: " << value << std::endl;
}
int main() {
try {
mightFail(10);
mightFail(-5); // 这里会抛出异常
mightFail(20); // 这行代码将不会被执行
} catch (const std::runtime_error& e) {
// 捕获std::runtime_error及其派生类
std::cerr << "捕获到运行时错误: " << e.what() << std::endl;
} catch (...) {
// 捕获所有其他类型的异常(通用捕获)
std::cerr << "捕获到未知异常。" << std::endl;
}
std::cout << "程序继续执行。" << std::endl;
return 0;
}在这个例子中,当
mightFail(-5)
throw std::runtime_error(...)
mightFail
main
catch (const std::runtime_error& e)
在C++中,选择
throw
std::exception
为什么是对象?很简单,对象能承载更多信息。一个
throw "Error!"
std::runtime_error
而选择
std::exception
std::exception
what()
catch
std::exception
e.what()
std::runtime_error
std::logic_error
MyCustomError
当然,如果你需要更具体的信息,可以自定义异常类:
#include <string>
#include <stdexcept>
class FileOperationError : public std::runtime_error {
public:
std::string filename;
int errorCode;
FileOperationError(const std::string& msg, const std::string& file, int code)
: std::runtime_error(msg), filename(file), errorCode(code) {}
// 可以选择覆盖what()方法,提供更详细的描述
const char* what() const noexcept override {
// 这是一个简化版本,实际可能需要更复杂的字符串拼接
// 但这里我们展示如何利用基类的what()并添加额外信息
static std::string fullMsg;
fullMsg = std::runtime_error::what();
fullMsg += " (File: " + filename + ", Code: " + std::to_string(errorCode) + ")";
return fullMsg.c_str();
}
};
void readFile(const std::string& path) {
// 模拟文件不存在的错误
if (path == "non_existent.txt") {
throw FileOperationError("文件无法打开", path, 404);
}
// 正常处理文件...
std::cout << "文件 " << path << " 已成功读取。" << std::endl;
}
int main() {
try {
readFile("data.txt");
readFile("non_existent.txt");
} catch (const FileOperationError& e) {
std::cerr << "文件操作错误: " << e.what() << std::endl;
std::cerr << "文件名: " << e.filename << ", 错误码: " << e.errorCode << std::endl;
} catch (const std::exception& e) {
std::cerr << "捕获到标准异常: " << e.what() << std::endl;
}
return 0;
}这样,你的
catch
捕获
throw
try-catch
首先,
catch
&
const &
catch (ExceptionType e)
FileOperationError
catch
std::exception
catch (const ExceptionType& e)
其次,
catch
catch
catch (const std::exception& e)
catch (const FileOperationError& e)
FileOperationError
std::exception
// 错误的catch顺序示例
try {
// ... 可能会抛出 FileOperationError
} catch (const std::exception& e) { // 会先捕获所有std::exception及其派生类
std::cerr << "通用错误: " << e.what() << std::endl;
} catch (const FileOperationError& e) { // 永远不会被执行到
std::cerr << "文件操作错误: " << e.what() << std::endl;
}正确的顺序应该是:
try {
// ... 可能会抛出 FileOperationError
} catch (const FileOperationError& e) { // 先捕获最具体的
std::cerr << "文件操作错误: " << e.what() << std::endl;
// 这里可以访问 e.filename, e.errorCode 等具体信息
} catch (const std::runtime_error& e) { // 其次捕获稍微通用一些的运行时错误
std::cerr << "运行时错误: " << e.what() << std::endl;
} catch (const std::exception& e) { // 最后捕获所有标准异常
std::cerr << "标准异常: " << e.what() << std::endl;
} catch (...) { // 终极捕获,处理所有未知异常
std::cerr << "未知异常被捕获。" << std::endl;
// 通常用于日志记录或程序终止前的清理
}catch (...)
最后,一个重要的概念是重新抛出(rethrowing)。在
catch
throw;
throw;
C++的异常处理机制,在我看来,是一把双刃剑。它在提升代码健壮性和可读性方面有巨大潜力,但如果使用不当,也可能对性能和资源管理造成困扰。
性能方面: 很多人对C++异常处理的性能有所顾虑,认为它会带来显著开销。但现代C++编译器,特别是GCC和Clang,在实现异常处理时,大多采用了所谓的“零成本异常”(zero-cost exceptions)模型。这意味着:
try-catch
catch
catch
所以,我的观点是,异常应该用于真正的异常情况,即那些不经常发生、且无法通过正常逻辑处理的错误。它不应该被滥用作常规的控制流机制。例如,用异常来表示用户输入不合法,这通常是不明智的,因为用户输入不合法可能很常见,而更适合的方案是返回一个错误码或者布尔值。
资源管理方面: 这是异常处理机制真正闪光的地方,也是C++独有的强大特性——RAII(Resource Acquisition Is Initialization,资源获取即初始化)。 当一个异常被抛出并开始栈展开时,它会跳过正常的函数返回路径。如果函数内部在分配了资源(如内存、文件句柄、锁)后,没有及时释放就发生了异常,这些资源就会泄漏。 RAII正是为了解决这个问题而生。它的核心思想是:
这意味着,无论函数是正常返回还是因为异常而退出,所有在栈上创建的局部对象都会被正确地销毁,从而保证它们所管理的资源得到释放。 例如,
std::unique_ptr
std::shared_ptr
std::lock_guard
std::unique_lock
std::fstream
#include <iostream>
#include <fstream>
#include <memory> // for std::unique_ptr
#include <mutex> // for std::lock_guard
std::mutex global_mutex;
void processData(const std::string& filename) {
// 使用RAII管理文件句柄
std::ifstream file(filename);
if (!file.is_open()) {
throw std::runtime_error("无法打开文件: " + filename);
}
// 使用RAII管理互斥锁
std::lock_guard<std::mutex> lock(global_mutex); // 锁在lock_guard构造时获取,析构时释放
// 模拟一些可能抛出异常的操作
std::unique_ptr<int> data = std::make_unique<int>(42); // 内存由unique_ptr管理
if (filename == "error.txt") {
throw std::logic_error("模拟处理错误"); // 抛出异常
}
std::cout << "成功处理文件: " << filename << std::endl;
// 文件、锁、内存都会在函数结束或异常发生时自动释放
}
int main() {
try {
processData("data.txt");
processData("error.txt"); // 这里会抛出异常
} catch (const std::exception& e) {
std::cerr << "捕获到异常: " << e.what() << std::endl;
}
std::cout << "程序继续执行,资源已妥善管理。" << std::endl;
return 0;
}在这个例子中,即使
processData
std::ifstream
std::lock_guard
std::unique_ptr
所以,尽管异常在被抛出时有性能成本,但其在复杂错误处理逻辑中的清晰性,以及与RAII机制结合后对资源管理的强大保障,使得它在许多场景下成为不可替代的工具。关键在于理解其工作原理,并明智地运用。
以上就是C++如何使用throw表达式传递异常的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号