【问题标题】:C++ Memory managementC++ 内存管理
【发布时间】:2010-09-06 20:23:37
【问题描述】:

我在大学里了解到,你总是必须释放你未使用的对象,而不是你实际上是如何做的。例如,正确地构建您的代码等等。 是否有关于如何在 C++ 中处理指针的一般规则?

我目前不允许使用 boost。我必须坚持使用纯 C++,因为我使用的框架禁止使用任何泛型。

【问题讨论】:

  • 我必须坚持使用纯 C++,因为我使用的框架禁止使用任何泛型。 这个决定背后肯定有理由吗?我很想听听它,因为我想不出任何完全禁止模板的理由。
  • @KonradRudolph:对此的官方回答是:它们不能移植到不同的操作系统,尤其是编译器和链接编辑器支持它们的方式。跨度>
  • @AlexanderStolz:这是错误的。我的意思是,C++ 作为一个整体在不同的操作系统之间是不兼容的(大多数平台都有一个清晰且定义良好的 C ABI,而不是 C++,尤其是当你了解虚拟继承表示等细节时)。模板不会改变这方面的任何东西。实例化的模板只是具有有趣名称的类。编译器支持在几年前是一个紧迫的问题,但现在没有理由不使用 g++ 4.x 或 VC++ 2005/2008,这两种版本对于任何正常使用都绰绰有余。
  • 太糟糕了,这太旧了。我很想听听关于为什么你不能使用(特别是)泛型的解释。
  • 来自框架的文档:不要使用模板。它们不能移植到不同的操作系统,尤其是编译器和链接编辑器支持它们的方式。目前我只能说这些

标签: c++ memory pointers


【解决方案1】:

规则:

  1. 尽可能使用 smart pointer。 Boost有一些 good ones
  2. 如果你 不能使用智能指针,null out your pointer after deleting it
  3. 切勿在不允许您使用规则 1 的地方工作。

如果有人不允许规则 1,请记住,如果您获取其他人的代码、更改变量名称并删除版权声明,那么没人会注意到。除非这是一个学校项目,否则他们实际上会使用非常复杂的工具检查这种恶作剧。另请参阅this question

【讨论】:

  • -1 用于归零。我不会在这里讨论它,但这是不是的好习惯。如果代码写得相当好,那应该是没有必要的,因为在确定没有 other 指向您分配的内存块的引用/指针不再存在之前,您不应该删除。
【解决方案2】:
  • 当您必须使用管理内存时 手动,确保你调用 delete 在相同的 范围/功能/类/模块,其中 永远先申请,例如:
  • 让函数的调用者分配由它填充的内存, 不要返回新的指针。
  • 始终在调用 new 时在同一个 exe/dll 中调用 delete,否则您可能会遇到堆损坏问题(不同的不兼容运行时库)。

【讨论】:

    【解决方案3】:

    我使用过嵌入式 Symbian 操作系统,该操作系统为此配备了出色的系统,完全基于开发人员约定。

    1. 只有一个对象会拥有一个指针。默认情况下,这是创建者。
    2. 可以传递所有权。为了指示所有权的传递,对象在方法签名中作为指针传递(例如 void Foo(Bar *zonk);)。
    3. 所有者将决定何时删除该对象。
    4. 要将对象传递给仅用于使用的方法,该对象将作为方法签名中的引用传递(例如 void Foo(Bat &zonk);)。
    5. 非所有者类可以存储对对象的引用(绝不是指针),只有当它们可以确定所有者在使用过程中不会破坏它时,才能存储它们。

    基本上,如果一个类只是使用某些东西,它就会使用引用。如果一个类拥有某些东西,它会使用一个指针。

    这很好用,使用起来很愉快。内存问题非常罕见。

    【讨论】:

    • 我会使用 std::auto_ptr 来显示所有权正在转移(而不是 RAW 指针)。假设您已经存储在智能指针中,如果预期唯一所有权 std::auto_ptr 是完美的。
    • OP 说他不能使用泛型,这大概排除了任何智能指针类型。
    • 如果引用指向的对象被误删除,应用程序核心转储?如果它是一个指针,我们可以检查是否为 null。(如果删除器将其设置为 null。)
    • @balki 它可能;它可能不会;它可能会使烤面包机从天而降:它是UB。但 Sander 说,只有在保证它们不会悬空的情况下才会使用引用,所以这是一个代码质量问题。更重要的是,指针也好不到哪里去:另一个实体删除对象不会将观察者的指针副本设置为nullptr,因此它不能像引用一样测试有效性(阅读:@987654322 @ness 是地址的属性,而不是该地址的数据)...除非每个观察者 ptr 都由 ref 与所有者注册以供以后更新或复杂的东西
    【解决方案4】:

    在一般情况下(资源管理,其中资源不一定是内存),你需要熟悉RAII pattern。这是 C++ 开发人员最重要的信息之一。

    【讨论】:

      【解决方案5】:

      您可以从一些实现智能指针功能的基类派生所有内容(使用 ref()/unref() 方法和计数器。

      @Timbo 突出显示的所有点在设计该基类时都很重要。

      【讨论】:

        【解决方案6】:

        生日,

        我建议阅读 Scott Meyers 的“Effective C++”的相关部分。易于阅读,他涵盖了一些有趣的陷阱来诱捕粗心的人。

        我也对缺少模板很感兴趣。所以没有 STL 或 Boost。哇。

        顺便说一句,让人们就约定达成一致是一个绝妙的主意。就像让每个人都同意 OOD 的约定一样。顺便说一句,最新版本的 Effective C++ 没有第一版中关于 OOD 约定的优秀章节,这很遗憾,例如公共虚拟继承等约定总是模拟“isa”关系。

        罗伯

        【讨论】:

          【解决方案7】:

          我会在这里添加另一个规则:

          • 当自动对象可以正常工作时,不要新建/删除对象。

          我们发现,刚接触 C++ 的程序员,或者从 Java 等语言转过来的程序员,似乎会学习新的东西,然后在他们想要创建任何对象时痴迷地使用它,而不管上下文如何。当在函数中本地创建对象纯粹是为了做一些有用的事情时,这尤其有害。以这种方式使用 new 可能会损害性能,并且在忘记相应的删除操作时很容易引入愚蠢的内存泄漏。是的,智能指针可以帮助后者,但它不会解决性能问题(假设在幕后使用 new/delete 或等效项)。有趣的是(嗯,也许),我们发现在使用 Visual C++ 时,delete 往往比 new 更昂贵。

          其中一些混淆还来自这样一个事实,即它们调用的函数可能将指针,甚至智能指针作为参数(当引用可能会更好/更清晰时)。这使他们认为他们需要“创建”一个指针(很多人似乎认为这就是 new 所做的)才能将指针传递给函数。显然,这需要一些关于如何编写 API 的规则,以使调用约定尽可能明确,并通过函数原型提供的清晰 cmets 得到加强。

          【讨论】:

          • Picking from Nice "这让他们认为他们需要“创建”一个指针(很多人似乎认为这就是 new 所做的)才能将指针传递给函数。)”也许,但我认为这是因为大多数大学首先教授 Java。在 Java 中,您必须“新”一切,而且很难改掉这种习惯,尤其是当这两种语言看起来如此相似但实际上完全不同时。为了真正的乐趣,请尝试同时使用两者!
          【解决方案8】:

          一般而言,除非必须,否则应避免从堆中分配。如果必须,请对长期存在且需要在代码的不同部分之间共享的对象使用引用计数。

          有时您需要动态分配对象,但它们只会在一定的时间范围内使用。例如,在之前的项目中,我需要创建数据库模式的复杂内存表示——基本上是对象的复杂循环图。但是,该图仅在数据库连接期间才需要,之后可以一次性释放所有节点。在这种情况下,我称之为“本地 GC 习惯用法”的一个很好的使用模式。我不确定它是否有一个“官方”名称,因为我只在我自己的代码和 Cocoa 中看到过它(参见 Apple 的 Cocoa 参考中的NSAutoreleasePool)。

          简而言之,您创建了一个“收集器”对象,该对象保存指向您使用 new 分配的临时对象的指针。它通常与程序中的某个范围相关联,可以是静态范围(例如——作为实现 RAII 惯用语的堆栈分配对象)或动态范围(例如——与数据库连接的生命周期相关联,如我以前的项目)。当“收集器”对象被释放时,它的析构函数会释放它指向的所有对象。

          另外,像 DrPizza 一样,我认为不使用模板的限制太苛刻了。但是,在对 Solaris、AIX 和 HP-UX 的古老版本(就在最近——是的,这些平台仍然在财富 50 强中)进行了大量开发之后,我可以告诉你,如果你真的关心可移植性,你应该尽可能少地使用模板。不过,将它们用于容器和智能指针应该没问题(它对我有用)。如果没有模板,我描述的技术实施起来会更加痛苦。它要求“收集器”管理的所有对象都派生自一个公共基类。

          【讨论】:

          • 虽然本地 GC 习语听起来不错,但没有必要。手动内存管理并不是那么难做好。
          猜你喜欢
          • 2018-12-17
          • 2014-10-24
          • 2020-01-27
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多