C++. Сборник рецептов - [129]
> dev2_(Device(devno2)) { // инициализации
> // Здесь задаются операторы тела конструктора.
> } catch (...) { // catch обработчик задается *после*
> throw; // тела конструктора
> }
Режим работы блоков >try
и >catch
вполне ожидаем; единственное синтаксическое отличие от обычного блока >try
заключается в том, что при перехвате исключений, выброшенных из списка инициализации, за ключевым словом >try
идет двоеточие, затем список инициализации и после этого собственно блок >try
, который является одновременно и телом конструктора. Если какое-нибудь исключение выбрасывается из списка инициализации или из тела конструктора, оно будет перехвачено >catch
-обработчиком, который расположен после тела конструктора. Вы можете при необходимости добавить в тело конструктора дополнительную пару блоков >try/catch
, однако вложенные блоки >try/catch
обычно выглядят непривлекательно.
Кроме перемещения операторов инициализации членов в список инициализации пример 9.3 отличается от примера 9.2 еще одним свойством. Объекты-члены >Device
на этот раз не создаются в динамической памяти с помощью оператора >new
. Я сделал это для иллюстрации двух особенностей, связанных с безопасностью и применением объектов-членов.
Во-первых, использование стека вместо объектов динамической памяти позволяет компилятору автоматически обеспечить их безопасность. Если какой-нибудь объект в списке инициализации выбрасывает исключение в ходе конструирования, занимаемая им память автоматически освобождается по мере раскрутки стека в процессе обработки исключения. Во-вторых, что более важно, любые другие объекты, которые уже были успешно сконструированы, уничтожаются, и вам не требуется перехватывать исключения и явно их удалять оператором >delete
.
Но, возможно, вам требуется иметь члены, использующие динамическую память (или с ними вы предпочитаете иметь дело). Рассмотрим подход, используемый в первоначальном классе >Broker
в примере 9.2. Вы можете просто инициализировать ваши указатели в списке инициализации, не так ли?
>class BrokerBad {
>public:
> BrokerBad(int devno1, int devno2)
> try : dev1_(new Device(devno1)), // Создать объекты динамической
> dev2_(new Device(devno2)) {} // памяти в списке инициализации
> catch (...) {
> if (dev1_) {
> delete dev1_; // He должно компилироваться и
> delete dev2_; // является плохим решением, если
> } // все же будет откомпилировано
> throw; // Повторное выбрасывание того же самого исключения
> }
> ~BrokerBad() {
> delete dev1_;
> delete dev2_;
> }
>private:
> BrokerBad();
> Device* dev1_;
> Device* dev2_;
>};
Нет, так делать нельзя. Здесь две проблемы. Прежде всего, это не допустит ваш компилятор, потому что расположенный в конструкторе блок >catch
не должен позволить программному коду получить доступ к переменным-членам, так как их еще нет. Во-вторых, даже если ваш компилятор позволяет это делать, это будет плохим решением. Рассмотрим ситуацию, когда при конструировании объекта >dev1_
выбрасывается исключение. Ниже дается программный код, который будет выполняться в >catch
-обработчике.
>catch (...) {
> if (dev1_) { // Какое значение имеет эта переменная?
> delete dev1_; // в данном случае вы удаляете неопределенное значение
> delete dev2_;
> }
> throw; // Повторное выбрасывание того же самого исключения
>}
Если исключение выбрасывается в ходе конструирования >dev1_
, то оператором >new
не может быть возвращен адрес нового выделенного участка памяти и значение >dev1_
не меняется. Тогда что эта переменная содержит? Она будет иметь неопределённое значение, так как она никогда не инициализировалась. В результате, когда вы станете выполнять оператор >delete dev1_
, вы, вероятно, попытаетесь удалить объект, используя недостоверный адрес, что приведет к краху программы, вы будете уволены, и вам придется жить с этим позором всю оставшуюся жизнь.
Чтобы избежать такое фиаско, круто изменяющее вашу жизнь, инициализируйте в списке инициализации ваши указатели значением >NULL
и затем создавайте в конструкторе объекты, использующие динамическую память. В этом случае будет легче перехватывать любую исключительную ситуацию и выполнять подчистку, поскольку допускается использовать оператор >delete
для >NULL
-указателей.
>BrokerBetter(int devno1, int devno2) :
> dev1_(NULL), dev2_(NULL) {
> try {
> dev1_ = new Device(devno1);
> dev2_ = new Device(devno2);
> } catch (...) {
> delete dev1_; // Это сработает в любом случае
> throw;
> }
> }
Итак, вышесказанное можно подытожить следующим образом: если вам необходимо использовать члены-указатели, инициализируйте их значением >NULL
в списке инициализации и затем выделяйте в конструкторе память для соответствующих объектов, используя блок >try/catch
. Вы можете освободить любую память в >catch
-обработчике. Однако, если допускается работа с автоматическими членами, сконструируйте их в списке инициализации и используйте специальный синтаксис блока >try/catch
для обработки любых исключений.
Рецепт 9.2.
9.4. Создание безопасных при исключениях функций-членов
Создается функция-член и необходимо обеспечить базовые и строгие гарантии ее безопасности при исключениях, а именно отсутствие утечки ресурсов и то, что объект не будет иметь недопустимое состояние в том случае, если выбрасывается исключение.
Java Enterprise Edition (Java EE) остается одной из ведущих технологий и платформ на основе Java. Данная книга представляет собой логичное пошаговое руководство, в котором подробно описаны многие спецификации и эталонные реализации Java EE 7. Работа с ними продемонстрирована на практических примерах. В этом фундаментальном издании также используется новейшая версия инструмента GlassFish, предназначенного для развертывания и администрирования примеров кода. Книга написана ведущим специалистом по обработке запросов на спецификацию Java EE, членом наблюдательного совета организации Java Community Process (JCP)
Разработчику часто требуется много сторонних инструментов, чтобы создавать и поддерживать проект. Система Git — один из таких инструментов и используется для контроля промежуточных версий вашего приложения, позволяя вам исправлять ошибки, откатывать к старой версии, разрабатывать проект в команде и сливать его потом. В книге вы узнаете об основах работы с Git: установка, ключевые команды, gitHub и многое другое.В книге рассматриваются следующие темы:основы Git;ветвление в Git;Git на сервере;распределённый Git;GitHub;инструменты Git;настройка Git;Git и другие системы контроля версий.
Рассмотрено все необходимое для разработки, компиляции, отладки и запуска приложений Java. Изложены практические приемы использования как традиционных, так и новейших конструкций объектно-ориентированного языка Java, графической библиотеки классов Swing, расширенной библиотеки Java 2D, работа со звуком, печать, способы русификации программ. Приведено полное описание нововведений Java SE 7: двоичная запись чисел, строковые варианты разветвлений, "ромбовидный оператор", NIO2, новые средства многопоточности и др.
Система сборки программ, используемая во FreeBSD, имеет значительно большие возможности, чем те, которые мы задействовали. Какие это возможности и как их использовать в своих портах?
Python - объектно-ориентированный язык сверхвысокого уровня. Python, в отличии от Java, не требует исключительно объектной ориентированности, но классы в Python так просто изучить и так удобно использовать, что даже новые и неискушенные пользователи быстро переходят на ОО-подход.
«Как пасти котов» – это книга о лидерстве и руководстве, о том, как первое совмещать со вторым. Это, если хотите, словарь трудных случаев управления IT-проектами. Программист подобен кошке, которая гуляет сама по себе. Так уж исторически сложилось. Именно поэтому так непросто быть руководителем команды разработчиков. Даже если вы еще месяц назад были блестящим и дисциплинированным программистом и вдруг оказались в роли менеджера, вряд ли вы знаете, с чего надо начать, какой выбрать стиль руководства, как нанимать и увольнять сотрудников, проводить совещания, добиваться своевременного выполнения задач.