Деструктор - я должен использовать delete или delete []? - PullRequest
7 голосов
/ 03 февраля 2010

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

Теперь я хочу создать деструктор для этого контейнера.Как освободить память, указанную этим указателем?У меня нет возможности узнать априори, массив это или отдельный элемент.

Я новичок в C ++, так что терпите меня.Я всегда использовал C, и Java - мой предпочтительный язык OO, но между желанием изучать C ++ и требованиями к скорости моего проекта я перешел на C ++.

Было бы лучше изменить контейнер с шаблона на контейнер для абстрактного класса, который может реализовать свой собственный деструктор?

Ответы [ 11 ]

6 голосов
/ 03 февраля 2010

Вы должны задокументировать, как этот класс ожидает использования, и всегда размещать его как положено. Вы также можете передать флаг объекту, указав, как он должен уничтожаться. Также посмотрите на интеллектуальные указатели Boost , которые могут справиться с этим различием для вас.

6 голосов
/ 03 февраля 2010

Если вы не знаете, было ли оно выделено с new или new[], удалить его небезопасно.

Возможно, ваш код работает. Например, на одной платформе, на которой я работаю, разница имеет значение только тогда, когда у вас есть массив объектов, которые имеют деструкторы. Итак, вы делаете это:

// by luck, this works on my preferred platform
// don't do this - just an example of why your code seems to work
int *ints = new int[20];
delete ints;

но затем вы делаете это:

// crashes on my platform
std::string *strings = new std::string[10];
delete strings;
5 голосов
/ 03 февраля 2010

Краткий ответ:

Если вы используете [] с новым, вы хотите использовать [] с удалением.

//allocate some memory
myObject* m = new myObject[100];

//later on...destructor...
delete m; //wrong
delete[] m; //correct

Это были голые кости,Другая вещь, на которую вы можете взглянуть, это boost .Также довольно сложно ответить, учитывая, что вы не уверены, является ли он массивом или отдельным объектом.Вы можете проверить это через флаг, указывающий вашему приложению, использовать ли delete или delete [].

2 голосов
/ 04 февраля 2010

Если у вас есть класс, который принимает указатель, на который он переходит в собственность, тогда контракт на использование класса должен включать одну из нескольких вещей. Или:

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

или

  • интерфейс должен включать механизм, где политика освобождения может быть указана тем, что дает указатель на класс. Это может быть так же просто, как обеспечение механизма для передачи функтора (или даже простого старого указателя на функцию), который будет вызываться для освобождения объекта (предпочтительно в той же функции / конструкторе, который передается в самом указателе). Это делает класс, возможно, более сложным в использовании (но, например, наличие политики по умолчанию для вызова delete для указателя, может сделать его таким же простым в использовании, как вариант 1 для большинства применений). Теперь, если кто-то хочет дать классу указатель на статически размещенный объект, он может передать функтор без операции, поэтому ничего не произойдет, если класс захочет освободить его, или функтор операции delete[], если объект был выделен new[] и т. д.
2 голосов
/ 03 февраля 2010

(Перемещение моего комментария в ответ по запросу.)

Правильный ответ JonH (об использовании уничтожения массива только при использовании конструкции массива), поэтому, возможно, вам следует предложить шаблоны: один для массивов, другой - нет.

Другой ответ - избегать массивов и вместо этого ожидать единственного экземпляра, который может или не может быть надлежащей коллекцией, которая очищается после себя, такого как vector <>.

1010 * редактировать *

Явно украдя у Роджера Пейта, я добавлю, что вам может потребоваться использование умного указателя, который составляет коллекцию из одного элемента.

2 голосов
/ 03 февраля 2010
  • Используйте delete, если вы выделены с new.
  • Используйте delete[], если вы выделены с new[].

После этих утверждений, еслиу вас все еще есть проблема (может быть, вы хотите удалить объект, созданный кем-то другим), тогда вы нарушаете третье правило:

  • Всегда удаляйте то, что создали.Следствие, никогда не удаляйте то, что вы не создали.
2 голосов
/ 03 февраля 2010

Вы не должны удалять это вообще. Если ваш класс принимает уже инициализированный указатель, удалить его небезопасно. Это может даже не указывать на объект в куче; вызов delete или delete[] может иметь катастрофические последствия.

Распределение и освобождение памяти должны происходить в одной и той же области. То, какой код владеет и инициализирует экземпляр вашего класса, также предположительно отвечает за инициализацию и передачу указателя, и означает, что - это то, где должен быть ваш delete

2 голосов
/ 03 февраля 2010

Как общее правило разработки, вы должны придерживаться схемы, в которой класс, вызывающий new, должен также вызывать delete

1 голос
/ 03 февраля 2010

Поскольку указатель в C ++ не сообщает нам, как он был распределен, да, нет способа решить, какой метод освобождения использовать. Решение состоит в том, чтобы дать выбор пользователю, который, мы надеемся, знает, как была распределена память. Взгляните на Boost smart ptr библиотеку, особенно на shared_ptr конструктор со вторым параметром, для отличного примера.

0 голосов
/ 04 февраля 2010

Проще говоря, учитывая только указатель на динамически распределяемую память, нет способа определить, как безопасно перераспределить ее. Указатель мог быть выделен любым из следующих способов:

  • используя новый
  • используя новый []
  • с использованием malloc
  • с использованием пользовательской функции
  • и т.д.

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

...