Основной вопрос: когда программа вызывает метод деструктора класса в C ++? Мне сказали, что он вызывается всякий раз, когда объект выходит за пределы области видимости или подвергаетсяdelete
Более конкретные вопросы:
1) Если объект создается с помощью указателя, и этот указатель позже удаляется или ему предоставляется новый адрес, на который он указывает, вызывает ли объект, на который он указывал, свой деструктор (при условии, что на него ничего не указывает)?
2) В продолжение вопроса 1, что определяет, когда объект выходит за пределы области видимости (не в отношении того, когда объект покидает данный {блок}). Итак, другими словами, когда деструктор вызывается для объекта в связанном списке?
3) Хотели бы вы когда-нибудь вызвать деструктор вручную?
источник
Ответы:
Это зависит от типа указателей. Например, интеллектуальные указатели часто удаляют свои объекты при удалении. Обычных указателей нет. То же самое верно, когда указатель указывает на другой объект. Некоторые интеллектуальные указатели уничтожат старый объект или уничтожат его, если на него больше не будет ссылок. У обычных указателей такой хитрости нет. Они просто содержат адрес и позволяют вам выполнять операции с объектами, на которые они указывают, специально для этого.
Это до реализации связанного списка. Типичные коллекции при уничтожении уничтожают все содержащиеся в них объекты.
Таким образом, связанный список указателей обычно уничтожает указатели, но не объекты, на которые они указывают. (Что может быть правильным. Это могут быть ссылки других указателей.) Однако связанный список, специально разработанный для содержания указателей, может удалять объекты при собственном уничтожении.
Связанный список интеллектуальных указателей может автоматически удалять объекты при удалении указателей или делать это, если у них больше нет ссылок. Все зависит от вас, чтобы выбрать части, которые делают то, что вы хотите.
Конечно. Одним из примеров может быть ситуация, когда вы хотите заменить объект другим объектом того же типа, но не хотите освобождать память только для ее повторного выделения. Вы можете уничтожить старый объект на месте и построить новый на месте. (Однако в целом это плохая идея.)
источник
new Foo()
с большой буквы.)Foo myfoo("foo")
это не самый неприятный анализ, но онchar * foo = "foo"; Foo myfoo(foo);
есть.delete myFoo
задавать раньшеFoo *myFoo = new Foo("foo");
? В противном случае вы бы удалили только что созданный объект, нет?myFoo
доFoo *myFoo = new Foo("foo");
линии. Эта строка создает новую переменную с именемmyFoo
, затеняя любую существующую. Хотя в этом случае его нет, так какmyFoo
вышеупомянутое входит в сферу действияif
, которая закончилась.Другие уже рассмотрели другие проблемы, поэтому я просто остановлюсь на одном моменте: вы когда-нибудь хотели удалить объект вручную.
Ответ положительный. @DavidSchwartz привел один пример, но он довольно необычный. Я приведу пример, который находится под капотом того, что многие программисты на C ++ используют все время:
std::vector
(иstd::deque
хотя он используется не так часто).Как известно большинству людей,
std::vector
будет выделять больший блок памяти, когда / если вы добавляете больше элементов, чем может вместить его текущее распределение. Однако, когда он это делает, у него есть блок памяти, способный хранить больше объектов, чем сейчас в векторе.Чтобы управлять этим, то, что
vector
скрывается, выделяет необработанную память черезAllocator
объект (что, если не указано иное, означает, что он использует::operator new
). Затем, когда вы используете (например),push_back
чтобы добавить элемент вvector
, внутри вектор использует aplacement new
для создания элемента в (ранее) неиспользованной части его пространства памяти.Теперь, что происходит, когда / если вы
erase
получаете элемент из вектора? Он не может просто использоватьdelete
- это освободит весь его блок памяти; ему необходимо уничтожить один объект в этой памяти, не разрушая другие, или освобождая какой-либо блок памяти, который он контролирует (например, если у васerase
5 элементов из вектора, а затем сразу ещеpush_back
5 элементов, гарантируется, что вектор не будет перераспределен память, когда вы это сделаете.Для этого вектор напрямую уничтожает объекты в памяти, явно вызывая деструктор, а не используя
delete
.Если, возможно, кто-то другой должен был написать контейнер, использующий непрерывное хранилище, примерно как это
vector
делает (или какой-то его вариант, как наstd::deque
самом деле), вы почти наверняка захотите использовать тот же метод.Например, давайте рассмотрим, как можно написать код для кольцевого буфера.
В отличие от стандартных контейнеров, здесь используется
operator new
иoperator delete
напрямую. Для реального использования вы, вероятно, захотите использовать класс распределителя, но на данный момент он будет больше отвлекать, чем способствовать (во всяком случае, IMO).источник
new
, вы отвечаете за вызовdelete
. Когда вы создаете объект с помощьюmake_shared
, результатshared_ptr
отвечает за ведение счетчика и вызов,delete
когда счетчик использования становится равным нулю.new
(т.е. это объект стека).new
.источник
1) Объекты не создаются «с помощью указателей». Есть указатель, который назначается любому «новому» объекту. Предполагая, что это именно то, что вы имеете в виду, если вы вызываете 'delete' на указателе, он фактически удалит (и вызовет деструктор) объект, разыменованный указателем. Если присвоить указатель другому объекту, произойдет утечка памяти; ничто в C ++ не соберет ваш мусор за вас.
2) Это два отдельных вопроса. Переменная выходит за пределы области видимости, когда кадр стека, в котором она объявлена, извлекается из стека. Обычно это когда вы покидаете блок. Объекты в куче никогда не выходят за пределы области видимости, хотя их указатели в стеке могут. Ничто в частности не гарантирует, что деструктор объекта в связанном списке будет вызван.
3) Не совсем. Может быть Deep Magic, который предполагает иное, но обычно вы хотите сопоставить свои «новые» ключевые слова с ключевыми словами «удалить» и поместить в свой деструктор все, что необходимо, чтобы убедиться, что он должным образом очищается. Если вы этого не сделаете, обязательно прокомментируйте деструктор конкретными инструкциями для всех, кто использует класс, о том, как они должны очищать ресурсы этого объекта вручную.
источник
Чтобы дать подробный ответ на вопрос 3: да, есть (редкие) случаи, когда вы можете явно вызвать деструктор, в частности, как аналог нового размещения, как отмечает dasblinkenlight.
Чтобы привести конкретный пример этого:
Цель такого рода вещей - отделить выделение памяти от построения объекта.
источник
Указатели - обычные указатели не поддерживают RAII. Без явного
delete
будет фигня. К счастью, в C ++ есть автоматические указатели, которые справятся с этим за вас!Область действия - подумайте о том, когда переменная становится невидимой для вашей программы. Обычно это в конце
{block}
, как вы указываете.Ручное уничтожение - никогда не пытайтесь это сделать. Просто позвольте прицелу и RAII творить чудеса за вас.
источник
std::auto_ptr
устарела в C ++ 11, да. Если OP действительно имеет C ++ 11, он должен использовать егоstd::unique_ptr
для отдельных владельцев илиstd::shared_ptr
для нескольких владельцев с подсчетом ссылок.std::queue<std::shared_ptr>?
я обнаружил, чтоpipe()
параллелизм между потоком-производителем и потоком-потребителем становится намного проще, если копирование не слишком дорого.Каждый раз, когда вы используете «новый», то есть прикрепляете адрес к указателю или, скажем, претендуете на место в куче, вам нужно «удалить» его.
1. да, при удалении вызывается деструктор.
2. При вызове деструктора связанного списка вызывается деструктор его объектов. Но если это указатели, их нужно удалить вручную. 3. когда на место претендует «новый».
источник
Да, деструктор (он же dtor) вызывается, когда объект выходит за пределы области видимости, если он находится в стеке, или когда вы вызываете
delete
указатель на объект.Если указатель удален через,
delete
то будет вызван dtor. Если вы переназначаете указатель без предварительного вызоваdelete
, вы получите утечку памяти, потому что объект все еще существует где-то в памяти. В последнем случае dtor не вызывается.Хорошая реализация связанного списка будет вызывать dtor всех объектов в списке, когда список уничтожается (потому что вы либо вызвали какой-то метод для его уничтожения, либо он сам вышел за пределы области видимости). Это зависит от реализации.
Сомневаюсь, но не удивлюсь, если возникнут какие-то странные обстоятельства.
источник
Если объект создается не с помощью указателя (например, A a1 = A ();), деструктор вызывается, когда объект разрушается, всегда когда функция, в которой находится объект, завершена. Например:
деструктор вызывается, когда код выполняется до строки "finish".
Если объект создается с помощью указателя (например, A * a2 = new A ();), деструктор вызывается при удалении указателя (delete a2;). Если точка не удалена пользователем явно или не задана новый адрес перед его удалением, происходит утечка памяти. Это ошибка.
В связанном списке, если мы используем std :: list <>, нам не нужно заботиться о деструкторе или утечке памяти, потому что std :: list <> завершил все это за нас. В связанном списке, написанном нами, мы должны написать десктруктор и удалить указатель явно, иначе это вызовет утечку памяти.
Мы редко вызываем деструктор вручную. Это функция, обеспечивающая систему.
Простите за мой плохой английский!
источник
Помните, что конструктор объекта вызывается сразу после выделения памяти для этого объекта, а деструктор вызывается непосредственно перед освобождением памяти этого объекта.
источник