
异常机制是一种重要的错误处理方法,可以帮助程序在运行时检测并处理问题,从而提高程序的可靠性和可维护性。C++异常机制的核心思想是:将错误检测和错误处理分离,从而让程序结构更清晰。
throw)信号,将错误信息传递给能够处理它的另一个部分。在C++中,异常的抛出和捕获分为以下几个步骤:
throw)
当程序遇到错误或特殊情况时,使用throw关键字抛出异常对象:if (b == 0) {
string s("Divide by zero condition!");
throw s;
}std::string)或用户自定义类型。throw之后的代码不会被执行。catch)
捕获异常通过try-catch块完成:try {
cout << Divide(len, time) << endl;
} catch (const string& errmsg) {
cout << errmsg << endl;
}try块包裹可能发生异常的代码。catch子句处理捕获的异常,参数类型必须匹配抛出异常的类型。catch (const std::exception& e)放在通用的catch (...)之前。try块中没有匹配的catch子句,异常会沿调用链向上传递。catch,程序会调用std::terminate()终止。栈展开是C++异常机制的核心,它描述了异常从抛出到被捕获的整个传播过程。
catch块。throw语句所在函数是否有try-catch,如果没有或类型不匹配,退出当前函数。catch块或到达main函数。main函数仍未找到匹配的catch块,程序会终止。
double Divide(int a, int b) {
if (b == 0) {
string s("Divide by zero condition!");
throw s;
}
return (double)a / (double)b;
}
void Func() {
int len, time;
cin >> len >> time;
try {
cout << Divide(len, time) << endl;
} catch (const string& errmsg) {
cout << "Caught exception: " << errmsg << endl;
}
}
int main() {
try {
Func();
} catch (const string& errmsg) {
cout << "Unhandled exception: " << errmsg << endl;
}
return 0;
}catch (...))。std::shared_ptr、std::unique_ptr)。在C++的异常处理机制中,当程序抛出一个异常对象时,系统会按照一定规则查找与该对象类型匹配的catch代码块,并执行相应的异常处理逻辑。
catch的形参类型完全匹配时,会优先选中该catch子句。例如:try {
throw std::string("Error occurred");
} catch (const std::string& err) {
std::cout << "String exception: " << err << std::endl;
}catch块,C++允许以下类型转换来匹配: const类型转换为const类型。class Exception {
public:
Exception(const std::string& errmsg) : _errmsg(errmsg) {}
virtual std::string what() const { return _errmsg; }
private:
std::string _errmsg;
};
class SqlException : public Exception {
public:
SqlException(const std::string& errmsg) : Exception(errmsg) {}
};
try {
throw SqlException("SQL Error");
} catch (const Exception& e) { // 基类捕获派生类异常
std::cout << e.what() << std::endl;
}catch (...)捕获所有类型的异常。这种方式一般用于处理未知异常:try {
throw 42;
} catch (...) {
std::cout << "Unknown exception caught!" << std::endl;
}main函数仍未被捕获,程序会调用std::terminate()函数终止程序。为了避免程序非预期终止,可以在main中使用catch (...)捕获所有未匹配的异常。在某些情况下,捕获到一个异常后,需要将其重新抛出,供调用链上的其他部分继续处理。
throw;)
在catch块中,使用不带参数的throw关键字可以重新抛出当前捕获的异常。例如:try
{
throw std::runtime_error("Error occurred");
}
catch (const std::exception& e) {
std::cout << "Caught: " << e.what() << ", rethrowing..." << std::endl;
throw; // 重新抛出异常
}catch块。例如:void InnerFunc() {
throw std::runtime_error("Inner exception");
}
void OuterFunc() {
try {
InnerFunc();
} catch (...) {
std::cout << "Logging exception in OuterFunc" << std::endl;
throw; // 重新抛出异常
}
}
int main() {
try {
OuterFunc();
} catch (const std::exception& e) {
std::cout << "Caught in main: " << e.what() << std::endl;
}
}输出结果:
Logging exception in OuterFunc
Caught in main: Inner exceptioncatch块中捕获异常后,抛出另一个异常对象:try {
throw std::runtime_error("Original Error");
} catch (const std::exception& e) {
throw std::logic_error("New Error"); // 抛出新的异常
}以下示例展示了如何在复杂项目中使用异常处理、基类匹配以及重新抛出异常。
class Exception {
public:
Exception(const std::string& errmsg) : _errmsg(errmsg) {}
virtual std::string what() const { return _errmsg; }
private:
std::string _errmsg;
};
class SqlException : public Exception {
public:
SqlException(const std::string& errmsg) : Exception(errmsg) {}
};
void SQLMgr() {
throw SqlException("SQL Error");
}
void CacheMgr() {
try {
SQLMgr();
} catch (const Exception& e) {
std::cout << "Caught in CacheMgr: " << e.what() << std::endl;
throw; // 重新抛出
}
}
int main() {
try {
CacheMgr();
} catch (const Exception& e) {
std::cout << "Caught in main: " << e.what() << std::endl;
}
return 0;
}运行结果:
Caught in CacheMgr: SQL Error
Caught in main: SQL Errorcatch (...))以处理未知异常。throw;重新抛出当前异常。通过合理运用异常匹配与重新抛出,能够让程序在复杂情况下保持健壮性和可维护性。
在C++中,异常规范是描述函数是否可能抛出异常,以及可能抛出哪些类型的异常。随着C++标准的演变,异常规范的用法发生了一些变化,从C++98的throw()到C++11及之后的noexcept,逐步变得简化和实用。
throw()或throw(类型列表),用于说明函数可能抛出异常的情况:void func1() throw(); // 表示函数不会抛出任何异常
void func2() throw(std::bad_alloc); // 表示函数可能抛出std::bad_alloc异常
void func3() throw(int, char); // 表示函数可能抛出int或char类型的异常throw()(不会抛出异常),但实际抛出了异常,程序仍可能崩溃。throw(类型),在现代开发中也很少被使用。noexcept)为解决C++98中异常规范的不足,C++11引入了noexcept,替代throw(),并提供更强大的功能和简单的语法。
noexcept基本语法 noexcept表示函数不会抛出任何异常:void func1() noexcept; // 保证函数不会抛出异常
void func2(); // 未声明noexcept,可能抛出异常- 如果`noexcept`函数实际抛出了异常,程序会调用`std::terminate()`终止执行,而不会进行异常传播。noexcept(expression)示例:
int i = 0;
std::cout << noexcept(++i) << std::endl; // 输出1(不会抛异常)
std::cout << noexcept(throw "Error!"); // 输出0(会抛异常)- `noexcept`还可以作为**运算符**,用于判断表达式是否可能抛出异常:noexcept(expression) * 如果`expression`在编译期确定不会抛出异常,`noexcept(expression)`返回`true`。
* 如果可能抛出异常,返回`false`。noexcept取代了throw(),现代C++中几乎不再使用throw()。noexcept是更强的约束,声明为noexcept的函数如果抛出异常,程序直接终止。noexcept比C++98的throw(类型)更简洁,无需列出具体类型。noexcept修饰的函数是否实际可能抛出异常。noexcept函数实际抛出了异常,直接调用std::terminate()。noexcept的场景与注意事项noexcept
标准库中的许多函数使用了noexcept修饰。例如:size_t size() const noexcept; // 容器的size()函数不会抛出异常
iterator begin() noexcept; // begin()函数也不会抛出异常noexcept,这可以帮助编译器优化代码。double Divide(int a, int b) noexcept {
if (b == 0) {
throw "Division by zero condition!"; // 会导致std::terminate()
}
return (double)a / (double)b;
}noexcept函数抛出异常,程序终止执行,且不会传播异常。noexcept,只有在可以完全保证不抛出异常时才使用。noexcept函数进行优化,因为可以假设它们不会抛出异常。std::vector的移动构造),如果被移动的对象的移动操作声明为noexcept,容器可以更高效地移动对象。以下代码展示了noexcept的使用,以及noexcept(expression)运算符的行为:
#include <iostream>
#include <stdexcept>
int SafeDivide(int a, int b) noexcept {
if (b == 0) {
throw "Division by zero!"; // noexcept函数抛异常会终止程序
}
return a / b;
}
int PotentialThrow(int x) {
if (x < 0) throw std::runtime_error("Negative value!");
return x;
}
int main() {
try {
std::cout << "SafeDivide: " << SafeDivide(10, 0) << std::endl;
} catch (...) {
std::cout << "Caught exception in SafeDivide!" << std::endl;
}
std::cout << "noexcept(SafeDivide(10, 2)): "
<< noexcept(SafeDivide(10, 2)) << std::endl; // 输出1(静态分析)
std::cout << "noexcept(PotentialThrow(10)): "
<< noexcept(PotentialThrow(10)) << std::endl; // 输出0(可能抛异常)
return 0;
}运行结果:
Caught exception in SafeDivide!
noexcept(SafeDivide(10, 2)): 1
noexcept(PotentialThrow(10)): 0throw()): noexcept): noexcept进行优化,增强程序的性能。noexcept。noexcept,因为一旦函数抛出异常,程序会直接终止。noexcept(expression)进行静态分析,确保表达式的安全性。通过合理使用异常规范,可以提高代码的可读性和可靠性,同时优化程序性能。