`throw std::runtime_error("链接数据库失败.");`
技术学习笔记:C++标准库异常类
1. C++异常处理机制概述
C++的异常处理机制允许程序在遇到错误时,能够以一种结构化的方式进行错误处理。异常处理包括三个主要部分:抛出(throw)、捕获(catch)和处理(handle)。
2. 标准异常类std::exception
所有标准异常类都继承自std::exception
,它提供了一个虚函数what()
,返回一个C风格字符串,描述了异常的原因。
示例代码:
cpp
try {
throw std::exception("基础异常");
} catch (const std::exception& e) {
std::cerr << "异常信息: " << e.what() << std::endl;
}
3. 运行时异常std::runtime_error
用于表示程序运行时遇到的意外情况,如文件找不到、内存不足等。
示例代码:
cpp
try {
throw std::runtime_error("运行时错误:文件未找到");
} catch (const std::runtime_error& e) {
std::cerr << "运行时错误: " << e.what() << std::endl;
}
4. 逻辑异常std::logic_error
用于表示程序内部逻辑错误,比如违反了程序的预设条件。
示例代码:
cpp
try {
throw std::logic_error("逻辑错误:无效操作");
} catch (const std::logic_error& e) {
std::cerr << "逻辑错误: " << e.what() << std::endl;
}
5. 其他标准异常类
以下是C++标准库中提到的异常类的具体使用示例:
5.1 std::bad_exception
std::bad_exception
通常不是直接抛出的,而是当标准异常处理机制遇到问题时由编译器抛出。
示例代码:
cpp
try {
throw; // 重新抛出当前异常
} catch (...) {
throw std::bad_exception(); // 抛出std::bad_exception
}
5.2 std::bad_alloc
当内存分配失败时,例如使用new
操作符时,会抛出此异常。
示例代码:
cpp
try {
throw std::bad_alloc(); // 模拟内存分配失败
} catch (const std::bad_alloc& e) {
std::cerr << "内存分配失败: " << e.what() << std::endl;
}
5.3 std::bad_cast
当使用dynamic_cast
进行向下转型失败时,会抛出此异常。
示例代码:
cpp
class Base {
public:
virtual ~Base() {}
};
class Derived : public Base {
};
try {
Base* base = new Derived();
Derived* derived = dynamic_cast<Derived*>(base);
if (!derived) {
throw std::bad_cast(); // 模拟类型转换失败
}
delete base;
} catch (const std::bad_cast& e) {
std::cerr << "类型转换失败: " << e.what() << std::endl;
}
5.4 std::bad_typeid
当使用typeid
操作符并且操作失败时,会抛出此异常。这种情况很少发生,因为typeid
操作符通常不会失败。
示例代码:
cpp
try {
// 模拟typeid操作失败的情况
throw std::bad_typeid();
} catch (const std::bad_typeid& e) {
std::cerr << "typeid操作失败: " << e.what() << std::endl;
}
5.5 std::domain_error
当函数的参数值不在有效域内时,可以抛出此异常。
示例代码:
cpp
try {
double x = -1.0;
if (x < 0) {
throw std::domain_error("负数不在有效域内");
}
} catch (const std::domain_error& e) {
std::cerr << "域错误: " << e.what() << std::endl;
}
5.6 std::invalid_argument
当传递给函数的参数无效时,可以抛出此异常。
示例代码:
cpp
try {
std::string arg = "";
if (arg.empty()) {
throw std::invalid_argument("参数不能为空");
}
} catch (const std::invalid_argument& e) {
std::cerr << "无效参数: " << e.what() << std::endl;
}
5.7 std::length_error
当容器无法分配请求的长度时,会抛出此异常。
示例代码:
cpp
try {
std::vector<int> v(1000000000); // 尝试分配大量内存
} catch (const std::length_error& e) {
std::cerr << "长度错误: " << e.what() << std::endl;
}
5.8 std::out_of_range
当索引或迭代器超出容器的界限时,会抛出此异常。
示例代码:
cpp
try {
std::vector<int> v = {1, 2, 3};
v.at(5); // 尝试访问不存在的索引
} catch (const std::out_of_range& e) {
std::cerr << "索引越界: " << e.what() << std::endl;
}
5.9 std::overflow_error
当发生算术溢出时,可以抛出此异常。
示例代码:
cpp
try {
int a = std::numeric_limits<int>::max();
a++; // 导致溢出
} catch (const std::overflow_error& e) {
std::cerr << "溢出错误: " << e.what() << std::endl;
}
5.10 std::range_error
当数值范围错误时,可以抛出此异常。
示例代码:
cpp
try {
double sqrt_input = -1.0;
sqrt(sqrt_input); // 尝试计算负数的平方根
} catch (const std::range_error& e) {
std::cerr << "范围错误: " << e.what() << std::endl;
}
请注意,这些示例代码中的异常抛出是为了演示目的,实际使用中可能需要根据具体情况进行调整。
通用异常处理示例代码:
cpp
#include <stdexcept>
#include <new> // For std::bad_alloc
#include <typeinfo> // For std::bad_cast
try {
// 模拟不同类型的异常抛出
throw std::out_of_range("索引越界");
// ... 其他异常
} catch (const std::exception& e) {
std::cerr << "捕获到标准库异常: " << e.what() << std::endl;
}
6. 自定义异常类
除了使用标准库中的异常类,开发者也可以根据需要定义自己的异常类,以携带更多的错误上下文信息。
示例代码:
cpp
class MyException : public std::exception {
public:
const char* what() const throw() {
return "自定义异常";
}
};
try {
throw MyException();
} catch (const std::exception& e) {
std::cerr << "捕获到自定义异常: " << e.what() << std::endl;
}
7. 异常处理的最佳实践
- 异常应该用于处理预期之外的错误情况。
- 避免在频繁执行的代码路径中使用异常,因为这可能会影响性能。
- 尽量使用标准库异常类,但在需要时定义自定义异常类。
- 确保异常安全,即在异常发生时,程序的状态应该是一致的。
实际操练
以下是一个使用C++异常处理机制的完整示例程序。这个程序演示了如何定义一个自定义异常类,抛出和捕获不同类型的异常,并展示了异常处理的最佳实践。
cpp
#include <iostream>
#include <vector>
#include <stdexcept>
// 自定义异常类
class CustomException : public std::exception {
private:
std::string message;
public:
CustomException(const std::string& msg) : message(msg) {}
virtual const char* what() const throw() {
return message.c_str();
}
};
// 模拟一个可能抛出异常的函数
int divide(int numerator, int denominator) {
if (denominator == 0) {
throw CustomException("除数不能为零");
}
return numerator / denominator;
}
// 另一个可能抛出异常的函数
void processElement(const std::vector<int>& elements, size_t index) {
if (index >= elements.size()) {
throw std::out_of_range("索引超出范围");
}
std::cout << "处理元素: " << elements[index] << std::endl;
}
int main() {
try {
// 尝试除以零
std::cout << "尝试除以零的结果: " << divide(10, 0) << std::endl;
} catch (const CustomException& e) {
std::cerr << "捕获到自定义异常: " << e.what() << std::endl;
} catch (const std::exception& e) {
std::cerr << "捕获到标准异常: " << e.what() << std::endl;
}
try {
std::vector<int> elements = {1, 2, 3};
// 尝试访问不存在的元素
processElement(elements, 5);
} catch (const std::out_of_range& e) {
std::cerr << "捕获到索引越界异常: " << e.what() << std::endl;
} catch (const std::exception& e) {
std::cerr << "捕获到其他标准异常: " << e.what() << std::endl;
}
return 0;
}
程序说明:
- 自定义异常类 :
CustomException
继承自std::exception
,用于表示自定义的错误情况。 - 可能抛出异常的函数 :
divide
函数在尝试除以零时抛出CustomException
异常;processElement
函数在尝试访问数组越界时抛出std::out_of_range
异常。 - 异常捕获 :在
main
函数中,我们使用两个try-catch
块来捕获和处理这些异常。第一个try-catch
块捕获divide
函数可能抛出的异常,第二个try-catch
块捕获processElement
函数可能抛出的异常。 - 异常处理 :在
catch
块中,我们使用what()
方法来获取异常的描述信息,并将其输出到标准错误流。
这个示例程序演示了如何使用C++的异常处理机制来处理程序中的错误情况,包括自定义异常和标准库异常。通过这种方式,我们可以在不中断程序正常流程的情况下优雅地处理错误。
分享一个有趣的 学习链接