【问题标题】:C++ Array of pointers: delete or delete []?C++ 指针数组:删除还是删除 []?
【发布时间】:2011-02-18 08:12:47
【问题描述】:

考虑以下代码:

class Foo
{
    Monster* monsters[6];

    Foo()
    {
        for (int i = 0; i < 6; i++)
        {
            monsters[i] = new Monster();
        }
    }

    virtual ~Foo();
}

什么是正确的析构函数?

这个:

Foo::~Foo()
{
    delete [] monsters;
}

或者这个:

Foo::~Foo()
{
    for (int i = 0; i < 6; i++)
    {
        delete monsters[i];
    }
}

我目前有最上面的构造函数,一切正常,但是我当然看不到它是否恰好在泄漏......

就我个人而言,考虑到我正在做的事情,我认为第二个版本更合乎逻辑。无论如何,这样做的“正确”方法是什么?

【问题讨论】:

    标签: c++ arrays pointers delete-operator


    【解决方案1】:

    delete[] monsters;

    不正确,因为monsters 不是指向动态分配数组的指针,它 指针数组。作为类成员,当类实例被销毁时会自动销毁。

    您的其他实现是正确的,因为数组中的指针确实指向动态分配的Monster 对象。

    请注意,使用您当前的内存分配策略,您可能希望声明自己的复制构造函数和复制赋值运算符,以便无意复制不会导致双重删除。 (如果您想防止复制,可以将它们声明为私有而不实际实现它们。)

    【讨论】:

    • 你能解释一下为什么要禁用复制构造函数和复制赋值运算符吗?
    • @gen 这是一个猜测,所以要考虑它的价值,但我 认为 如果没有自定义复制分配/构造函数,可能会发生以下情况:2 Foo具有指向 same 怪物的怪物指针数组的对象。如果您销毁 Foo1,则 Foo2 中的指针将无效,因为它们指向的内存已被 Foo1 释放。
    【解决方案2】:

    对于new,您应该使用delete。对于new[],请使用delete[]。您的第二个变体是正确的。

    【讨论】:

      【解决方案3】:

      为了简化答案,让我们看看下面的代码:

      #include "stdafx.h"
      #include <iostream>
      using namespace std;
      
      class A
      {
      private:
          int m_id;
          static int count;
      public:
          A() {count++; m_id = count;}
          A(int id) { m_id = id; }
          ~A() {cout<< "Destructor A "   <<m_id<<endl; }
      };
      
      int A::count = 0;
      
      void f1()
      {   
          A* arr = new A[10];
          //delete operate only one constructor, and crash!
          delete arr;
          //delete[] arr;
      }
      
      int main()
      {
          f1();
          system("PAUSE");
          return 0;
      }
      

      输出是: 析构函数 A 1 然后它就崩溃了(表达式:_BLOCK_TYPE_IS_VALID(phead- nBlockUse))。

      我们需要使用:delete[] arr;因为它删除了整个数组,而不仅仅是一个单元格!

      尝试使用 delete[] arr;输出是: 析构函数 A 10 析构函数 A 9 析构函数 A 8 破坏者 A 7 析构函数 A 6 析构函数 A 5 析构函数 A 4 析构函数 A 3 析构函数 A 2 析构函数 A 1

      同样的原理也适用于指针数组:

      void f2()
      {
          A** arr = new A*[10];
          for(int i = 0; i < 10; i++)
          {
              arr[i] = new A(i);
          }
          for(int i = 0; i < 10; i++)
          {
              delete arr[i];//delete the A object allocations.
          }
      
          delete[] arr;//delete the array of pointers
      }
      

      如果我们使用 delete arr 而不是 delete[] arr。它不会删除数组中的整个指针 => 指针对象的内存泄漏!

      【讨论】:

        【解决方案4】:

        在这种情况下,第二个是正确的(好吧,至少是错误的)。

        编辑:“最少错误”,因为在原始代码中显示没有充分的理由首先使用 newdelete,所以你应该只使用:

        std::vector<Monster> monsters;
        

        结果将是更简单的代码和更清晰的职责分离。

        【讨论】:

        • @Jerry 我假设Monster 是一种多态类型。在这种情况下,必须使用某种指针作为vector 的元素类型以启用多态性。
        • @FredOverflow:虽然他当然有可能处理多态层次结构,但 1) 他实际上并没有证明这一点,并且 2) 如果是向量,它仍然可以。跨度>
        • @Jerry 1) 正确 2) 绝对
        • 如果 Monster 是一个基类并且它不想与其他 Foo 对象共享它的怪物,那么它应该是 boost::ptr_vector 如果不是那么一个 Monster 对象数组(不是怪物指针)。
        • 你需要做这两件事!! delete mosters[i] 将删除数组中指向的数据。 delete [] monsters 将删除实际的数组!
        【解决方案5】:

        delete[] monsters 绝对是错误的。我的堆调试器显示以下输出:

        allocated non-array memory at 0x3e38f0 (20 bytes)
        allocated non-array memory at 0x3e3920 (20 bytes)
        allocated non-array memory at 0x3e3950 (20 bytes)
        allocated non-array memory at 0x3e3980 (20 bytes)
        allocated non-array memory at 0x3e39b0 (20 bytes)
        allocated non-array memory at 0x3e39e0 (20 bytes)
        releasing     array memory at 0x22ff38
        

        如您所见,您尝试使用错误的删除形式(非数组与数组)进行释放,并且指针 0x22ff38 从未通过对 new 的调用返回。第二个版本显示正确的输出:

        [allocations omitted for brevity]
        releasing non-array memory at 0x3e38f0
        releasing non-array memory at 0x3e3920
        releasing non-array memory at 0x3e3950
        releasing non-array memory at 0x3e3980
        releasing non-array memory at 0x3e39b0
        releasing non-array memory at 0x3e39e0
        

        无论如何,我更喜欢一开始就不需要手动实现析构函数的设计。

        #include <array>
        #include <memory>
        
        class Foo
        {
            std::array<std::shared_ptr<Monster>, 6> monsters;
        
            Foo()
            {
                for (int i = 0; i < 6; ++i)
                {
                    monsters[i].reset(new Monster());
                }
            }
        
            virtual ~Foo()
            {
                // nothing to do manually
            }
        };
        

        【讨论】:

          【解决方案6】:

          你的第二个例子是正确的;您不需要删除 monsters 数组本身,只需删除您创建的各个对象。

          【讨论】:

            【解决方案7】:

            如果你的代码是这样的,那就有意义了:

            #include <iostream>
            
            using namespace std;
            
            class Monster
            {
            public:
                    Monster() { cout << "Monster!" << endl; }
                    virtual ~Monster() { cout << "Monster Died" << endl; }
            };
            
            int main(int argc, const char* argv[])
            {
                    Monster *mon = new Monster[6];
            
                    delete [] mon;
            
                    return 0;
            }
            

            【讨论】:

              【解决方案8】:

              您单独删除每个指针,然后删除整个数组。确保您已经为存储在数组中的类定义了适当的析构函数,否则您无法确定对象是否已正确清理。确保您的所有析构函数都是虚拟的,以便它们在与继承一起使用时能够正常运行。

              【讨论】:

              • 我认为两者都做会很奇怪,因为数组的长度是恒定的,所以你不需要删除它。另外,我确实将析构函数声明为虚拟的,因此该注释毫无用处。
              猜你喜欢
              • 2015-08-09
              • 1970-01-01
              • 2018-05-14
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2013-04-22
              相关资源
              最近更新 更多