如何从 C++ 中的动态分配中恢复内存泄漏?
How to recover memory leak from dynamic allocation in C++?
如果我写:
b = new int;
b = new int;
delete b;
我知道 delete b;
只会从内存中删除第二个 b
。由于此删除后没有指向第一个 b
的内容,因此存在内存泄漏。
在这种情况下,我知道有一种方法可以在 C# 和 Java 中恢复此泄漏。有没有办法在 C++ 中恢复此内存泄漏?
I know that there is a way to recover this memory leak in C# and Java
Java 和 C# 都是垃圾收集语言,所以它们不会经常发生内存泄漏(这几乎是不可能的),但是当它们发生时你可以抛出异常,
Is there a way to recover this memory leak in C++?
在c++中内存泄漏比较常见,因为你每次创建指针时都在手动分配内存,所以你总是会抛出异常来指出内存泄漏发生在哪里,发生在哪里以及如何发生处理它......所以你的问题的答案是......不,你无法从 C++ 中的内存泄漏中恢复,它是一种 "Down To The Metal" 语言,这就是为什么如果编写不正确它会如此脆弱,但你总是可以当确实发生内存泄漏时抛出异常,您可以查明它并查看如何修复它。
选项 1.
最简单的方法是完全避免 new
和 delete
。
而不是:
b = new int;
b = new int;
delete b;
写入:
std::unique_ptr<int> b;
...
b = std::make_unique<int>();
b = std::make_unique<int>();
当 b
被覆盖或超出范围时,它会释放内存。不需要甚至不允许使用 delete
。注意很像 Java 垃圾收集器,但大部分时间都足够好。
选项 2.
另一种方法是使用 add-on garbage collector,但该解决方案不可取。首先,垃圾收集器是保守的,这意味着尽管有垃圾收集器,仍有可能发生内存泄漏。其次,这些解决方案往往会使现有问题变得更糟,因为内存泄漏在引入时不会被注意到和处理。在引入一年后处理内存泄漏比在引入一小时后处理它们要困难几个数量级。
注意:使用 unique_ptr
不如在垃圾收集语言(如 C#、Java 和 Python 中安全。当没有明确的所有权模型并且存在循环所有权时,它会失败。如果元素 a
有一个 unique_ptr
到 b
并且 b
有一个 unique_ptr
到 a
那么它们将永远不会被释放。它们永远不会被释放,因为 unique_ptr
在析构函数中释放了对象,但是没有人会调用 a
和 b
的析构函数。如果没有人调用析构函数,那么 unique_ptr
将永远不会删除该对象,也永远不会调用另一个对象的析构函数。
NOTE 2:有时应该使用std::shared_ptr
代替unique_ptr
,但这并不能解决循环引用的问题。这仅解决了单个对象的多个所有者的问题。
注意 3:这些智能指针不会降低由于析构函数中的深度递归而导致堆栈溢出的可能性。这很可能发生在长链接列表或不平衡二叉树的递归破坏中。在这种情况下使用 unique_ptr
只是隐藏了正在发生递归的事实。
如果我写:
b = new int;
b = new int;
delete b;
我知道 delete b;
只会从内存中删除第二个 b
。由于此删除后没有指向第一个 b
的内容,因此存在内存泄漏。
在这种情况下,我知道有一种方法可以在 C# 和 Java 中恢复此泄漏。有没有办法在 C++ 中恢复此内存泄漏?
I know that there is a way to recover this memory leak in C# and Java
Java 和 C# 都是垃圾收集语言,所以它们不会经常发生内存泄漏(这几乎是不可能的),但是当它们发生时你可以抛出异常,
Is there a way to recover this memory leak in C++?
在c++中内存泄漏比较常见,因为你每次创建指针时都在手动分配内存,所以你总是会抛出异常来指出内存泄漏发生在哪里,发生在哪里以及如何发生处理它......所以你的问题的答案是......不,你无法从 C++ 中的内存泄漏中恢复,它是一种 "Down To The Metal" 语言,这就是为什么如果编写不正确它会如此脆弱,但你总是可以当确实发生内存泄漏时抛出异常,您可以查明它并查看如何修复它。
选项 1.
最简单的方法是完全避免 new
和 delete
。
而不是:
b = new int; b = new int; delete b;
写入:
std::unique_ptr<int> b;
...
b = std::make_unique<int>();
b = std::make_unique<int>();
当 b
被覆盖或超出范围时,它会释放内存。不需要甚至不允许使用 delete
。注意很像 Java 垃圾收集器,但大部分时间都足够好。
选项 2.
另一种方法是使用 add-on garbage collector,但该解决方案不可取。首先,垃圾收集器是保守的,这意味着尽管有垃圾收集器,仍有可能发生内存泄漏。其次,这些解决方案往往会使现有问题变得更糟,因为内存泄漏在引入时不会被注意到和处理。在引入一年后处理内存泄漏比在引入一小时后处理它们要困难几个数量级。
注意:使用 unique_ptr
不如在垃圾收集语言(如 C#、Java 和 Python 中安全。当没有明确的所有权模型并且存在循环所有权时,它会失败。如果元素 a
有一个 unique_ptr
到 b
并且 b
有一个 unique_ptr
到 a
那么它们将永远不会被释放。它们永远不会被释放,因为 unique_ptr
在析构函数中释放了对象,但是没有人会调用 a
和 b
的析构函数。如果没有人调用析构函数,那么 unique_ptr
将永远不会删除该对象,也永远不会调用另一个对象的析构函数。
NOTE 2:有时应该使用std::shared_ptr
代替unique_ptr
,但这并不能解决循环引用的问题。这仅解决了单个对象的多个所有者的问题。
注意 3:这些智能指针不会降低由于析构函数中的深度递归而导致堆栈溢出的可能性。这很可能发生在长链接列表或不平衡二叉树的递归破坏中。在这种情况下使用 unique_ptr
只是隐藏了正在发生递归的事实。