如何让一个C++函数不抛出异常

本文探讨了不同方式下C++异常处理的方法,包括如何在一个函数内管理多个可能抛出异常的子函数,以及如何精简代码结构。

1. 尝试一

如果一个fun()调用funA(),funA()可能会抛出异常,那么想让fun()不抛出异常,fun()就得catch funA()的所有异常,并且不要继续往上throw

#include <iostream>
using namespace std;

int funA(int a) {
	std::cout << "funA : " << a << std::endl;
	throw 1;
	//return a;
}

int fun(int b) {
	std::cout << "fun : " << b << std::endl;
	try {
		funA(b);
	} catch (...) {
		std::cout << "catched funA exception" << std::endl;
	}
	return b;
}

int main(int argc, char *argv[]) {
	int ret = fun(60);
	std::cout << "main : " << ret << std::endl;
	return 0;
}

输出:
fun : 60
funA : 60
catched funA exception
main : 60

很简单,fun中catch了funA的异常,打印出了日志。

2. 尝试二

上面fun函数中只调用了一个可能抛出异常的函数funA,要是要调用多个怎么办,最先想到的,应该是这样的。

#include <iostream>
using namespace std;

int funA(int a) {
	std::cout << "funA : " << a << std::endl;
	throw 1;
	//return a;
}

int funB(int a) {
	std::cout << "funB : " << a << std::endl;
	throw 1;
	//return a;
}

int funC(int a) {
	std::cout << "funC : " << a << std::endl;
	throw 1;
	//return a;
}

int fun(int b) {
	std::cout << "fun : " << b << std::endl;
	try {
		funA(b);
	} catch (...) {
		std::cout << "catched funA exception" << std::endl;
	}
	
	try {
		funB(b);
	} catch (...) {
		std::cout << "catched funB exception" << std::endl;
	}
	
	try {
		funC(b);
	} catch (...) {
		std::cout << "catched funC exception" << std::endl;
	}
	return b;
}

int main(int argc, char *argv[]) {
	int ret = fun(60);
	std::cout << "main : " << ret << std::endl;
	return 0;
}

输出:fun : 60
funA : 60
catched funA exception
funB : 60
catched funB exception
funC : 60
catched funC exception
main : 60

fun中catch了funA、funB和funC的异常,打印出了日志。但是代码量是不是多了,而且,那么多try…catch…,是不是有些喧宾夺主了?

3. 尝试三

用一个try…catch…区解决fun中的所有异常行不行,当然可以。

#include <iostream>
using namespace std;

int funA(int a) {
	std::cout << "funA : " << a << std::endl;
	throw 1;
	//return a;
}

int funB(int a) {
	std::cout << "funB : " << a << std::endl;
	throw 1;
	//return a;
}

int funC(int a) {
	std::cout << "funC : " << a << std::endl;
	throw 1;
	//return a;
}

int fun(int b) {
	std::cout << "fun : " << b << std::endl;
	try {
		funA(b);
		funB(b);
		funC(b);
	} catch (...) {
		std::cout << "catched exception" << std::endl;
	}
	return b;
}

int main(int argc, char *argv[]) {
	int ret = fun(60);
	std::cout << "main : " << ret << std::endl;
	return 0;
}

输出:
fun : 60
funA : 60
catched exception
main : 60

3个可能抛出异常的函数被一个try…catch…管理了,看着比上一个清晰了,但是一个try…catch…中处理多个业务,会导致try…catch…中代码特别长

4. 最终

#include <iostream>
using namespace std;

int funA(int a) {
	std::cout << "funA : " << a << std::endl;
	throw 1;
	//return a;
}

int funB(int a) {
	std::cout << "funB : " << a << std::endl;
	throw 1;
	//return a;
}

int funC(int a) {
	std::cout << "funC : " << a << std::endl;
	throw 1;
	//return a;
}

int fun(int b) try {
	std::cout << "fun : " << b << std::endl;
	funA(b);
	funB(b);
	funC(b);
	return b;
} catch (...) {
	std::cout << "catched exception" << std::endl;
	return b;
}

int main(int argc, char *argv[]) {
	int ret = fun(60);
	std::cout << "main : " << ret << std::endl;
	return 0;
}

输出:
fun : 60
funA : 60
catched exception
main : 60

try…catch…写在函数外,却对整个函数有作用!

C++ 中,函数可以通过 `throw` 关键字抛出异常,以通知调用者或异常处理程序程序中出现了错误或异常情况。异常处理机制主要由 `try`、`catch` 和 `throw` 三个关键字构成,它们共同作用来捕获和处理异常。 ### 抛出异常函数中,使用 `throw` 后跟一个表达式来抛出异常抛出的表达式可以是基本数据类型(如 `int`、`char`、`bool`)或自定义的异常类对象。例如: ```cpp void divide(int a, int b) { if (b == 0) { throw "Division by zero error"; // 抛出字符串类型的异常 } std::cout << "Result: " << a / b << std::endl; } ``` 在此例中,如果 `b == 0`,函数抛出一个字符串类型的异常,表示除以零的错误。这种机制允许函数在遇到可恢复的错误时,将控制权交给调用链中能够处理该异常的部分[^1]。 ### 捕获异常 为了捕获和处理异常,需要使用 `try` 和 `catch` 块。`try` 块包含可能抛出异常的代码,而 `catch` 块则用于捕获并处理异常。例如: ```cpp int main() { try { divide(10, 0); // 可能抛出异常 } catch (const char* msg) { std::cerr << "Caught exception: " << msg << std::endl; } return 0; } ``` 在 `try` 块中调用的 `divide` 函数如果抛出异常,`catch` 块将捕获该异常并执行相应的处理逻辑。`catch` 块可以捕获特定类型的异常,也可以使用 `...` 来捕获所有类型的异常: ```cpp catch (...) { std::cerr << "Unknown exception caught" << std::endl; } ``` ### 异常传播 如果一个函数没有在其内部捕获异常异常将沿着调用栈向上传播,直到找到匹配的 `catch` 块。如果没有找到匹配的 `catch` 块,程序将调用 `std::terminate()`,默认情况下会导致程序终止。这种机制确保了即使在深层嵌套的函数调用中出现异常,也能够得到适当的处理[^1]。 ### 构造函数与析构函数中的异常 在构造函数抛出异常时,需要注意资源管理问题。由于构造函数抛出异常意味着对象未能成功构造,因此其析构函数会被调用。此时,已经构造的成员对象将被自动销毁,但手动分配的资源(如动态内存)可能会被释放,从而导致内存泄漏。为了避免这种情况,可以使用 RAII(资源获取即初始化)模式来管理资源,确保资源在对象生命周期结束时自动释放[^3]。 析构函数中应尽量避免抛出异常,因为如果在析构过程中抛出异常,而该异常未被捕获,程序将调用 `std::terminate()`,导致程序终止。从 C++11 开始,析构函数默认被声明为 `noexcept`,以防止意外抛出异常[^4]。 ### 异常安全保证 在编写异常安全的代码时,应确保即使在抛出异常的情况下,程序的状态仍然保持一致。通常有三种级别的异常安全保证: - **基本保证**:程序状态保持一致,但某些对象可能处于可预测的状态。 - **强保证**:操作要么完全成功,要么改变程序状态。 - **无抛出保证**:操作抛出异常。 为了提供更强的异常安全保证,可以使用复制-交换技术或使用智能指针等资源管理工具来简化资源管理和异常处理逻辑。 ### 示例代码 以下是一个完整的示例,展示了如何在函数抛出异常并在 `main` 函数中捕获和处理异常: ```cpp #include <iostream> #include <stdexcept> void divide(int a, int b) { if (b == 0) { throw std::runtime_error("Division by zero error"); // 抛出标准异常 } std::cout << "Result: " << a / b << std::endl; } int main() { try { divide(10, 0); // 可能抛出异常 } catch (const std::exception& e) { std::cerr << "Caught exception: " << e.what() << std::endl; } return 0; } ``` 在此示例中,`divide` 函数抛出一个 `std::runtime_error` 类型的异常,`main` 函数中的 `catch` 块捕获并处理了该异常使用标准库提供的异常类可以提高代码的可读性和可维护性。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值