C++ 异常处理
你好,有抱负的程序员们!今天,我们将踏上一段令人兴奋的旅程,进入C++异常处理的世界。作为你友好的邻居计算机科学老师,我将引导你了解这个重要的主题。所以,拿起你最喜欢的饮料,舒服地坐好,让我们开始吧!
什么是异常?
在我们像经验丰富的杂技演员一样抛出和捕获异常之前,让我们先了解它们是什么。在编程世界中,异常是在程序执行期间发生的意外事件。它们就像我曾经给的惊喜小测验(对此表示抱歉!)——出乎意料,有时处理起来也有些挑战。
异常会中断程序指令的正常流程。它们可能由以下各种因素引起:
- 除以零
- 访问数组越界
- 内存不足
- 尝试打开不存在的文件
现在,让我们看看C++如何允许我们优雅地管理这些意外情况。
抛出异常
抛出的基础
在C++中,我们使用throw
关键字来引发一个异常。这就像在课堂上举手一样,当你有问题或问题时。以下是一个简单的例子:
#include <iostream>
using namespace std;
int main() {
try {
throw 20;
}
catch (int e) {
cout << "发生了一个异常。异常编号 " << e << endl;
}
return 0;
}
在这个例子中,我们抛出一个值为20的整数异常。但别担心,我们马上就会捕获它!
抛出不同类型的异常
C++很灵活,允许你抛出各种类型的异常。让我们看一个更实用的例子:
#include <iostream>
#include <stdexcept>
using namespace std;
double divide(int a, int b) {
if (b == 0) {
throw runtime_error("除以零!");
}
return static_cast<double>(a) / b;
}
int main() {
try {
cout << divide(10, 2) << endl; // 这将正常工作
cout << divide(10, 0) << endl; // 这将抛出异常
}
catch (const runtime_error& e) {
cout << "捕获到异常: " << e.what() << endl;
}
return 0;
}
在这个例子中,当有人尝试除以零时,我们抛出一个runtime_error
异常。这就像在我们的数学社区中竖起一个“禁止除以零”的标志!
捕获异常
捕获的基础
现在我们知道如何抛出异常,接下来学习如何捕获它们。捕获异常就像是一个负责任的宠物主人——你需要准备好处理代码抛给你的任何东西!
我们使用try-catch
块来捕获异常。try
块包含可能抛出异常的代码,而catch
块在异常发生时处理它。
#include <iostream>
using namespace std;
int main() {
try {
int age = -5;
if (age < 0) {
throw "年龄不能为负!";
}
cout << "年龄是: " << age << endl;
}
catch (const char* msg) {
cerr << "错误: " << msg << endl;
}
return 0;
}
在这个例子中,我们检查年龄是否为负。如果是,我们抛出一个带有自定义错误消息的异常。
捕获多个异常
有时,同一块代码可能会抛出不同类型的异常。在这种情况下,我们可以有多个catch
块:
#include <iostream>
#include <stdexcept>
using namespace std;
int main() {
try {
int choice;
cout << "输入1表示整数异常,2表示运行时错误: ";
cin >> choice;
if (choice == 1) {
throw 42;
} else if (choice == 2) {
throw runtime_error("出现了一个野生的运行时错误!");
} else {
throw "未知的选择!";
}
}
catch (int e) {
cout << "捕获到整数异常: " << e << endl;
}
catch (const runtime_error& e) {
cout << "捕获到运行时错误: " << e.what() << endl;
}
catch (...) {
cout << "捕获到一个未知的异常!" << endl;
}
return 0;
}
这个例子展示了我们如何捕获不同类型的异常。catch (...)
块是一个捕获所有异常的块,它会处理之前catch
块没有捕获的任何异常。它就像是一个安全网,可以捕获所有意外的惊喜!
C++标准异常
C++附带了一套标准异常,你可以在程序中使用。这些就像是异常世界中的瑞士军刀——多功能且随时待命!
以下是一些常用的标准异常表:
异常 | 描述 |
---|---|
std::runtime_error | 运行时逻辑错误 |
std::logic_error | 逻辑错误 |
std::out_of_range | 超出范围访问 |
std::overflow_error | 算术溢出 |
std::bad_alloc | 内存分配失败 |
让我们看一个使用标准异常的例子:
#include <iostream>
#include <vector>
#include <stdexcept>
using namespace std;
int main() {
vector<int> numbers = {1, 2, 3, 4, 5};
try {
cout << numbers.at(10) << endl; // 这将抛出一个out_of_range异常
}
catch (const out_of_range& e) {
cerr << "范围错误: " << e.what() << endl;
}
return 0;
}
在这个例子中,我们尝试访问向量中超出范围的元素。当这种情况发生时,at()
函数会抛出一个out_of_range
异常。
定义新异常
虽然标准异常很好,但有时你需要更符合你特定需求的东西。这时自定义异常就派上用场了!
以下是如何定义你自己的异常类:
#include <iostream>
#include <exception>
using namespace std;
class NegativeValueException : public exception {
public:
const char* what() const throw() {
return "不允许负值!";
}
};
double squareRoot(double x) {
if (x < 0) {
throw NegativeValueException();
}
return sqrt(x);
}
int main() {
try {
cout << squareRoot(25) << endl; // 这将正常工作
cout << squareRoot(-5) << endl; // 这将抛出我们的自定义异常
}
catch (const NegativeValueException& e) {
cerr << "错误: " << e.what() << endl;
}
return 0;
}
在这个例子中,我们创建了一个自定义的NegativeValueException
类。我们在squareRoot
函数中使用它来抛出一个异常,当有人尝试计算负数的平方根时。
好了,各位!我们已经涵盖了C++异常处理的基础知识。请记住,异常是你的朋友。它们帮助你编写更健壮、更抗错误的代码。继续练习,很快你就能像专业人士一样处理异常了!
编程愉快,愿你的异常总能被捕获!
Credits: Image by storyset