Что такое РАИИ? Примеры?


19

Всегда, когда используется термин RAII, люди фактически говорят о деконструкции, а не об инициализации. Я думаю, что у меня есть базовое понимание того, что это может означать, но я не совсем уверен. Кроме того: является ли C ++ единственным языком RAII? А как насчет Java или C # /. NET?

Ответы:


25

Resource Acquisition Is Initialization означает, что объекты должны заботиться о себе как о целостном пакете и не ожидать, что другой код сообщит экземпляру «эй, кстати, ты скоро будешь вычищен - пожалуйста, приберись сейчас». Обычно это означает, что в деструкторе есть что-то значимое. Это также означает, что вы пишете класс специально для управления ресурсами, зная, что при определенных трудно предсказуемых обстоятельствах, таких как генерируемые исключения, вы можете рассчитывать на выполнение деструкторов.

Скажем, вы хотите написать некоторый код, в котором вы собираетесь изменить курсор Windows на курсор ожидания (песочные часы, пончик-неработающий и т. Д.), Выполнить свои действия и затем изменить его обратно. И скажите также, что «делай свое дело» может вызвать исключение. RAII способ сделать это - создать класс, ctor которого устанавливает курсор на ожидание, чей единственный «реальный» метод сделал то, что вы хотели, и чей dtor вернул курсор назад. Ресурсы (в данном случае состояние курсора) привязаны к области видимости объекта. Когда вы приобретаете ресурс, вы инициализируете объект. Вы можете рассчитывать на уничтожение объекта при возникновении исключений, а это значит, что вы можете рассчитывать на очистку ресурса.

Использование RAII хорошо означает, что вам не нужно finally. Конечно, он основан на детерминированном разрушении, которого вы не можете иметь в Java. Вы можете получить своего рода детерминированное разрушение в C # и VB.NET using.


4
Я думаю, это то, к чему вы стремитесь, но вы можете добавить, что причина, по которой Java и C # не поддерживают RAII, заключается в сборщике мусора. В C ++ локальный объект будет уничтожен, как только он выйдет из области видимости. В Java / C # это не так.
Джейсон Бейкер

Если говорить о точке зрения Джейсона, то причина, по которой Java и C # не могут гарантировать своевременное уничтожение, заключается в возможности ссылочных циклов, а это означает, что невозможно определить безопасный порядок запуска деструкторов. Референтные циклы также могут происходить в C ++, но последствия различны - программист становится ответственным за определение порядка уничтожения и выполнение явного удаления. Эта ответственность очень часто упакована в некоторые деструкторы классов более высокого уровня - например, контейнерный класс отвечает за обеспечение уничтожения всех содержащихся элементов. «Владение» является ключевым.
Steve314

1
@Jason - это то, что я имел в виду под «детерминированным разрушением» - программист на C ++ знает, когда деструктор запустится.
Кейт Грегори

Я знаю, что это старый ответ, но я все еще в замешательстве. Я только что узнал об этом термине, и некоторая информация говорит о том, что приобретение должно происходить в конструкторе. Это не имеет смысла для меня, и этот ответ, кажется, противоречит этому, но не могли бы вы уточнить?
Пер Йоханссон

1
@PerJohansson Да, вы приобретаете в КТО. А ты отпусти в дтор. Я сосредоточился на втором пункте, но они идут вместе. Когда ctor готов, вы знаете, что у вас есть действующий объект. И вы знаете, что независимо от того, что произойдет, ресурс будет выпущен в нужное время.
Кейт Грегори

4

Частично RAII решает, когда объект становится ответственным за свою собственную очистку - правило состоит в том, что объект становится ответственным, если и когда инициализация его конструктора завершится. Симметрия инициализации и очистки, конструктор и деструктор, означает, что они тесно связаны друг с другом.

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

Но что произойдет, если исключение происходит в конструкторе?

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

Рассмотрим, в частности, конструкторы для данных членов внутри разрушаемого объекта. Если один из них выдает исключение, код вашего основного конструктора не будет работать вообще, но будет иметь некоторый код, который образует неявную часть этого конструктора. Все члены, которые были успешно построены, будут автоматически уничтожены. Любые члены, которые не были созданы (в том числе тот, который вызвал исключение), не являются.

Таким образом, в основном RAII - это политика, которая гарантирует, что все, что будет полностью построено, будет уничтожено своевременно, особенно при наличии исключений, и что любой объект либо будет полностью построен, либо нет (не существует половины построенные объекты, которые вы не можете знать, как безопасно убирать). Выделенные ресурсы также освобождаются. И большая часть работы автоматизирована, поэтому программисту не нужно слишком беспокоиться об этом.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.