Там є чудові відповіді, тому я просто додаю забуті речі.
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: Придбання mutex виконується за ключовим словом ( synchronized
або lock
), а непридбання буде здійснено при виході з області дії.
Це так природно в його позначенні, що майже не потребує пояснень навіть людям, які ніколи не чули про RAII.
Перевага C ++ в порівнянні з Java і C # тут полягає в тому, що все можна зробити за допомогою RAII. Наприклад, немає прямого вбудованого еквівалента synchronized
ані lock
в C ++, але ми все одно можемо їх мати.
У 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
?
[SHOUTING] Це остаточний відлік часу!
- Європа: Фінальний зворотний відлік (вибачте, у мене не було котирувань, тут ... :-)
finally
Пропозиція використовується в C # / Java для обробки утилізації ресурсів у разі виходу області видимості (або через return
або кинуто виключення).
Читачі специфіки проникливості помітили, що C ++ не має остаточного підрозділу. І це не помилка, оскільки C ++ її не потребує, оскільки RAII вже обробляє ресурси для видалення ресурсів. (І повірте, писати деструктор C ++ набагато простіше, ніж писати правильний пункт Java, нарешті, або навіть правильний метод розпорядження C #).
І все-таки іноді finally
застереження було б крутим. Чи можемо ми це зробити на C ++? Так, ми можемо! І знову з почерговим використанням RAII.
Висновок: RAII - це більше, ніж філософія в C ++: це C ++
РАЙ? ЦЕ С ++ !!!
- обурений коментатор розробника C ++, який безсоромно копіював незрозумілий король Спарти та його 300 друзів
Досягнувши певного рівня досвіду роботи на C ++, ви починаєте думати з точки зору RAII , з точки зору автоматизованого виконання конструкторів і деструкторів .
Ви починаєте думати в термінах областей , а також {
і }
персонажі стають одними з найважливіших у вашому коді.
І майже все підходить з точки зору RAII: безпека винятків, мьютекси, підключення до бази даних, запити до бази даних, підключення до сервера, годинник, ручки ОС тощо, і останнє, але не менш важливе значення - пам'ять.
Частина бази даних не є незначною, оскільки, якщо ви погоджуєтесь платити ціну, ви навіть можете писати в стилі " транзакційного програмування ", виконуючи рядки та рядки коду до прийняття рішення, врешті-решт, якщо ви хочете здійснити всі зміни або, якщо це неможливо, повернення всіх змін назад (до тих пір, поки кожен рядок задовольняє принаймні гарантії сильного винятку). (див. другу частину цієї статті Герба Саттера про програмування транзакцій).
І як головоломка, все підходить.
RAII - це стільки частина C ++, C ++ не може бути C ++ без нього.
Це пояснює, чому досвідчені розробники C ++ так захоплені RAII, і чому RAII - це перше, що вони шукають, намагаючись іншу мову.
І це пояснює, чому сміттєзбірник, хоча сам по собі є найбільш важливим елементом технології, не так вражає з точки зору розробника C ++:
- RAII вже обробляє більшість випадків, якими займається GC
- GC краще, ніж RAII, має кругові посилання на чисто керовані об'єкти (пом'якшені розумним використанням слабких покажчиків)
- Але GC обмежений пам'яттю, тоді як RAII може обробляти будь-який ресурс.
- Як описано вище, RAII може зробити набагато, набагато більше ...