C++异常处理基础:try-catch
较难27好的,我将为你扩展这篇关于C++异常处理的文章,让它更详细、更贴近中小学生的理解。
给 C++ 程序系上“安全带”——异常处理 try-catch 入门
想象一下,你正开开心心地走在放学的路上,突然被一块凸起的地砖绊了一下。如果你没做准备,很可能摔个“狗啃泥”,疼得哇哇哭。但如果提前系好了“虚拟安全带”——比如总是小心看路,或者身边有个朋友及时扶你一把——你就不会受伤,还能继续往前走。
在 C++ 程序里,也会遇到类似的“绊脚石”。比如:
- 除数为 0(数学上不允许)
- 访问不存在的数组元素(比如数组只有 5 个元素,你却想拿第 10 个)
- 打开不存在的文件
- 内存空间不够用
这些意外情况在编程里叫做 异常(exception)。如果不处理,程序就会“摔倒”——直接崩溃退出,就像你摔跤后走不动了。而 try-catch 就是给程序准备的“安全带”和“朋友的手”,能让程序在遇到错误时安全地“扶住”自己,继续运行下去。
怎么用?——三步走:try → throw → catch
- try:把可能出错的代码放进
try { }里面,表示“这里我要小心走,可能要摔”。 - throw:当我们判断出错误时,主动用
throw抛出一个“警告信号”(异常)。这就像你快要摔倒时大喊一声“哎呀,我要倒了!”。 - catch:
catch块跟在try后面,专门抓住throw抛出的信号,然后处理它,就像朋友听到你的喊声后伸手扶住你。
让我们看一个最简单的例子:除法运算,分母可能为零。
#include <iostream>
using namespace std;
int main() {
int a = 10, b = 0; // b是0,除法会出问题
try {
if (b == 0) {
throw "除数不能为0!"; // 主动抛出异常,像大喊一声“我要摔倒了”
}
int result = a / b; // 如果b不是0,这里会正常计算
cout << "结果是:" << result << endl;
}
catch (const char* msg) { // 捕获抛出的字符串类型异常
cout << "错误:" << msg << endl; // 扶住程序,输出友好提示
}
cout << "程序继续运行,没有崩溃!" << endl;
return 0;
}
运行这段代码,会输出:
错误:除数不能为0!
程序继续运行,没有崩溃!
如果 b 不是 0,比如改成 int b = 5;,那么就不会进入 throw 语句,程序会正常计算并输出结果,然后跳过 catch 块直接执行后面的代码。
关键点:throw 抛出的东西是什么类型,catch 后面就要用对应的类型来接住。上面例子抛出了一个字符串常量,类型是 const char*,所以 catch 也写 const char*。
不只是字符串——可以抛出各种类型
throw 后面可以跟任何类型的数据,比如整数、浮点数、甚至一个对象。你可以利用这一点,让 catch 根据不同的错误类型做出不同的反应。
比如,用整数代表错误代码:
int age = -5;
try {
if (age < 0) {
throw 1001; // 抛出一个整数,表示“年龄错误”代码
}
cout << "年龄:" << age << endl;
}
catch (int err_code) { // 捕获整数异常
cout << "错误代码:" << err_code << ",年龄不能为负数!" << endl;
}
再比如,抛出一个浮点数表示错误级别:
double score = 120.5;
try {
if (score > 100) {
throw 1.5; // 抛出一个 double 类型
}
}
catch (double level) {
cout << "分数异常,危险等级:" << level << endl;
}
一个 catch 不够?那就多来几个 catch
现实中的绊倒原因有很多种:可能是石头、可能是香蕉皮、也可能是没关好的井盖。程序也会遇到各种不同的异常,我们希望给每种异常不同的处理方法。
try 后面可以跟 多个 catch,每个 catch 捕获一种类型。捕获顺序很重要:越具体的类型要放在前面,越通用的类型放在后面(比如 ... 表示“任何其他异常”,要放在最后)。
看例子:
#include <iostream>
using namespace std;
int main() {
int choice;
cout << "请输入一个数字(1或2): ";
cin >> choice;
try {
if (choice == 1) {
throw 404; // 抛出一个整数
}
else if (choice == 2) {
throw "网络错误"; // 抛出一个字符串
}
else {
throw 3.14; // 抛出一个 double 类型
}
}
catch (int code) { // 第一个 catch,专门处理整数
cout << "整数错误,代码:" << code << endl;
}
catch (const char* msg) { // 第二个 catch,处理字符串
cout << "字符串错误:" << msg << endl;
}
catch (...) { // 第三个 catch,处理所有其他类型(三个点)
cout << "未知异常,请检查输入" << endl;
}
cout << "程序结束" << endl;
return 0;
}
注意:catch (...) 中的三个点是 C++ 的省略号语法,表示“匹配任何未匹配的异常类型”。它必须放在所有 catch 的最后,否则其他 catch 就没机会了。
新手常常犯的错误
-
忘记写
throw,或者throw写错了地方 有些同学以为只要在try里写代码,程序就会自动处理错误。不对!你必须自己检查条件,然后手动throw抛出异常。计算机不会自动知道“这个除数是0需要报错”,需要你用 if 判断并抛出。错误示例:
int a = 10, b = 0; try { int c = a / b; // 这里直接除,b=0会导致崩溃,但不会触发 catch } catch (...) { cout << "出错啦"; // 这个根本不会执行,因为没 throw }正确做法是在除法前先判断 b==0,然后 throw。
-
catch 的类型和 throw 的类型不匹配 比如你
throw "错误"(类型是const char*),却写了catch (int e),那么异常不会被捕获,程序依然会崩溃(除非有更上层的 catch 或没处理)。错误示例:
try { throw 42; // 整数 } catch (const char* s) { // 类型不匹配,抓不住 cout << s; }程序会直接终止(默认会调用
terminate函数),不会输出任何内容。 -
忘记在
catch中使用捕获到的异常变量 有时你只想处理异常,不需要具体信息,可以只写类型而不写变量名,比如catch (int)。但建议还是写变量名,方便调试时打印信息。 -
把
try-catch当作万能安全网,滥用try-catch用来处理预料中的“异常情况”,而不是所有代码错误。比如你写了一个语法错误(少写分号),那不是异常,是 bug,编译器会直接报错。不要用异常来掩盖程序逻辑的不合理。
一个完整的示例:模拟零花钱发放
想象你帮老师发零花钱,每个同学应该发 10 元。但有时老师给你的钱不够,或者名单里有人要发负数?我们用异常处理来应对。
#include <iostream>
#include <string>
using namespace std;
int main() {
int total_money = 30; // 老师给了30元
int student_count = 10; // 有10个同学,每人10元需要100元,不够!
string name = "小明";
try {
if (student_count <= 0) {
throw "学生人数不能为零或负数";
}
if (total_money < student_count * 10) {
throw 500; // 抛出错误代码500,表示“钱不够”
}
cout << name << "领到零花钱10元" << endl;
}
catch (const char* msg) {
cout << "错误:" << msg << endl;
}
catch (int code) {
if (code == 500) {
cout << "错误代码500:老师给的钱不够,总共只有"
<< total_money << "元!" << endl;
}
}
catch (...) {
cout << "发生了未预料的错误" << endl;
}
cout << "程序运行完毕,没有崩溃!" << endl;
return 0;
}
运行输出:
错误代码500:老师给的钱不够,总共只有30元!
程序运行完毕,没有崩溃!
相关知识点指引
- 异常类型:除了基本类型,还可以抛出自定义类对象(比如
class NotEnoughMoney {};),这样能携带更丰富的错误信息。 - 栈展开:当异常被抛出时,程序会沿着函数调用链向上寻找匹配的
catch,沿途的局部变量会被自动销毁,有点像“退路清理”。 - 标准异常类:C++ 标准库提供了一组异常类(如
std::runtime_error),你可以直接throw std::runtime_error("错误信息");,然后catch (std::exception& e)捕获。 - 异常安全:设计代码时,要保证即使抛出异常,资源也能正确释放(比如用智能指针或 RAII)。
现在你可以试着在自己的程序里用上 try-catch 这个“安全带”了。遇到错误时,程序不会慌张崩溃,而是会优雅地告诉你哪里出了问题,然后继续跑完后面的路。这样写出来的代码更可靠,也更不容易被用户骂“你的程序又崩了”!
加油,小程序员!
例题精讲
关于C++异常处理中try-catch语句,以下哪个语法是正确的?
一个try块可以对应多个catch块,用于捕获不同类型的异常。
在C++异常处理中,当try块内的代码抛出异常时,程序会跳转到对应的___块执行。关于catch(...)的含义,以下描述正确的是?
C++中,try块后面必须至少有一个___块,否则编译器会报错。