代码之家  ›  专栏  ›  技术社区  ›  Jasper

指针的C++数组:删除还是删除[]?

  •  37
  • Jasper  · 技术社区  · 14 年前

    cosider以下代码:

    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];
        }
    }
    

    我现在有最上面的构造器,一切都正常工作,但我当然看不出它是否碰巧泄漏了…

    就我个人而言,考虑到我所做的事情,我认为第二个版本更符合逻辑。不管怎样,“正确”的方法是什么?

    8 回复  |  直到 9 年前
        1
  •  42
  •   CB Bailey    14 年前

    delete[] monsters;

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

    另一个实现是正确的,因为数组中的指针指向动态分配的 Monster 物体。

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

        2
  •  35
  •   Kirill V. Lyadvinsky    14 年前

    为了 new 你应该使用 delete . 为了 new[] 使用 delete[] . 您的第二个变体是正确的。

        3
  •  11
  •   Jerry Coffin    14 年前

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

    编辑:“最小错误”,如原始代码中所示,没有充分的理由使用 new delete 首先,你应该使用:

    std::vector<Monster> monsters;
    

    其结果是代码更简单,职责分离更清晰。

        4
  •  10
  •   shai vashdi    12 年前

    为了简化Answare,我们来看下面的代码:

    #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))。

    我们需要使用:删除[]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,它不会删除数组中的所有指针=>指针对象内存泄漏!

        5
  •  5
  •   fredoverflow    14 年前

    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
  •  3
  •   Carl Norum    14 年前

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

        7
  •  1
  •   Daniel    14 年前

    如果您的代码是这样的,它将使sens:

    #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
  •  0
  •   Eric H    14 年前

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