通过 const 引用按值捕获的最佳实践
一般来说,按值(而不是通过指针)抛出是一种好的做法,但是通过(const)引用来捕获。
try {
// throw new std::runtime_error("Error!"); // Don't do this!
// This creates an exception object
// on the heap and would require you to catch the
// pointer and manage the memory yourself. This can
// cause memory leaks!
throw std::runtime_error("Error!");
} catch (const std::runtime_error& e) {
std::cout << e.what() << std::endl;
}
通过引用捕获是一个好的做法的一个原因是它消除了在传递给 catch 块时(或当传播到其他 catch 块时)重构对象的需要。通过引用捕获还允许以多态方式处理异常并避免对象切片。但是,如果要重新抛出异常(如 throw e;
,请参见下面的示例),你仍然可以获得对象切片,因为 throw e;
语句会根据声明的类型生成异常的副本:
#include <iostream>
struct BaseException {
virtual const char* what() const { return "BaseException"; }
};
struct DerivedException : BaseException {
// "virtual" keyword is optional here
virtual const char* what() const { return "DerivedException"; }
};
int main(int argc, char** argv) {
try {
try {
throw DerivedException();
} catch (const BaseException& e) {
std::cout << "First catch block: " << e.what() << std::endl;
// Output ==> First catch block: DerivedException
throw e; // This changes the exception to BaseException
// instead of the original DerivedException!
}
} catch (const BaseException& e) {
std::cout << "Second catch block: " << e.what() << std::endl;
// Output ==> Second catch block: BaseException
}
return 0;
}
如果你确定不打算更改异常(例如添加信息或修改消息),那么使用 const 引用可以使编译器进行优化并提高性能。但这仍然会导致对象拼接(如上例所示)。
警告: 小心在 catch
块中抛出意外异常,尤其是与分配额外内存或资源有关。例如,构造 logic_error
,runtime_error
或它们的子类可能会因复制异常字符串时内存耗尽而丢失 bad_alloc
,I / O 流可能会在日志记录期间抛出相应的异常掩码集等。