2010-02-03 2 views
7

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

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

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

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

+3

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

+1

@Steven Sudit: Думаю, вы должны ответить на этот комментарий. –

+0

У вас могут возникнуть проблемы с потоками с этим подходом - вы не можете удалить что-то, что было выделено в другом потоке. – ChrisF

ответ

6

Если вы не знаете, было ли оно выделено 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; 
+1

Это не удача, это то, что 'int' не имеет деструктора. –

+5

@Steven: Это удача. Mis-matching 'new' и' delete' приводит к неопределенному поведению, периоду. Не определено, даже если тип имеет тривиальный деструктор. Для меня совершенно разумно заменить 'operator new' и' operator delete' как на вид; и я могу ожидать, что указатели в моей функции 'delete' будут выводиться соответствующим' new'. – GManNickG

+3

@Steven - это удача, потому что платформа * выбирает * для оптимизации распределения массивов, когда тип не имеет деструктора. Но другая платформа могла бы справиться с этим по-другому, и тогда этот код сломался бы на этой другой платформе. –

5

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

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

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

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

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

+2

Не отвечает на его вопрос. По-видимому, он понимает, что важно использовать правильную функцию 'delete', иначе он не будет задавать свой вопрос. : P – GManNickG

+0

@GMan Я отредактирую в соответствии с потребностями :). – JonH

+0

импульс, кажется, все решает ... У вас когда-нибудь была эта мысль? :) – JonH

0

Умный указатель как повышающего shared_pointer уже это покрыто, вы могли бы использовать его? linky

2

По общему правилу развития, вы должны придерживаться конструкции, где класс, который вызывает new следует также назвать delete

+4

Не совсем, это как раз противоположность большинства современных библиотек интеллектуальных указателей: 'std :: auto_ptr ap (new int()); boost :: shared_ptr sp (new int()); 'Оба вызова delete для вас. –

6

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

+0

Это в основном правильно. Если вы передаете указатель внутри, должен быть либо A: только один «правильный» способ его удаления, либо B: другая переменная «флаг», которая указывает, какой способ ее удалить, или C: пользовательский делектор, который вероятно, далеко за пределами понимания новичка. Идите с «B» в вашем случае IMO. –

+0

@ Кевин: Только в основном? Я что-то пропустил? Опция, с которой я иду, - это не тот, о котором вы упомянули: получите этот указатель в соответствующий интеллектуальный указатель ASAP и дайте умному указателю обработать, зная, как/если/когда его удалять. Основы * использования * auto_ptr или любого из интеллектуальных указателей повышения не очень сложны (хотя все подробности о настройке и особенностях реализации реализованы). –

+3

Для чего был downvote? –

2

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

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

+3

Так что я не могу использовать 'auto_ptr' /' unique_ptr' для передачи права собственности? – GManNickG

+0

@GMan Есть очевидные исключения, когда класс, в который вы передаете указатель, * разработан *, чтобы взять на себя его ... – meagar

1

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

+0

Укажите, пожалуйста, выбор пользователя? Это не имеет большого смысла? – JonH

+0

@JonH, * пользователю контейнера *, то есть код, использующий его, а не * пользователь на клавиатуре * :) –

+0

ahh Я тебя достал, был смущен на секунду. – JonH

2
  • Используйте delete, если вы выделили new.
  • Используйте delete[], если вы выделили new[].

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

  • Всегда удалять то, что вы создано. Следствие, никогда не удаляйте то, что вы не создали.
+0

Это третье правило нужно иногда прерывать, хотя я согласен с ним в целом. –

+0

Следствие из третьего правила - «никогда не создавать умные указатели» :-) –

+0

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

2

(Moving мой комментарий в ответ на запрос.)

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

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

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

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

0

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

  • с использованием новых
  • с использованием нового []
  • с помощью таНоса
  • с помощью определенного пользователя функции
  • т.д.

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

+0

В основном вы решили, будет ли ваш класс представлять smart_ptr или smart_array и оставить его вызывающему, чтобы дать подходящий выделенный указатель вашему классу. – UncleBens

+0

@UncleBens Не уверен, что ваш комментарий означает WRT мой ответ - несмотря на его название, «умный указатель» не является указателем. –

+0

Я имею в виду, что если все, что у вас есть, является указателем, то вы не можете определить, как оно было выделено. Я не думаю, что это заканчивается. Вы просто говорите: у меня есть класс, который будет называться 'delete' /' delete [] '/' free() '/ определяемая пользователем функция освобождения, и пользователь вашего класса должен предоставить подходящий выделенный указатель. В основном вы делаете что-то, что имеет смысл, а остальное не ваша проблема. – UncleBens

2

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

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

или

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

+1 Это правильный ответ. Я удивлен, что это не подтверждено. –

 Смежные вопросы

  • Нет связанных вопросов^_^