编写内存安全的 C++ 应用程序需要什么?

What does it take to write memory safe C++ applications?

是否可以创建编码标准或使用可以证明可以消除 C++ 中任何内存管理错误的库?

我正在考虑类似 Java 的东西,例如在 Java 应用程序中不可能有悬挂指针。

Is it possible to either create a coding standard or use of a library that can be proved to eliminate any memory management errors in C++?

是也不是。

即使您使用非常严格的标准,这样做也会将您限制在非常狭窄的 C++ 语言子集中。例如,Power of Ten (Rules for Developing Safety-Critical Code) 表示您应该完全禁用堆使用。然而,仅此一点并不能阻止您造成内存损坏。

我想如果这个问题有一个确切的答案,这个行业在几十年前就已经解决了,但我们现在...

我认为没有确定的方法可以确保您的代码完全安全,但是有最佳实践这将帮助您确保问题尽可能少。

这里有一些建议:

  • 如前所述,完全禁止使用堆 可能 可以帮助您摆脱所有内存管理问题,但它并不能完全解决问题,因为它不保存你来自例如。杂散指针写入。
  • 我建议您阅读关于 The Rule of Three, Five and Zero 的内容,其中解释了您需要注意的一些事项。
  • 与其自己管理内存,不如使用 shared_ptrunique_ptr 等智能指针。当然,如果您愿意,您仍然可以滥用这些。 (例如,如果你有循环引用,shared_ptr 将不会帮助你...)
  • 使用像 valgrind 这样的内存检查工具,它可以帮助您发现问题并验证您的代码是否 error-free。

即使您遵守任何编码标准或最佳实践,也可能并且将会发生错误。没有人能保证您会安全。但是,通过遵守这些建议,您可以最大限度地减少错误的可能性和影响。

Is it possible to either create a coding standard or use of a library that can be proved to eliminate any memory management errors in C++?

没有

但是Java也是如此。虽然Java在技术上不允许内存泄漏,如果你不注意的话,它在实践中确实有它们(和其他资源泄漏)。

一个在 Android 世界中特别广为人知的 class 典型例子是当 collection 侦听器实例随着程序运行时间的延长而不断增长时,因为侦听器忘记注销他们自己。当侦听器是某些 window 或视图 class 的实例时,这会很快导致 数百 MB 泄漏到 GUI 应用程序中,并保持对大图形的引用。由于所有内存仍然可用,垃圾 collection 无法清理它。

从技术上讲,您没有丢失指针(它仍在 collection 中)这一事实对您没有任何帮助。恰恰相反;这是泄漏的原因,因为它可以防止垃圾 collection.

与上述相同,虽然 Java 在技术上不允许悬挂指针,但类似的错误可能会导致您访问指向某些 window 或视图 object 的指针,这是仍在您的程序的有效内存区域中,但它应该不再存在并且不再可见。虽然指针访问本身不会导致任何崩溃或问题,但由于程序逻辑混乱,其他类型的错误或崩溃(如 NullPointerException)通常很快就会出现。


坏消息就这么多。

好消息是,如果您遵循简单的指南,这两种语言都可以让您减少memory-management问题。就 C++ 而言,这意味着:

  • 尽可能使用标准 collections(例如 std::vectorstd::set)。
  • 将动态分配作为您的第二选择。首选应该始终是创建本地 object.
  • 如果必须使用动态分配,请使用std::unique_ptr
  • 如果一切都失败了,考虑std::shared_ptr
  • 仅当您实施某些 low-level 容器 class 时才使用裸 new,因为现有的标准容器 class 是(例如 std::vectorstd::set) 不适用于您的用例。不过,这应该是极其罕见的情况。

还有Boehm garbage collector for C++,不过我个人没用过