通常,按值(而不是按指针)但由(常量)引用捕获可认为是一种好习惯。
try { // throw new std::runtime_error("Error!"); // 不要这样! // 这将创建一个异常对象 // 在堆上,将需要您抓住 //指针并自己管理内存。这个可以 // 导致内存泄漏! throw std::runtime_error("Error!"); } catch (const std::runtime_error& e) { std::cout << e.what() << std::endl; }
引用捕获是一种好习惯的原因之一是,它消除了在传递到捕获块(或传播到其他捕获块)时重建对象的需求。通过引用捕获还可以对异常进行多态处理,并避免对象切片。但是,如果要抛出异常(例如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 << "第一个捕获块: " << e.what() << std::endl; // Output ==> 第一个捕获块: DerivedException throw e; // 这会将异常更改为BaseException // 而不是原始的DerivedException! } } catch (const BaseException& e) { std::cout << "第二个捕获块: " << e.what() << std::endl; // Output ==> 第二个捕获块: BaseException } return 0; }
如果您确定不做任何更改异常的操作(例如添加信息或修改消息),则按const引用进行捕获可以使编译器进行优化并提高性能。但这仍可能导致对象拼接(如上例所示)。
警告:当心不要在catch块中引发意外的异常,尤其是与分配额外的内存或资源有关的异常。例如,由于在复制异常字符串时内存耗尽,可能会构造logic_error,runtime_error或它们的子类抛出bad_alloc异常,在设置了各自的异常掩码的日志记录期间可能会抛出I / O流,等等。