异常的抛出方式主要包含显式抛出,一些库在运行时抛出以及一些未定义的行为可能直接导致程序崩溃的情况,下面将在这篇文章中分别进行说明:
1. 显式抛出 (throw
关键字)
这是我们说的「自己抛出的」异常。
cpp
#include <stdexcept>
#include <iostream>
void test() {
throw std::runtime_error("manual error");
}
int main() {
try {
test();
} catch (const std::exception& e) {
std::cout << "Caught: " << e.what() << '\n';
}
}
输出:
Caught: manual error
这种情况是程序员主动 用 throw
抛出异常。很多自定义异常类、手动检查逻辑都是这么做的。
2. 由库/运行时产生的异常
即使你没有显式使用 throw
,某些库函数或运行时也会在错误条件下自动抛出异常。
例 1:标准库越界访问抛出异常
cpp
#include <vector>
#include <iostream>
#include <stdexcept>
int main() {
std::vector<int> v = {1, 2, 3};
try {
int x = v.at(5); // at() 会检查索引范围,越界时抛出 std::out_of_range
} catch (const std::out_of_range& e) {
std::cout << "Caught: " << e.what() << '\n';
}
}
输出例子(取决于实现):
Caught: vector::_M_range_check: __n (which is 5) >= this->size() (which is 3)
注意 :
这里你没有写
throw
,但是v.at()
内部在发现越界条件时会自动执行throw std::out_of_range
。
例 2:new
分配失败抛出异常
cpp
#include <iostream>
#include <new> // std::bad_alloc
int main() {
try {
// 故意申请一个极大数量的内存
int* p = new int[1'000'000'000'000];
delete[] p;
} catch (const std::bad_alloc& e) {
std::cout << "Memory allocation failed: " << e.what() << '\n';
}
}
输出:
Memory allocation failed: std::bad_alloc
这是 运行时系统 在内存不足时自动抛出的
std::bad_alloc
。
例 3:某些运算也可能抛出异常
严格意义上 算术运算本身(如整数除以0)不会抛出标准 C++ 异常 ,而是导致未定义行为(多数平台直接程序崩溃)。
但如果你使用了一些安全数学类、智能数值库,它们可能会检查并主动 throw
。
3. try-catch 并不会「自动抛异常」
try
块只是捕捉 从其范围内抛出的异常,本身不会触发抛出。
是否会发生异常,要看:
- 代码中是否显式
throw
- 被调用的库函数或运算是否会自动抛异常(比如
vector::at
、new
、部分算法)
如果是普通的数组越界访问 arr[10]
(越过边界),这不会抛出标准异常,而是未定义行为:
cpp
int arr[3] = {1, 2, 3};
try {
int x = arr[10]; // 未定义行为,不会抛出异常
} catch (...) {
std::cout << "This won't catch anything\n";
}
要捕获越界,你必须使用会检查边界并抛异常的接口,比如 std::vector::at()
。
4. 总结
- 异常可以是手动
throw
出的,也可以是库或运行时自动抛出的。 try
/catch
不会制造异常,它只是异常捕获机制。- 常见自动抛出的场景:
std::vector::at()
越界 →std::out_of_range
new
分配失败 →std::bad_alloc
- 标准库里很多算法会在不满足前置条件时
throw
- 原生数组越界、整数除0 等并不会自动抛标准异常,而是未定义行为。