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

#c #pointers #memory-leaks #ownership-semantics

#c #указатели #утечки памяти #владение-семантика

Вопрос:

Например,

 class Test{
private:
   int* foo;
public:
   Test(int* foo){this->foo = foo;}
}
 

В этом случае, есть ли какой-либо способ удалить foo в деструкторе? Должен ли я удалить foo в деструкторе или, по крайней мере, установить для него значение nullptr?

Комментарии:

1. Почему вы назвали элемент и конструктор agument с тем же именем?

2. Вы не можете вызвать delete что-то, с чем не было создано new . Все просто. Если вызывающий объект Test() передает указатель на new объект ‘ed, то delete он. В противном случае не делайте этого. Вот почему использование необработанных указателей настолько неоднозначно. Вместо этого используйте интеллектуальные указатели, чтобы сделать семантику владения более явной.

3. Если контракт вашего конструктора класса заключается в том, что он должен стать владельцем указателя, тогда он должен нести ответственность за удаление указателя.

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

5. @mATG Практика работает достаточно хорошо (насколько это возможно для чтения), если параметр не используется в теле функции, что предполагает, что используется список инициализаторов элементов, как в Test(int* foo) : foo(foo) {} . Это может показаться странным при первом просмотре, но к нему легко привыкнуть.

Ответ №1:

Конечно, вы можете. Это законный синтаксис. И в зависимости от остальной части вашей программы он может делать то, что вы хотите, или это может быть двойное удаление (удаление одного и того же указателя дважды), которое повреждает кучу и приведет к возможному сбою. (Компиляторы отладки могут это уловить.)

Одна из причин, по которой стандартная библиотека шаблонов C предоставила нам shared_ptr и unique_ptr заключается в том, что управление памятью вручную затруднено. Это не невозможно; люди делали это (или пытались) много-много лет. Но это также было источником многих сбоев во время выполнения, либо двойного удаления (также называемого преждевременным освобождением от старых подпрограмм malloc / free), либо, наоборот, утечки памяти. Автоматическое управление памятью было одним из преимуществ Java как языка, похожего на C / C , без риска ошибок памяти. Позже C # сделал то же самое для пользователей.

Я предлагаю использовать STL и подумать о семантике владения вашего foo . Может быть, тестовый класс должен владеть им; может быть, он должен поделиться им; может быть, у него действительно должна быть слабая ссылка. Не могу определить по фрагменту программы. Я могу только сказать, что вам следует пересмотреть современные идеи управления памятью в C и принять их.

Комментарии:

1. «или это может быть двойное удаление» — или это может быть попытка удалить нединамическую переменную (автоматическая или статическая продолжительность хранения), что тоже плохо.

Ответ №2:

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

Обработка необработанных указателей в C , как правило, является плохой практикой, и эта проблема является одним из примеров того, почему. Чтобы сделать использование класса очевидным для любого, кто его читает, я бы использовал std::unique_ptr , если вы намерены стать владельцем указателя и ссылки или std::shared_ptr если вы этого не сделаете.

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

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