истинно ли утверждение что деструктор может принимать аргументы

Урок №120. Деструкторы

На этом уроке мы рассмотрим, что такое деструкторы в языке С++, зачем они нужны, как их использовать и нюансы, которые могут возникнуть при их использовании.

Деструкторы

Деструктор — это специальный тип метода класса, который выполняется при удалении объекта класса. В то время как конструкторы предназначены для инициализации класса, деструкторы предназначены для очистки памяти после него.

Когда объект автоматически выходит из области видимости или динамически выделенный объект явно удаляется с помощью ключевого слова delete, вызывается деструктор класса (если он существует) для выполнения необходимой очистки до того, как объект будет удален из памяти. Для простых классов (тех, которые только инициализируют значения обычных переменных-членов) деструктор не нужен, так как C++ автоматически выполнит очистку самостоятельно.

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

Имена деструкторов

Так же, как и конструкторы, деструкторы имеют свои правила, которые касаются их имен:

деструктор должен иметь то же имя, что и класс, со знаком тильда (

деструктор не может принимать аргументы;

деструктор не имеет типа возврата.

Из второго правила вытекает еще одно правило: для каждого класса может существовать только один деструктор, так как нет возможности перегрузить деструкторы, как функции, и отличаться друг от друга аргументами они не могут.

Пример использования деструктора на практике

Рассмотрим простой класс с деструктором:

Massiv() вызывается тоже здесь

Результат выполнения программы:

The value of element 7 is 8

В первой строке функции main() мы создаем новый объект класса Massiv с именем arr и передаем длину ( length ) 15. Это приводит к вызову конструктора, который динамически выделяет память для массива класса ( m_array ). Мы должны здесь использовать динамическое выделение, поскольку на момент компиляции мы не знаем длину массива (это значение нам передает caller).

В конце функции main() объект arr выходит из области видимости. Это приводит к вызову деструктора

Massiv() и к удалению массива, который мы выделили ранее в конструкторе!

Выполнение конструкторов и деструкторов

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

Результат выполнения программы:

Constructing Another 1
1
Constructing Another 2
2
Destructing Another 2
Destructing Another 1

Идиома программирования RAII

Идиома RAII (англ. «Resource Acquisition Is Initialization» = «Получение ресурсов есть инициализация») — это идиома объектно-ориентированного программирования, при которой использование ресурсов привязывается к времени жизни объектов с автоматической продолжительностью жизни. В языке C++ идиома RAII реализуется через классы с конструкторами и деструкторами. Ресурс (например, память, файл или база данных) обычно приобретается в конструкторе объекта (хотя этот ресурс может быть получен и после создания объекта, если в этом есть смысл). Затем этот ресурс можно использовать, пока объект жив. Ресурс освобождается в деструкторе при уничтожении объекта. Основным преимуществом RAII является то, что это помогает предотвратить утечку ресурсов (например, памяти, которая не была освобождена), так как все объекты, содержащие ресурсы, автоматически очищаются.

В рамках идиомы программирования RAII объекты, располагающие ресурсами, не должны быть динамически выделенными, так как деструкторы вызываются только при уничтожении объектов. Для объектов, выделенных из стека, это происходит автоматически, когда объект выходит из области видимости, поэтому нет необходимости беспокоиться о том, что ресурс в конечном итоге не будет очищен. Однако за очистку динамически выделенных объектов, которые выделяются из кучи, уже пользователь несет ответственность: если он забыл её выполнить, деструктор вызываться не будет, и память как для объекта класса, так и для управляемого ресурса будет потеряна — произойдет утечка памяти!

Класс Massiv из программы, приведенной в начале этого урока, является примером класса, который реализует принципы RAII: выделение в конструкторе, освобождение в деструкторе. std::string и std::vector — это примеры классов из Стандартной библиотеки С++, которые следуют принципам RAII: динамическая память выделяется при инициализации и автоматически освобождается при уничтожении.

Правило: Используйте идиому программирования RAII и не выделяйте объекты вашего класса динамически.

Предупреждение о функции exit()

Если вы используете функцию exit(), то ваша программа завершится, и никакие деструкторы не будут вызваны. Будьте осторожны, если в таком случае вы полагаетесь на свои деструкторы для выполнения необходимой работы по очистке (например, перед тем, как выйти, вы записываете что-нибудь в лог-файл или в базу данных).

Заключение

Используя конструкторы и деструкторы, ваши классы могут выполнять инициализацию и очистку после себя автоматически без вашего участия! Это уменьшает вероятность возникновения ошибок и упрощает процесс использования классов.

Поделиться в социальных сетях:

Урок №119. Делегирующие конструкторы

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

долгое время считал что деструктор автоматически удаляет все, что создано в конктрукторе. Достаточно его просто создать. Недавно задумался что это не так, и впервые про это прочитал тут. Спасибо, где ж вы раньше были)

Не совсем понял фразу в тексте: «В рамках идиомы программирования RAII объекты, располагающие ресурсами, не должны быть динамически выделенными, так как деструкторы вызываются только при уничтожении объектов». Ведь деструкторы нужны в основном только при использовании динамической памяти, а тут написано, что «объекты не должны быть динамически выделенными». Как-то я запутался. Даже GOOGLE не помог.
Кто бы мне объяснил, я бы был очень благодарен.

Если объект создается в автоматической области памяти (напр. в функции), то при выходе из этой области он будет уничтожен деструктором автоматически, без вашего участия. Если же вы создаете объекты в динамической памяти (используя new), то освобождать ее вы должны сами операцией delete (это должно быть отражено в деструкторе вами, те дб написан нужный код). Почитайте «Язык программирования C++. Лекции и упражнения» Стивена Прата
6-е издание, глава 12 — там все есть.

Коротко, емко, толково. Спасибо. Хотя все это и знал, но не могу не оставить положительный отзыв за грамотное и сжатое изложение.

Источник

Есть ли деструктор в C#?

Чем отличается деструктор от финализатора

Деструктор — это метод для деинициализации объекта. Здесь важно упомянуть о такой штуке, как deterministic destruction. То есть мы точно знаем, когда объект будет удален. Чаще всего это происходит, когда заканчивается область видимости объекта, или программист явно освобождает память(в с/с++).

А вот определение финализатора из Википедии.

Финализатор — это метод класса, который автоматически вызывается средой исполнения в промежутке времени между моментом, когда объект этого класса опознаётся сборщиком мусора как неиспользуемый, и моментом удаления объекта (освобождения занимаемой им памяти). Это уже обратная штука — nondeterministic destruction.
То есть главный минус финализатора в том, что мы не знаем, когда он вызовется. Это может создать огромное количество проблем.

В Visual C# есть финализатор, который создается с помощью синтаксиса создания деструктора в С++, и который некоторыми даже называется как деструктор, хотя таковым не является. Выполнен он через метод Finalize, который нельзя переопределить(в C# нет, но в VB можно), поэтому и приходится использовать синтаксис деструктора через тильду(

ClassName). И только при компиляции в IL, компилятор называет его Finalize. При выполнении этот метод также вызывает финализатор родительского класса.

Если заглянуть в спецификацию языка программирования C#(4.0 на данный момент), то там слово «finalizer» ни разу не встречается. Ну это еще можно объяснить. Финализатор тесно связан со сборщиком мусора, который в свою очередь является частью среды выполнения(CLR в нашем случае), но не самого языка программирования.

Теперь пойдем еще дальше и залезем в спецификацию CLI(ECMA-335). Здесь вот что написано.

A class definition that creates an object type can supply an instance method (called a finalizer) to be called
when an instance of the class is no longer reachable.

Это, несомненно, описание финализатора, хотя на мой взгляд, немного неточное.

Далее, идем на msdn. Ни в одной статье не встречается слово finalizer в чистом виде — зато почти всегда используется слово деструктор. Возникает закономерный вопрос — почему люди называют деструктором то, что им не является. Получается, что майкрософтовские разработчики сознательно поменяли значение этого слова. И вот почему.

Мы знаем, что в Visual C# nondeterministic destruction. Это значит, что даже если область видимости объекта закончилась, и сборщик мусора понял, что можно освобождать занимаемую им память, не факт, что это произойдет незамедлительно. То есть это чистой воды финализатор. Так как он использует синтаксис, который во всех языках используется для деструктора, можно предположить, что в Visual C# нет способа определить деструктор(в общем понимании). Это значит, что его просто-напросто нет. Да, необходимости в нем тоже особой нет, но нужно согласиться с тем, что и самого деструктора в Visual C# быть не может.

Вывод — либо я идиот, и совсем все неправильно понял(а вероятность этого довольно высока), либо нужно смириться с этим, и называть деструктором то, что внешне на него похоже(тильда, привет), но по сути является финализатором. Надо же как-то жить в этом мире.

Источник

Деструкторы (C++)

). Например, деструктор для класса String объявляется следующим образом:

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

Рассмотрим следующее объявление класса String :

В предыдущем примере деструктор String::

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

Объявление деструкторов

Деструкторы — это функции с тем же именем, что и класс, но с добавленным в начало знаком тильды (

При объявлении деструкторов действуют несколько правил. Деструкторы:

Не могут иметь аргументов.

Не возвращают значение (или void ).

Использование деструкторов

Деструкторы вызываются, когда происходит одно из следующих событий:

Локальный (автоматический) объект с областью видимости блока выходит за пределы области видимости.

Время существования временного объекта заканчивается.

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

Деструктор явно вызываться с использованием полного имени функции деструктора.

Деструкторы могут свободно вызывать функции-члена класса и осуществлять доступ к данным членов класса.

Существуют два ограничения на использование деструкторов.

Вы не можете получить его адрес.

Производные классы не наследуют деструктор своего базового класса.

Порядок уничтожения

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

Вызывается деструктор класса, и выполняется тело функции деструктора.

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

Деструкторы для невиртуальных базовых классов вызываются в обратную последовательность объявления.

Деструкторы для виртуальных базовых классов вызываются в порядке, обратном порядку их объявления.

Виртуальные базовые классы

Деструкторы для виртуальных базовых классов вызываются в порядке, обратном их указанию в направленном ациклическом графе (в глубину, слева направо, обход в обратном порядке). На следующем рисунке представлен граф наследования.

истинно ли утверждение что деструктор может принимать аргументы. Смотреть фото истинно ли утверждение что деструктор может принимать аргументы. Смотреть картинку истинно ли утверждение что деструктор может принимать аргументы. Картинка про истинно ли утверждение что деструктор может принимать аргументы. Фото истинно ли утверждение что деструктор может принимать аргументы
Граф наследования, показывающий виртуальные базовые классы

Ниже перечислены заголовки классов, представленных на рисунке.

Просмотрите левую часть графа, начиная с самой глубокой точки графа (в данном случае E ).

Просматривайте граф справа налево, пока не будут пройдены все узлы. Запомните имя текущего узла.

Пересмотрите предыдущий узел (вниз и вправо), чтобы определить, является ли рассматриваемый узел виртуальным базовым классом.

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

Если рассматриваемого узла еще нет в списке, добавьте его вниз списка.

Просмотрите граф вверх и вдоль следующего пути вправо.

Перейдите к шагу 2.

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

Перейдите к шагу 3.

Выполняйте этот процесс, пока нижний узел снова не станет текущим узлом.

Таким образом, для класса E порядок удаления будет следующим.

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

Порядок построения или удаления очень важен, когда конструкторы и деструкторы в одном классе полагаются на другой компонент, который создается первым или сохраняется дольше, например если деструктор A (на рисунке выше) полагается на то, что B будет по-прежнему присутствовать после выполнения кода, или наоборот.

Такие взаимозависимости между классами в графе наследования опасны, поскольку классы, наследуемые впоследствии, могут изменить крайний левый путь, тем самым изменив порядок построения и удаления.

Не являющиеся виртуальными базовыми классами

Деструкторы для невиртуальных базовых классов вызываются в порядке, в котором объявляются имена базовых классов. Рассмотрим следующее объявление класса.

Явные вызовы деструктора

Редко возникает необходимость в явном вызове деструктора. Однако может быть полезно выполнить удаление объектов, размещенных по абсолютным адресам. Обычно эти объекты выделяются с помощью определяемого пользователем new оператора, принимающего аргумент размещения. delete Оператор не может освободить эту память, так как она не выделена из бесплатного хранилища (Дополнительные сведения см. delete ). Вызов деструктора, однако, может выполнить соответствующую очистку. Для явного вызова деструктора для объекта ( s ) класса String воспользуйтесь одним из следующих операторов.

Нотация для явных вызовов деструкторов, показанная в предыдущем примере, может использоваться независимо от того, определяет ли тип деструктор. Это позволяет выполнять такие явные вызовы, не зная, определен ли деструктор для типа. Явный вызов деструктора, если ни один из них не определен, не имеет никакого эффекта.

Отказоустойчивость

Классу требуется деструктор, если он получает ресурс, и для безопасного управления ресурсом, вероятно, потребуется реализовать конструктор копии и назначение копирования.

Если эти специальные функции не определены пользователем, они неявно определяются компилятором. Неявно созданные конструкторы и операторы присваивания выполняют поверхностную почленном копию, которая почти наверняка неверно, если объект управляет ресурсом.

Явное определение деструктора, конструктора копирования или оператора присваивания копирования предотвращает неявное определение конструктора перемещения и оператора присваивания перемещения. В этом случае не удастся предоставить операции перемещения, если копирование занимает много ресурсов, но пропущенная возможность оптимизации.

Источник

Деструкторы / FAQ C++

Что там с деструкторами?

Деструктор выполняет над объектом последние ритуалы.

Деструкторы – это функция-член «подготовки к смерти». Часто их называют сокращенно «dtor».

В каком порядке разрушаются локальные объекты?

В порядке, обратном созданию: первым создан – последним разрушен.

В каком порядке разрушаются объекты в массиве?

В порядке, обратном созданию: первым создан – последним разрушен.

В каком порядке разрушаются подобъекты объекта?

В порядке, обратном созданию: первым создан – последним разрушен.

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

В следующем примере порядок вызовов деструктора, когда d выходит за пределы области видимости, будет

Могу ли я перегрузить деструктор для своего класса?

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

Должен ли я явно вызывать деструктор локальной переменной?

Что, если я хочу, чтобы локальный объект «умер» до закрытия > области, в которой он был создан? Могу ли я вызвать деструктор локального объекта, если действительно хочу это сделать?

Ладно, ладно, уже; я не буду явно называть деструктор локального объекта; но как мне справиться с ситуацией из предыдущего вопроса?

Просто оберните время жизни локального объекта в искусственный блок <. >:

Что делать, если я не могу обернуть локальный объект в искусственный блок?

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

Тогда деструктор Fred::

Fred() будет автоматически вызван, когда вы удалите его через:

Что такое «размещение new » и зачем его использовать?

СОВЕТ. Без необходимости не используйте синтаксис «размещения new ». Используйте его только тогда, когда вам действительно важно, чтобы объект был помещен в определенное место в памяти. Например, если ваше оборудование имеет устройство таймера ввода-вывода с отображением в памяти, и вы хотите разместить объект Clock в этом месте памяти.

ОПАСНОСТЬ. Вы несете исключительную ответственность за то, чтобы указатель, который вы передаете оператору «размещение new », указывал на область памяти, которая достаточно велика и правильно выровнена для типа объекта, который вы создаете. Ни компилятор, ни система времени выполнения не пытаются проверить, правильно ли вы это сделали. Если ваш класс Fred нужно выровнять по 4-байтовой границе, но вы указали местоположение, которое не выровнено должным образом, у вас может возникнуть серьезная катастрофа (если вы не знаете, что означает «выравнивание», пожалуйста, не используйте синтаксис размещения new ). Вы предупреждены.

Вы также несете полную ответственность за уничтожение размещенного объекта. Это делается явным вызовом деструктора:

Это примерно единственный раз, когда вы явно вызываете деструктор.

Примечание: существует более чистый, но более изощренный способ справиться с задачей уничтожения/удаления.

Нет, но если оно вам необходимо, вы можете написать свое собственное.

Учитывая это, мы можем написать

Однако иногда программист знает, способ есть:

Теперь мы можем написать:

Также возможно определить пары операторов operator new() и operator delete() для иерархии классов TC++PL(SE) 15.6. Смотрите также D&E 10.4 и TC++PL(SE) 19.4.5.

Когда я пишу деструктор, нужно ли мне явно вызывать деструкторы для моих объектов-членов?

Нет. Вам никогда не нужно явно вызывать деструктор (за исключением размещения new ).

Деструктор класса (вне зависимости от того, определили вы его явно или нет) автоматически вызывает деструкторы для объектов-членов. Они уничтожаются в порядке, обратном тому, в котором были указаны в объявлении класса.

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

Нет. Вам никогда не нужно явно вызывать деструктор (за исключением размещения new ).

Деструктор производного класса (вне зависимости от того, определили вы его явно или нет) автоматически вызывает деструкторы для подобъектов базового класса. Базовые классы уничтожаются после объектов-членов. В случае множественного наследования прямые базовые классы уничтожаются в порядке, обратном их появлению в списке наследования.

Примечание. Зависимости порядка с виртуальным наследованием сложнее. Если вы полагаетесь на зависимости порядка в иерархии виртуального наследования, вам понадобится гораздо больше информации, чем в этом FAQ.

Должен ли мой деструктор генерировать исключение при обнаружении проблемы?

Осторожно. Подробности смотрите в ответе на этот вопрос (ссылка скоро появится).

Есть ли способ заставить new выделять память из определенной области памяти?

Да. Хорошей новостью является то, что эти «пулы памяти» полезны в ряде ситуаций. Плохая новость в том, что мне придется протащить вас через болото того, как это работает, прежде чем мы обсудим все применения. Но если вы не знаете о пулах памяти, возможно, стоит потратить время на изучение ответа на этот вопрос – вы можете узнать кое-что полезное!

Предполагая, что вы использовали размещение new и выжили после двух приведенных выше строк кода, следующим шагом будет превращение вашего распределителя памяти в объект. Такие объекты называют «пулом памяти» или «ареной памяти». Это позволяет вашим пользователям иметь более одного «пула» или «арены», из которых будет выделяться память. Каждый из этих объектов пула памяти будет выделять большой кусок памяти, используя определенный системный вызов (например, разделяемую память, постоянную память, стековую память и т.д.; смотрите ниже), и при необходимости распределяет его небольшими порциями. Ваш класс пула памяти может выглядеть примерно так:

Здесь есть несколько проблем, и все они решаемы:

Мы исправим эти проблемы в этом же порядке.

Дело в том, что компилятор освобождает память, если конструктор генерирует исключение. Но в случае синтаксиса « new с параметром» (обычно называемого «размещением new ») компилятор не знает, что делать, если возникает исключение, поэтому по умолчанию он ничего не делает:

После этого компилятор автоматически обернет вызовы конструкторов ваших выражений new в блок try :

Другими словами, однострочная функция operator delete(void* p, Pool& pool) заставляет компилятор автоматически закрывать утечку памяти. Конечно, эта функция может быть, но не обязательно, встраиваемой.

Проблемы №2 («уродливо, поэтому подвержено ошибкам») и №3 («пользователи должны вручную связывать указатели пула с объектом, который выделил для них память, что подвержено ошибкам») решаются одновременно с помощью дополнительных 10-20 строк кода в одном место. Другими словами, мы добавляем 10-20 строк кода в одном месте (заголовочный файл Pool ) и упрощаем сколь угодно большое количество других мест (каждый фрагмент кода, который использует ваш класс Pool ).

Несмотря на то, что этот метод требует поиска в std::map для каждого освобождения памяти, кажется, что он имеет приемлемую производительность, по крайней мере, во многих случаях.

Источник

В чем ошибка (работа с памятью, деструкторы)?

Простой 1 комментарий

истинно ли утверждение что деструктор может принимать аргументы. Смотреть фото истинно ли утверждение что деструктор может принимать аргументы. Смотреть картинку истинно ли утверждение что деструктор может принимать аргументы. Картинка про истинно ли утверждение что деструктор может принимать аргументы. Фото истинно ли утверждение что деструктор может принимать аргументы

При передаче объекта по значению происходит его копирование, но динамически выделенная память не копируется, а только значение указателя _coords. В результате у вас два объекта которые ссылаются на один и тот же массив. По завершению работы Dist копия объекта уничтожается с вызовом деструктора и освобождением памяти. А по завершению работы программы вызываются деструкторы объектов A и B в которых происходит попытка освободить память для _coords второй раз, а это неопределенное поведения (undefined behaviour) другими словами может произойти вообще что угодно.

Вам нужно самому реализовать конструктор копирования или запретить его. Когда вы просто удаляете деструктор происходит утечка памяти, когда передаете по ссылке объекты не копируются поэтому всё норм. Если объект динамически выделяет память, например с помощью new или delete, то реализация конструктора копирования обязательна. Можно также его запретить перенеся конструктор копирования в приватную секцию или для c++11 объявить Point(const Point&) = delete; В этом случае передача такого объекта по значению приведет к ошибке компиляции. Это важная тема изучите ее.

Все что выделено с помощью new [] удаляется только с помощью delete[]

истинно ли утверждение что деструктор может принимать аргументы. Смотреть фото истинно ли утверждение что деструктор может принимать аргументы. Смотреть картинку истинно ли утверждение что деструктор может принимать аргументы. Картинка про истинно ли утверждение что деструктор может принимать аргументы. Фото истинно ли утверждение что деструктор может принимать аргументы

истинно ли утверждение что деструктор может принимать аргументы. Смотреть фото истинно ли утверждение что деструктор может принимать аргументы. Смотреть картинку истинно ли утверждение что деструктор может принимать аргументы. Картинка про истинно ли утверждение что деструктор может принимать аргументы. Фото истинно ли утверждение что деструктор может принимать аргументы

У вас же C++, т.е. можете использовать вектор для хранения элементов без необходимости явного выделения памяти:

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *