c++中通过try catch throw这三个关键字处理异常,我们为什么要处理异常呢,为什么要用专门的异常处理来处理异常呢,这是本文将要解决的几个问题,我们会一一解答疑惑,异常处理的主要作用是,让我们的程序更健壮,当遇到不可预测的问题时可以做出处理,而不是直接崩溃,当发送错误时可以让程序执行备选方案,如果不得不接受程序时可以提前做一些必要的工作,如将内存中的数据写入文件、关闭打开的文件、释放动态分配的内存空间等。
try
:定义可能抛出异常的代码块。catch
:捕获并处理特定类型的异常。throw
:抛出一个异常对象(可以是任意类型,但建议继承std::exception
)。
void func1(){
throw std::runtime_error("test exception");
}
int main() {
try {
std::cout << "Hello, world!" << std::endl;
func1();
std::cout << "Test end" << std::endl;
} catch(const std::exception& e) {
std::cerr << e.what() << '\n';
}
return 0;
}
// 输出
Hello, world!
test exception
上面的代码使用了异常处理,func1会抛出一个异常,异常被catch捕获后会输出异常信息,通过输出可以看到,在try块中如果出现异常,会直接跳转到catch中不会再执行后面的内容了,如果没有异常就不会执行catch块中的内存
下面的代码演示了异常处理的使用,注意它不是一个好代码,只是一个用于演示异常处理的代码,这个代码的作用是读取number.txt文件其中的数字,并用用户的输入作为被除数,进行除法计算
class DivisionByZeroException : public std::logic_error {
public:
DivisionByZeroException()
: std::logic_error("division by zero error") {}
};
void openFile(std::ifstream& file, const std::string& filename){
file.open(filename);
if (!file.is_open()) {
throw std::runtime_error("cannot open file: " + filename);
}
}
int readNumFile(std::ifstream& file, const std::string& filename){
openFile(file, filename);
int number;
if (!(file >> number)) {
throw std::runtime_error("read file error");
}
return number;
}
int main() {
std::string filename = "number.txt";
std::ifstream file;
double a = 0.0;
try {
int number = readNumFile(file, filename);
std::cout << "number in the file is: " << number << std::endl;
while (true){
std::cout << "import a double number: ";
if (!(std::cin >> a)) throw std::runtime_error("read input error");
if (a == 0.0) throw DivisionByZeroException();
std::cout << "result: " << number / a << std::endl;
}
file.close();
} catch (const DivisionByZeroException& e) {
std::cerr << "[import error] " << e.what() << std::endl;
file.close();
} catch (const std::exception& e) {
std::cerr << e.what() << '\n';
file.close();
}
return 0;
}
throw相较于传统的异常处理方式,优势在于可以直接将错误抛给捕获的地方,不用一层一层的传递,在当前代码中如果打开文件失败,我们使用throw可以直接回到main函数中进行处理,如果是传统处理方式,需要先返回的readNumFile函数再返回到main中,另一个好处是可以自定义错误类型,我们通过c++的错误类可以实现自己的错误类
通过catch的分类机制,可以实现精确的异常处理,catch会从头到为进行遍历,检测抛出的异常属于那个catch快,这里要注意这些错误类型是范围的,我们继承而来的错误类型是会被父类捕获的,如果父类的catch放在子类前面,那么子类就永远不会触发了
} catch (const std::exception& e) {
std::cerr << e.what() << '\n';
file.close();
} catch (const DivisionByZeroException& e) {
std::cerr << "[import error] " << e.what() << std::endl;
file.close();
}
// 这时候我们的编译器会提示
main.cpp: In function 'int main()':
main.cpp:103:7: warning: exception of type 'DivisionByZeroException' will be caught by earlier handler [-Wexceptions]
103 | } catch (const DivisionByZeroException& e) {
| ^~~~~
D:\Code\C++\CppStudy\main.cpp:101:7: note: for type 'std::exception'
101 | } catch(const std::exception& e) {
|
- 异常分类清晰
不同模块的异常通过类型区分,避免混用std::runtime_error
。 - 精准处理错误
根据异常类型定制恢复策略(如清理输入流、关闭资源)。 - 提升可维护性
新增错误类型时只需扩展异常类,无需修改已有捕获逻辑。 - 符合工程实践
通过继承std::exception
体系,兼容标准异常处理流程。