Подтвердить что ты не робот

С++ Массив указателей: удалить или удалить []?

Косидеру следующий код:

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

В настоящее время у меня есть самый верхний конструктор, и все работает okey, но, конечно, я не вижу, что происходит утечка...

Лично я думаю, что вторая версия гораздо логичнее, учитывая то, что я делаю. Во всяком случае, что такое "правильный" способ сделать это?

4b9b3361

Ответ 1

delete[] monsters;

Неверно, потому что monsters не является указателем на динамически выделенный массив, это массив указателей. В качестве члена класса он будет автоматически уничтожен при уничтожении экземпляра класса.

Ваша другая реализация верна, так как указатели в массиве указывают на динамически выделяемые объекты Monster.

Обратите внимание, что с вашей текущей стратегией выделения памяти вы, вероятно, захотите объявить свой собственный конструктор копирования и оператор присваивания копиям, чтобы непреднамеренное копирование не вызывало двойных удалений. (Если вы хотите предотвратить копирование, вы можете объявить их как конфиденциальными, а не реализовывать их.)

Ответ 2

Для new вы должны использовать delete. Для new[] используйте delete[]. Ваш второй вариант правильный.

Ответ 3

Во-первых, это правильно (в любом случае, наименее ошибочно).

Изменить: "наименее ошибочный", как в исходном коде, нет веских оснований для использования new или delete в первую очередь, поэтому вы, вероятно, должны просто использовать:

std::vector<Monster> monsters;

В результате будет более простой код и более четкое разделение обязанностей.

Ответ 4

Для упрощения антивирусного программного обеспечения рассмотрим следующий код:

#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. он не удалит целые указатели в массиве = > утечка памяти объектов указателя!

Ответ 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

Как вы можете видеть, вы пытаетесь выпустить с неправильной формой delete (non-array vs. array), а указатель 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

Вы удаляете каждый указатель отдельно, а затем удаляете весь массив. Убедитесь, что вы определили правильный деструктор для классов, хранящихся в массиве, иначе вы не можете быть уверены, что объекты очищены должным образом. Убедитесь, что все ваши деструкторы являются виртуальными, чтобы они правильно работали при использовании с наследованием.

Ответ 8

Было бы хорошо, если бы ваш код был таким:

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