TL,博士;
异常处理会导致内存泄漏。您的 C++ 编译器足够聪明,可以意识到围绕异常处理的内存泄漏的可能性,并在必要时自动插入异常处理程序以防止内存泄漏。这意味着确实存在一个 try/catch 块,您可以在其中观察异常处理程序,除了它不处理任何内容并且在清理 C++ 对象后简单地重新抛出异常。
系统如何知道函数是否有异常处理程序,并区分没有 try/catch 的函数和有它的函数
系统不知道。事实上,系统不可能知道,所以它只是沿着异常处理程序链向下走,直到找到一个告诉系统“已处理异常”的处理程序。这意味着每当你看到这 3 条汇编指令时,总会有一个异常处理程序,我相信应该总是可以将异常处理程序反转为等效的 C++ try/catch 块。
但正如您所观察到的,并非所有异常处理程序都需要明确定义。您的 C++ 编译器会将它们插入到您的程序中,以便进行对象清理,因为异常处理会导致内存泄漏:
void i_leak_memory() {
void* memory = malloc(512);
throw std::exception();
// this line of code will never be executed
free(memory);
}
void i_handle_exceptions() {
try {
i_leak_memory();
}
catch(std::exception e) {
// exception handled
}
}
在上面的代码中,您会发现由于异常引起的中断,内存永远不会被释放。在异常发生的确切时刻,执行立即转移到 catch in i_handle_exceptions(),处理异常后,继续执行 in i_handle_exceptions()。i_leak_memory()异常发生后永远没有机会释放资源,因此我们最终会发生内存泄漏。
幸运的是,对于这种明显的内存泄漏,有一个简单的解决方案。只需替换i_leak_memory()为i_do_not_leak_memory():
void i_do_not_leak_memory() {
void* memory = 0;
try {
memory = malloc(512);
throw std:exception();
free(memory);
}
catch(std:exception e) {
free(memory);
throw;
}
}
通过简单地向函数添加一个 try/catch 块,我们可以给它最后一次在异常事件中释放资源的机会。catch 块不一定需要处理异常,但它允许我们包含防止内存泄漏的特殊逻辑。
使用 C++ 的 RAII 设计,对象清理的任务是 C++ 编译器的责任。使用时throw(),C++编译器需要在代码中(可能在多个函数中)注入特殊的逻辑,以防止发生内存泄漏。因此,在你的观察,到底发生了什么是你的C ++编译器替换i_leak_memory()用i_do_not_leak_memory(),因为如果没有,该计划将被泄漏的C ++对象时发生了异常。