Як ви, мабуть, вже здогадувались, так, C ++ надає ті самі можливості без цього механізму. Таким чином, строго кажучи, try
/ finally
механізм насправді не потрібен.
Однак сказане, що без цього не висуває певних вимог до способу оформлення іншої мови. У C ++ той самий набір дій втілено в деструкторі класу. Це працює в першу чергу (виключно?), Оскільки виклик деструктора в C ++ є детермінованим. Це, в свою чергу, призводить до деяких досить складних правил щодо життя об'єктів, деякі з яких, очевидно, не інтуїтивні.
Більшість інших мов натомість надають певну форму збору сміття. Хоча про збирання сміття є суперечливі речі (наприклад, його ефективність щодо інших методів управління пам'яттю), одне взагалі не полягає в тому: точний час, коли об'єкт буде "очищений" сміттєзбірником, не прив'язується безпосередньо до обсягу об’єкта. Це запобігає його використанню, коли очищення потрібно детермінізувати або тоді, коли це просто потрібно для правильної роботи, або при роботі з настільки дорогоцінними ресурсами, що їх очищення більшість не затримується довільно. try
/ finally
надає спосіб таким мовам впоратися з тими ситуаціями, які потребують детермінованого очищення.
Я думаю, що ті, хто стверджує, що синтаксис C ++ для цієї можливості є "менш дружелюбним", ніж Java, швидше відсутній. Гірше, що вони пропускають набагато важливіший момент щодо розподілу відповідальності, який виходить далеко за синтаксис і має набагато більше спільного з тим, як розроблений код.
У C ++ ця детермінована очистка відбувається в деструкторі об'єкта. Це означає, що об’єкт може бути (і зазвичай повинен бути) призначений для очищення після себе. Це доходить до суті об'єктно-орієнтованого дизайну - клас повинен бути розроблений для забезпечення абстракції та застосування власних інваріантів. У C ++ людина робить саме це - і одна з інваріантів, для якої він передбачає, що при знищенні об'єкта ресурси, керовані цим об'єктом (усі вони, а не лише пам'ять), будуть зруйновані правильно.
Java (і подібні) дещо відрізняються. Хоча вони (на зразок) підтримують finalize
те, що теоретично могло б забезпечити подібні можливості, підтримка настільки слабка, що вона в основному непридатна (і насправді ніколи не використовується).
Як результат, замість того, щоб сам клас міг зробити необхідну очистку, клієнтові класу необхідно вжити заходів для цього. Якщо ми зробимо достатньо короткозорий порівняння, на перший погляд може виявитися, що ця різниця є досить незначною, і Java є досить конкурентоспроможною щодо C ++ у цьому відношенні. Ми закінчуємо щось подібне. У C ++ клас виглядає приблизно так:
class Foo {
// ...
public:
void do_whatever() { if (xyz) throw something; }
~Foo() { /* handle cleanup */ }
};
... і клієнтський код виглядає приблизно так:
void f() {
Foo f;
f.do_whatever();
// possibly more code that might throw here
}
У Java ми обмінюємо трохи більше коду, де об’єкт використовується трохи менше у класі. Це спочатку виглядає як досить рівномірна. Насправді це далеко не все, тому що в більшості типових кодів ми визначаємо клас лише в одному місці, але ми використовуємо його у багатьох місцях. Підхід C ++ означає, що ми пишемо лише цей код для обробки очищення в одному місці. Підхід Java означає, що нам потрібно писати цей код для обробки очищення багато разів, у багатьох місцях - у кожному місці, де ми використовуємо об'єкт цього класу.
Коротше кажучи, підхід Java в основному гарантує, що багато абстракцій, які ми намагаємося надати, є "протікаючими" - будь-який клас, який потребує детермінованого очищення, зобов'язує клієнта класу знати про деталі, що потрібно очистити та як зробити очищення. , а не ті деталі, які ховаються в самому класі.
Хоча я називав це "підходом до Java" вище, try
/ finally
і подібні механізми під іншими іменами не повністю обмежені лише Java. Для одного видатного прикладу більшість (усіх?) Мов .NET (наприклад, C #) надають те саме.
Останні ітерації як Java, так і C # також забезпечують щось на півдорозі між "класичною" Java та C ++. У C # об’єкт, який хоче автоматизувати очищення, може реалізувати IDisposable
інтерфейс, який забезпечує Dispose
метод, який (принаймні неясно) схожий на деструктор C ++. Хоча це можна використовувати через try
/ finally
як у Java, C # трохи більше автоматизує завдання за допомогою using
заяви, яка дозволяє визначати ресурси, які будуть створені як область введення, і знищується при виході області. Хоча це все ще не вистачає рівня автоматизації та визначеності, що надається C ++, це все ще суттєве поліпшення порівняно з Java. Зокрема, класний дизайнер може централізувати деталі того, якрозпоряджатися класом при його реалізації IDisposable
. Все, що залишилося для клієнтського програміста, - це менший тягар для написання using
заяви, щоб переконатися, що IDisposable
інтерфейс буде використаний, коли він повинен бути. У Java 7 та новіших версіях назви були змінені для захисту винних, але основна ідея в основному однакова.