какое значение должен возвращать деструктор

Какое значение должен возвращать деструктор

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

имя класса, предназначенная для уничтожения переменных (delete).

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

4.3.1. Конструкторы

Конструктор по умолчанию

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

При создании любого экземпляра класса вызывается конструктор. Если при описании экземпляра не указываются никакие параметры – вызывается конструктор по умолчанию:

Полный конструктор

Полный конструктор позволяет явно инициализировать все переменные-члены класса.

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

Неполный конструктор

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

Инициализация переменных-членов класса в конструкторах может осуществляться не только в теле конструктора, но и после оператора :. При этом, во время присваивания переменной-члену значения, будет вызываться не оператор присваивания, а конструктор. Для встроенных типов данных, таких как double или int, это не существенно, но если членами класса являются абстрактные типы, вызов конструктора вместо оператора присваивания будет выполняться быстрее.

Конструктор копирования

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

4.3.2. Деструктор (пример 4.4. Конструктор и деструктор класса Матрица)

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

В классе Lens никакого динамического размещения не происходило, поэтому деструктор будет пустой, но его наличие все равно обязательно. Для примера реализации деструктора, представим, что имеется класс Matrix, который в конструкторе динамически создает двумерный массив размерности n x m. Тогда деструктор должен освобождать память, которую выделяет конструктор.

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

4.3.3. Проверка правильности параметров. Исключительные ситуации

Конструкторы должны проверять передаваемые им аргументы на корректность значений. Например, показатель преломления не может быть меньше 1. Что делать, если в конструктор были переданы неправильные параметры? Для этого в языке С++ существуют исключительные ситуации.

Класс exception является стандартным базовым классом C++ для всех исключений. Исключения можно сгенерировать в случае возникновения непредвиденной ошибки, например мы предполагаем что при вызове класса Lens никто не будет пытаться задать показатель преломления меньше 1, но при этом такая ситуация возможна, и это может привести к ошибке. Сгенерировать исключительную ситуацию можно при помощи оператора throw:

Для обработки возникшей исключительной ситуации используются try и catch блоки.

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

Если при выполнение какого-то оператора из блока try возникает исключение – управление сразу переходит к блоку catch. В блоке catch в скобках указывается тип исключения (exception это наиболее общий вид исключения, возможны и другие типы) и имя исключения. Внутри блока catch необходимо обработать ошибку. В нашем случае мы просто выводим на экран сообщение, в каких-то случаях потребуется более сложная обработка. Функция what() содержит текст, сгенерированный в момент создания исключения.

В результаты выполнения данного блока программы на экран выведется сообщение » Index of refraction should be greater than 1.».

Если никаких исключений в try-блоке не происходит, программа игнорирует его catch-обработчик.

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

Источник

12.9 – Деструкторы

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

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

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

Как и у конструкторов, у деструкторов есть особые правила именования:

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

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

Пример деструктора

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

Совет

Если вы скомпилируете приведенный выше пример и получите следующую ошибку:

Эта программа дает следующий результат:

В первой строке мы создаем экземпляр нового объекта класса IntArray с именем ar и передаем длину 10. Это вызывает конструктор, который динамически выделяет память для массива-члена. Здесь мы должны использовать динамическое выделение памяти, потому что во время компиляции не знаем, какова будет длина массива (это решает вызывающий).

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

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

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

Эта программа дает следующий результат:

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

Класс IntArray в примере выше является примером класса, который реализует RAII – выделение в конструкторе, освобождение в деструкторе. std::string и std::vector – классы стандартной библиотеки, которые следуют принципу RAII – динамическая память приобретается при инициализации и автоматически очищается при уничтожении.

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

Резюме

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

Источник

Деструкторы / 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 для каждого освобождения памяти, кажется, что он имеет приемлемую производительность, по крайней мере, во многих случаях.

Источник

BestProg

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

Содержание

Поиск на других ресурсах:

1. Какое назначение деструктора в классе?

Деструктор – это обратная по отношению к конструктору функция.

Имя деструктора совпадает с именем класса, перед которым следует символ ‘

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

Общий код модуля, в котором объявляется структура и класс.

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

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

4. В каких случаях целесообразно объявлять приватные деструкторы?

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

5. Какие ограничения возникают при работе с объектами класса, если в классе объявлен приватный деструктор?

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

Это связано с тем, что такие объекты в дальнейшем невозможно будет уничтожить.

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

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

то компилятор выдаст ошибку

6. Может ли деструктор иметь параметры?

Деструктор не может иметь параметров.

7. Какие основные отличия между использованием конструкторов и деструкторов в классах?

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

Источник

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Источник

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

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