С++ Массив указателей: удалить или удалить []?
Косидеру следующий код:
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, но, конечно, я не вижу, что происходит утечка...
Лично я думаю, что вторая версия гораздо логичнее, учитывая то, что я делаю. Во всяком случае, что такое "правильный" способ сделать это?
Ответы
Ответ 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;
}