#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
ему сразу после удаления.