C++通过多个catch块捕获不同异常类型,按从具体到通用的顺序匹配,确保精确处理;catch(...)用于捕获未知异常,适合作为最后防线进行日志记录或资源清理,避免程序崩溃。

C++捕获多个异常类型主要通过使用多个
catch
catch(...)
在C++中处理异常,特别是当你的代码可能抛出多种不同类型的错误时,我们需要一种机制来区分和响应这些错误。最直接也是最常用的方法,就是堆叠
catch
你可以为每一种你预期可能出现的异常类型编写一个
catch
#include <iostream>
#include <stdexcept> // 包含标准异常类,如std::runtime_error, std::logic_error
// 假设我们有自定义异常
class FileIOException : public std::runtime_error {
public:
FileIOException(const std::string& msg) : std::runtime_error(msg) {}
};
class NetworkException : public std::runtime_error {
public:
NetworkException(const std::string& msg) : std::runtime_error(msg) {}
};
void mightThrowDifferentExceptions(int type) {
if (type == 1) {
throw FileIOException("文件读取失败!");
} else if (type == 2) {
throw NetworkException("网络连接超时!");
} else if (type == 3) {
throw std::bad_alloc(); // 标准库的内存分配异常
} else if (type == 4) {
throw std::runtime_error("未知运行时错误!");
} else {
throw "一个C风格字符串异常"; // 尽量避免,但确实可能发生
}
}
int main() {
try {
mightThrowDifferentExceptions(2); // 尝试抛出网络异常
// mightThrowDifferentExceptions(1); // 尝试抛出文件异常
// mightThrowDifferentExceptions(3); // 尝试抛出内存异常
// mightThrowDifferentExceptions(4); // 尝试抛出运行时异常
// mightThrowDifferentExceptions(5); // 尝试抛出C风格字符串异常
} catch (const FileIOException& e) {
std::cerr << "捕获到文件IO异常: " << e.what() << std::endl;
// 这里可以进行文件相关的恢复操作
} catch (const NetworkException& e) {
std::cerr << "捕获到网络异常: " << e.what() << std::endl;
// 这里可以尝试重新连接或通知用户
} catch (const std::bad_alloc& e) {
std::cerr << "捕获到内存分配异常: " << e.what() << std::endl;
// 尝试释放一些内存,或者优雅地退出
} catch (const std::runtime_error& e) {
// 这个catch块会捕获所有继承自std::runtime_error的异常,
// 包括我们的FileIOException和NetworkException,如果它们没有被更早的catch块捕获
std::cerr << "捕获到通用运行时异常: " << e.what() << std::endl;
} catch (const std::exception& e) {
// 这个catch块会捕获所有继承自std::exception的异常
std::cerr << "捕获到标准库异常: " << e.what() << std::endl;
} catch (...) { // 捕获所有其他未知类型的异常
std::cerr << "捕获到未知异常类型!" << std::endl;
// 只能做一些通用的清理工作,无法获取异常的具体信息
}
std::cout << "程序继续执行..." << std::endl;
return 0;
}我个人在写代码时,更倾向于明确捕获已知异常,
catch(...)
立即学习“C++免费学习笔记(深入)”;
这是一个非常关键的问题,我见过不少新手在这里栽跟头,调试起来挺头疼的,因为异常可能被一个不那么精确的
catch
try
catch
catch
catch
catch
catch
这意味着,如果你有一个基类异常(例如
std::exception
std::runtime_error
FileIOException
catch
catch
catch
举个例子:
#include <iostream>
#include <stdexcept>
class DerivedException : public std::runtime_error {
public:
DerivedException(const std::string& msg) : std::runtime_error(msg) {}
};
void func() {
throw DerivedException("这是一个派生类异常");
}
int main() {
try {
func();
} catch (const std::runtime_error& e) { // 基类异常先捕获
std::cerr << "捕获到基类异常: " << e.what() << std::endl;
} catch (const DerivedException& e) { // 派生类异常永远不会被捕获到这里
std::cerr << "捕获到派生类异常: " << e.what() << std::endl;
}
return 0;
}在这个例子中,
DerivedException
std::runtime_error
catch (const std::runtime_error& e)
catch (const DerivedException& e)
DerivedException
catch
std::runtime_error
因此,记住这个原则:从最具体的异常类型到最通用的异常类型,这是编写多
catch
catch(...)
catch(...)
std::exception
catch(...)
正因如此,
catch(...)
以下是
catch(...)
记录日志并终止程序: 当你遇到一个完全未知的异常时,最安全的做法往往是记录下这个不可预知的错误,并尝试以一种受控的方式关闭程序,避免潜在的数据损坏或进一步的不可预测行为。
try {
// ... 可能抛出各种异常的代码
} catch (const std::exception& e) {
std::cerr << "已知标准异常: " << e.what() << std::endl;
// 尝试恢复或优雅退出
} catch (...) {
std::cerr << "捕获到未知异常,程序将终止。" << std::endl;
// 记录日志,例如到文件
// std::abort(); // 强制终止
exit(EXIT_FAILURE); // 优雅退出
}资源清理: 在某些情况下,即使你不知道异常类型,你也可能需要执行一些资源清理工作,例如关闭文件句柄、释放内存或解锁互斥量。
catch(...)
重新抛出(Rethrow)未知异常: C++11及更高版本引入了
std::current_exception
std::rethrow_exception
catch(...)
#include <exception> // for std::current_exception, std::rethrow_exception
void lowLevelFunc() {
throw "一个神秘的错误!"; // 抛出C风格字符串
}
void midLevelFunc() {
std::exception_ptr p; // 异常指针
try {
lowLevelFunc();
} catch (...) {
std::cerr << "中层函数捕获到未知异常,记录并重新抛出。" << std::endl;
p = std::current_exception(); // 获取当前异常的指针
}
if (p) {
std::rethrow_exception(p); // 重新抛出捕获到的异常
}
}
int main() {
try {
midLevelFunc();
} catch (const char* msg) {
std::cerr << "顶层函数捕获到字符串异常: " << msg << std::endl;
} catch (...) {
std::cerr << "顶层函数捕获到其他未知异常。" << std::endl;
}
return 0;
}这种方式允许你在中间层进行一些日志记录或局部清理,然后将异常“转发”给更了解如何处理它的上层代码。这比直接在
catch(...)
我个人觉得,设计一套好的自定义异常体系,比单纯依赖
std::exception
清晰的语义和意图: 标准库的异常(如
std::runtime_error
FileNotFoundException
DatabaseConnectionError
InvalidInputException
携带更多上下文信息: 标准异常通常只包含一个字符串消息。自定义异常可以添加额外的成员变量,来携带更丰富的上下文信息,这对于调试和错误恢复至关重要。例如,
FileNotFoundException
DatabaseConnectionError
InvalidInputException
#include <iostream>
#include <stdexcept>
#include <string>
class BaseApplicationException : public std::runtime_error {
public:
BaseApplicationException(const std::string& msg, int code = 0)
: std::runtime_error(msg), errorCode(code) {}
int getErrorCode() const { return errorCode; }
private:
int errorCode;
};
class FileOperationException : public BaseApplicationException {
public:
FileOperationException(const std::string& msg, const std::string& filename)
: BaseApplicationException(msg, 1001), fileName(filename) {}
const std::string& getFileName() const { return fileName; }
private:
std::string fileName;
};
class NetworkOperationException : public BaseApplicationException {
public:
NetworkOperationException(const std::string& msg, const std::string& host)
: BaseApplicationException(msg, 2001), hostName(host) {}
const std::string& getHostName() const { return hostName; }
private:
std::string hostName;
};
void processData(bool fileError, bool netError) {
if (fileError) {
throw FileOperationException("无法打开配置文件", "config.txt");
}
if (netError) {
throw NetworkOperationException("连接到服务器失败", "api.example.com");
}
std::cout << "数据处理成功。" << std::endl;
}
int main() {
try {
processData(true, false); // 模拟文件错误
// processData(false, true); // 模拟网络错误
} catch (const FileOperationException& e) {
std::cerr << "捕获到文件操作异常: " << e.what()
<< ", 文件名: " << e.getFileName()
<< ", 错误码: " << e.getErrorCode() << std::endl;
} catch (const NetworkOperationException& e) {
std::cerr << "捕获到网络操作异常: " << e.what()
<< ", 主机: " << e.getHostName()
<< ", 错误码: " << e.getErrorCode() << std::endl;
} catch (const BaseApplicationException& e) {
std::cerr << "捕获到通用应用异常: " << e.what()
<< ", 错误码: " << e.getErrorCode() << std::endl;
} catch (const std::exception& e) {
std::cerr << "捕获到标准异常: " << e.what() << std::endl;
}
return 0;
}构建异常层次结构: 通过继承,你可以创建一套异常类体系。例如,所有的文件相关异常可以继承自
FileBaseException
NetworkBaseException
ApplicationException
catch
catch
提高代码可维护性: 当系统变得复杂时,错误类型会越来越多。自定义异常和异常层次结构使得管理这些错误变得有序。当你需要添加新的错误类型时,只需创建新的异常类并将其集成到现有层次结构中,而无需修改大量的现有
catch
总而言之,自定义异常是C++中实现健壮、可读、可维护错误处理机制的基石。它们让错误不仅仅是一个“发生了什么”的模糊消息,而是带有丰富上下文和明确语义的、可操作的事件。
以上就是C++如何捕获多个异常类型的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号