Там есть отличные ответы, поэтому я просто добавляю некоторые забытые вещи.
0. RAII о масштабах
RAII о обоих:
- получение ресурса (независимо от того, какой ресурс) в конструкторе и удаление его в деструкторе.
- с конструктором, выполняемым при объявлении переменной, и деструктором, автоматически выполняемым, когда переменная выходит из области видимости.
Другие уже ответили об этом, поэтому я не буду уточнять.
1. При кодировании на Java или C # вы уже используете RAII ...
Мсье Журден: Что! Когда я говорю: «Николь, принеси мне мои тапочки,
и дай мне мой колпак ", это проза?
ФИЛОСОФСКИЙ МАСТЕР: Да, сэр.
Мсье Журден: Более сорока лет я говорил прозой, ничего не зная об этом, и я очень благодарен вам за то, что вы научили меня этому.
- Мольер: джентльмен среднего класса, акт 2, сцена 4
Как месье Журден сделал с прозой, C # и даже Java люди уже используют RAII, но скрытно. Например, следующий код Java (который написан так же в C # путем замены synchronized
на lock
):
void foo()
{
// etc.
synchronized(someObject)
{
// if something throws here, the lock on someObject will
// be unlocked
}
// etc.
}
... уже использует RAII: получение мьютекса выполняется по ключевому слову (synchronized
или lock
), и отмена будет выполнена при выходе из области действия.
Это так естественно в его обозначениях, что почти не требует объяснений даже для людей, которые никогда не слышали о RAII.
Преимущество C ++ перед Java и C # в том, что с помощью RAII можно сделать все что угодно. Например, в C ++ нет прямого встроенного эквивалента synchronized
или lock
, но мы все равно можем их иметь.
В C ++ было бы написано:
void foo()
{
// etc.
{
Lock lock(someObject) ; // lock is an object of type Lock whose
// constructor acquires a mutex on
// someObject and whose destructor will
// un-acquire it
// if something throws here, the lock on someObject will
// be unlocked
}
// etc.
}
, который можно легко написать способом Java / C # (с использованием макросов C ++):
void foo()
{
// etc.
LOCK(someObject)
{
// if something throws here, the lock on someObject will
// be unlocked
}
// etc.
}
2. RAII имеют альтернативное использование
БЕЛЫЙ КРОЛИК: [поет] Я опаздываю / опаздываю / На очень важную дату. / Нет времени говорить "Привет". / Прощай. / Я опоздал, я опоздал, я опоздал.
- Алиса в стране чудес (версия Диснея, 1951)
Вы знаете, когда будет вызван конструктор (при объявлении объекта), и вы знаете, когда будет вызван соответствующий ему деструктор (при выходе из области видимости), так что вы можете написать почти волшебный код, кроме строки. Добро пожаловать в страну чудес C ++ (по крайней мере, с точки зрения разработчика C ++).
Например, вы можете написать встречный объект (я позволю это в качестве упражнения) и использовать его, просто объявив его переменную, как был использован объект блокировки выше:
void foo()
{
double timeElapsed = 0 ;
{
Counter counter(timeElapsed) ;
// do something lengthy
}
// now, the timeElapsed variable contain the time elapsed
// from the Counter's declaration till the scope exit
}
что, конечно же, может быть написано, опять же, способом Java / C # с использованием макроса:
void foo()
{
double timeElapsed = 0 ;
COUNTER(timeElapsed)
{
// do something lengthy
}
// now, the timeElapsed variable contain the time elapsed
// from the Counter's declaration till the scope exit
}
3. Почему C ++ не хватает finally
?
[КУРИТЬ] Это окончательный обратный отсчет!
- Европа: последний обратный отсчет (извините, здесь нет цитат, здесь ...: -)
Предложение finally
используется в C # / Java для обработки удаления ресурсов в случае выхода из области (либо через return
, либо через выброшенное исключение).
Внимательные читатели спецификаций заметят, что в C ++ нет предложения finally. И это не ошибка, потому что C ++ не нуждается в этом, так как RAII уже обрабатывает удаление ресурсов. (И поверьте мне, написание деструктора C ++ намного проще, чем написание правильного предложения Java finally или даже правильного метода Dispose в C #).
Тем не менее иногда предложение finally
было бы круто. Можем ли мы сделать это в C ++? Да, мы можем! И снова с альтернативным использованием RAII.
Вывод: RAII - это больше, чем философия в C ++: это C ++
RAII? ЭТО С ++ !!!
- возмущенный комментарий разработчика C ++, беззастенчиво скопированный неизвестным королем Спарты и его 300 друзьями
WheКогда вы достигаете некоторого уровня опыта в C ++, вы начинаете думать с точки зрения RAII , с точки зрения конструкторов и деструкторов автоматического выполнения .
Вы начинаете думать в терминах областей действия , и символы {
и }
становятся одними из самых важных в вашем коде.
И с точки зрения RAII почти все подходит: безопасность исключений, мьютексы, подключения к базе данных, запросы к базе данных, подключение к серверу, часы, дескрипторы ОС и т. Д., И, что не менее важно, память.
Часть базы данных не пренебрежимо мала, так как, если вы согласитесь заплатить цену, вы даже можете написать в стиле « транзакционное программирование », выполняя строки и строки кода до принятия решения, в конце концов , если вы хотите зафиксировать все изменения или, если это невозможно, вернуть все изменения обратно (при условии, что каждая строка удовлетворяет хотя бы строгой гарантии исключения). (см. вторую часть этой статьи Саттера Херба для транзакционного программирования).
И как пазл, все подходит.
RAII - это большая часть C ++, C ++ не может быть C ++ без него.
Это объясняет, почему опытные разработчики C ++ так влюблены в RAII, и почему RAII - это первое, что они ищут, когда пробуют другой язык.
И это объясняет, почему сборщик мусора, хотя сам по себе является великолепной технологией, не так впечатляет с точки зрения разработчика C ++:
- RAII уже обрабатывает большинство дел, обрабатываемых GC
- GC работает лучше, чем RAII, с циклическими ссылками на чисто управляемые объекты (смягчается умным использованием слабых указателей)
- Тем не менее GC ограничен памятью, в то время как RAII может обрабатывать любые виды ресурсов.
- Как описано выше, RAII может многое, намного больше ...