【问题标题】:Starting a C++ project. Should I worry about freeing dynamic allocated memory?启动 C++ 项目。我应该担心释放动态分配的内存吗?
【发布时间】:2010-04-27 17:32:57
【问题描述】:

我非常精通 C,在 C 中释放内存是必须的。

但是,我正在开始我的第一个 C++ 项目,并且我听说了一些关于您不需要通过使用共享指针和其他东西来释放内存的事情。

我应该在哪里阅读有关此内容的信息?这是正确的delete C++ 功能的有价值的替代品吗?它是如何工作的?

编辑

我很困惑,有人说我应该使用new 进行分配,并在释放过程中使用智能指针

其他人说我不应该首先分配动态内存。

其他人说如果我使用 new 我也必须像 C 一样使用 delete

那么,哪种方法被认为更标准并且更常用?

【问题讨论】:

  • 你应该专注于尽可能不要动态创建东西,而不是担心如何释放它们。
  • @Luca:你所说的所有事情都是模棱两可的。 “担心”是什么意思?不删除任何东西?有时删?所有新的都需要删除,期间。但是我们尽可能使用自动分配,因此您可以将动态分配的对象(通过指针)存储到自动变量中,以便它们会自动删除它们。为了:尽可能避免动态分配,在需要时使用:使用 new 将其直接分配到自动分配的容器中,以免泄漏。如果这不可能,请确保将其删除!
  • @Luca:堆栈分配几乎总是。您将知道在时机成熟时动态分配,或者您需要对象的寿命比它创建它的范围更长,或者您需要在运行时之前未知大小的内存。另请参阅stackoverflow.com/questions/2721128/… 在任何情况下,例如std::string,它是动态分配的包装器,因此您无需担心分配或删除。 std::string 使用自动存储创建,以确保自动释放它所做的任何事情。
  • @bta:可能没有标准的方法,但很多聪明的人都会觉得很新鲜,删除是一种糟糕的编码习惯。 永远不要手动删除,把它包起来。
  • @bta:等等,什么?我的讽刺雷达坏了吗? :)

标签: c++ garbage-collection memory-management


【解决方案1】:

我应该在哪里阅读有关此内容的信息?

Herb Sutter 的 Exceptional C++ 和 Scott Meyers 的 More Effective C++ 都是详细介绍该主题的优秀书籍。

网络上也有很多讨论(Google 或 StackOverflow 搜索“RAII”或“智能指针”无疑会产生很多好的结果)。

这是对正确的delete C++ 功能的有价值的替代品吗?

当然。不用担心清理资源的能力,尤其是在引发异常时,是使用 RAII 和智能指针最有价值的方面之一。

【讨论】:

  • RAII 可以用new/delete 来实现。将 new 放入构造函数,delete 放入析构函数。使用智能指针是另一种处理 RAII 的方法。
【解决方案2】:

我在评论中的意思是(对不起,我不得不跑去商店)是你应该使用:

std::string s = "foobar";

而不是:

std::string * s = new std::string( "foobar" );
...
delete s;

和:

vector <Person> p;
p.push_back( Person( "fred" ) );

而不是:

vector <Person *> p;
p.push_back( new Person( "fred" ) );

您应该始终使用为您管理内存的类。在 C++ 中,使用 new 创建对象的主要原因是您在编译时不知道它的类型。如果这不是原因,请在使用 new 和 delete,甚至是智能指针之前仔细考虑。

【讨论】:

  • ... 或vector&lt;shared_ptr&lt;Person&gt; &gt; p;
  • @greyfade:仅在实际需要时。
  • 当然,使用vector 而不是使用动态分配的数组,这可能是您在C 中所做的。同样,使用string 而不是使用char *;尽管后者并不总是需要动态分配,但当您想在 C 中连接字符数组时,事情会变得很烦人,至少与在 C++ 中连接 strings 相比。
【解决方案3】:

如果您分配动态内存(使用 new),则需要释放它(使用 delete),就像在 C 中使用 malloc/free。C++ 的强大之处在于它为您提供了许多不调用 new 的方法,在这种情况下你不需要调用 delete。

【讨论】:

    【解决方案4】:

    您仍然需要担心在 C++ 中释放内存,只是有更好的方法/工具可以这样做。有人可能会争辩说,由于增加了编写异常安全代码的要求,在 C++ 中关注内存管理也更加困难。这使得诸如:

    MyClass *y = new MyClass;
    doSomething(y);
    delete y;
    

    看起来完全无害,直到您发现doSomething() 抛出异常并且现在您有内存泄漏。这变得更加危险,因为代码在以后的版本中更改doSomething() 函数之前可能是安全的,因为上面的代码可能是安全的。

    遵循RAII 方法是解决内存管理挑战的重要部分,使用auto_ptrBoost 等库提供的共享指针可以更轻松地将这些方法合并到您的代码中。

    请注意,auto_ptr 不是“共享”指针。它是一个对象,它获取动态分配对象的所有权,并在分配和复制时放弃该所有权。它不计算对内存的引用。这使得它不适合在标准容器中使用,并且许多人通常更喜欢 Boost 的 shared_ptr 而不是标准提供的 auto_ptr。

    将 auto_ptrs 放入其中是不安全的 标准容器。有些人会 告诉你他们的编译器和 库编译得很好,其他的 会告诉你他们见过 正是这个例子推荐在 某个流行的文档 编译器;不要听他们的。

    问题是 auto_ptr 没有 相当符合一个类型的要求 你可以放入容器中,因为 auto_ptrs 的副本不是 相等的。一方面,有 没有什么说矢量不能只是 决定起来做一个“额外的” 某个对象的内部副本 包含。另一方面,当你打电话时 将复制的通用函数 元素,就像 sort() 所做的那样, 函数必须能够假设 副本将是 相等的。至少一种流行的类型 在内部获取“枢轴”的副本 元素,如果你尝试制作它 在 auto_ptrs 上工作会很开心 枢轴 auto_ptr 对象的副本 (从而取得所有权并投入 它在一个临时的 auto_ptr 上 边),完成其余的工作 序列(包括采取进一步 现在非拥有的 auto_ptr 的副本 被选为枢轴值),以及 当排序结束时,枢轴是 被摧毁,你有一个问题:在 序列中至少有一个 auto_ptr (作为枢轴值的那个)否 不再拥有它曾经持有的指针, 事实上它持有的指针有 已经被删除了!

    取自:Using auto_ptr Effectively

    【讨论】:

      【解决方案5】:

      好吧,你当然需要删除。我会将其改写为“我可以使用哪些库来自动删除分配的内存?”。我建议您先阅读Boost Smart pointers page

      【讨论】:

        【解决方案6】:

        我能给你的最佳答案是:something 需要为使用 new 创建的每个对象调用 delete。无论您是手动执行,还是使用基于作用域的智能指针、引用计数智能指针,甚至是非确定性垃圾收集器,仍然需要这样做。

        话虽如此,我已经有 10 年左右没有手动调用删除了。每当我可以创建一个自动对象(在堆栈上);当我出于某种原因需要在堆上创建对象时,我会尝试使用基于作用域的智能指针,在极少数情况下,当有正当理由拥有共享所有权时,我会使用引用计数智能指针。

        【讨论】:

          【解决方案7】:

          这是一个很好的问题,实际上是合二为一的:

          我需要担心管理内存吗?

          是的! C++ 中没有垃圾收集。每当您使用 new 分配某些内容时,您都需要在自己的代码中调用 delete,或者将该责任委托给 smart pointer 之类的东西。

          什么时候应该使用动态内存分配?

          您想要使用动态内存分配的原因(使用new 分配)。其中包括:

          • 您不知道在编译时分配的东西的大小
          • 您不知道在编译时分配的东西的类型
          • 您在不同的上下文中重复使用相同的数据,并且不想支付复制该数据的性能开销。

          还有很多其他原因,这些都是粗略的概括,但你明白了。

          我可以使用哪些工具来帮助我进行内存管理?

          Smart pointers 是去这里的方式。智能指针将获取您分配的内存的所有权,然后根据智能指针的策略在特定时间为您自动释放该内存。

          例如,boost::scoped_ptr 将在超出范围时为您释放内存

          {
             scoped_ptr<MyClass> myVar( new MyClass() );
          
             // do Something with myVar
          
          } // myVar goes out of scope and calls delete on its MyClass
          

          一般而言,您应该随时使用智能指针而不是原始指针。它将为您节省数年跟踪内存泄漏的时间。

          智能指针有多种形式,包括:

          如果你可以使用 Boost 智能指针,我会的。他们摇滚!

          【讨论】:

            【解决方案8】:

            由于 C++ 语言中没有内置垃圾收集器,因此您需要了解动态分配的内存以及如何释放该内存。

            也就是说,您可以使用智能指针来缓解必须通过 delete 手动释放内存的问题 - 例如,请参阅 Smart Ponters (boost)

            【讨论】:

              【解决方案9】:

              首先,在开始使用 auto_ptr 和编写自己的 RAII 类之前,请学习使用标准模板库。它提供了许多常见的容器类,当你实例化它们时会自动分配它们的内部内存,并在它们超出范围时释放它——比如向量、列表、映射等。当您使用 STL 时,很少需要使用 new-operator 和 delete(或 malloc 和 free)。

              【讨论】:

                【解决方案10】:

                在 C++ 中释放内存与在 C 中一样必须。

                您可能想到的是一个智能指针库(标准库的 auto_ptr 等等)——它将为您进行引用计数。

                【讨论】:

                • STL 不是标准 C++ 的一部分?从什么时候开始?
                • auto_ptr 是标准 C++ 的一部分,但它不是最有用的智能指针。
                • @Jeremy:严格来说,C++ 没有 STL 的概念。它有一个标准库。 STL 可能指代多种事物,并且通常是模棱两可的。也就是说,我同意 Alex 的回答措辞很奇怪,可以理解为 auto_ptr 不标准。
                • @Jeremy - STL 不是标准 C++ 语言规范的一部分,因此是首字母缩略词的 L(库)部分。同意我没有正确措辞并编辑了它。
                【解决方案11】:

                我很困惑,有些人在说 我应该使用 new 和 使用智能指针 释放过程。

                他们是对的。就像在 C 中一样,您仍然需要以一种或另一种方式管理所有内存。但是有一些方法可以使用该语言自动删除。

                智能指针基本上是指针的本地范围包装器,一旦智能指针(与堆栈上的任何其他对象一样)超出范围时,使用对象 .dtor 删除相应的指针

                【讨论】:

                  【解决方案12】:

                  C++ 的美妙之处在于,您可以明确控制何时创建事物以及何时销毁事物。做对了,你就不会遇到内存泄漏等问题。

                  根据您的环境,您可能希望在堆栈上创建对象,或者您可能希望动态分配(在“堆”上创建它们 - 用引号引起的堆,因为它是一个过度使用的术语,但现在已经足够了)。

                  Foo x; // 在堆栈上创建 - 当程序退出创建它的代码块时自动销毁。

                  Foo *y = new Foo; // 在堆上创建 - 它的 O.K.传递这个,因为你控制它什么时候被销毁

                  每当你使用'new'时,你应该在某个地方使用相应版本的delete...。如果您使用 new 来初始化智能指针,例如:

                  std::auto_ptr x = new Foo;

                  您实际上是在创建两个项目。 auto_ptr 的一个实例和 Foo 的一个实例。 auto_ptr 在栈上创建,Foo 在堆上。

                  当堆栈“展开”时,它会自动在 Foo 的那个实例上调用 delete。自动为您清理。

                  因此,一般的经验法则是,尽可能/实用地使用堆栈版本。在大多数情况下,它也会更快。

                  【讨论】:

                    【解决方案13】:

                    按照优先顺序,您应该:

                    1. 完全避免自己处理分配。 C++ 的 STL(标准模板库)带有许多为您处理分配的容器。使用vector 而不是动态分配的数组。对字符数组使用 string 而不是 char *。尝试从 STL 中寻找合适的容器,而不是自己设计。

                    2. 如果您正在设计自己的类并且确实需要动态分配(如果您使用 STL 的成员组成您的类,通常不需要),请将 new (new[]) 的所有实例放入你的构造函数和析构函数中delete (delete[]) 的所有实例。一般来说,您不需要mallocfree

                    3. 如果您无法在构造函数和析构函数中保持分配配对,请使用智能指针。真的,这与#2没有太大不同;智能指针基本上只是使用析构函数来确保释放发生的特殊类。

                    【讨论】:

                    • 顺便说一句:我更喜欢 2 而不是 3 的部分原因是任何不包含在 C++ 中的智能指针(例如 Boost)都不应该被视为“标准”,imo。而且你不能在 STL 容器中使用auto_ptr
                    • 如果不能使用Boost,至少复制shared_ptr和朋友。这样您就不需要浪费时间编写复制构造函数、复制赋值运算符和析构函数了。我唯一一次这样做是在制作一个小型实用程序类时,所以在那个用例中我不需要再这样做了。
                    猜你喜欢
                    • 2013-11-22
                    • 1970-01-01
                    • 2016-03-08
                    • 1970-01-01
                    • 2011-03-17
                    • 2017-06-13
                    • 2023-03-03
                    • 1970-01-01
                    相关资源
                    最近更新 更多