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