C++. Сборник рецептов - [128]

Шрифт
Интервал

гарантией безопасности исключений, обеспечивает неизменность состояния объекта, если операция завершается неудачно. Последнее относится к операциям, которые следуют после конструирования объекта, поскольку по определению объект, который выбрасывает исключение, всегда будет сконструирован не полностью и поэтому всегда будет иметь недостоверное состояние. Я вернусь к функциям-членам в рецепте 9.4. Теперь же давайте основное внимание уделим конструированию объектов.

В примере 9.2 определяется два класса, >Device и >Broker, которые делают не очень много, но с их помощью можно было бы легко представить любой сценарий работы пары устройство/брокер, когда вы имеете некоторый класс, который открывает соединение к каждому из двух устройств и управляет связью между ними. Брокер бесполезен, если доступно только одно устройство, поэтому семантика обработки транзакций при наличии брокера должна учитывать, что при выбрасывании исключения одним из двух этих устройств, когда делается попытка получения доступа к нему, должно освобождаться другое устройство. Это обеспечивает невозможность утечки памяти и других ресурсов.

Блоки >try и >catch сделают эту работу. В конструкторе заключите операторы по выделению динамической памяти для объекта в блок >try и перехватывайте все исключения, которые выбрасываются в ходе конструирования этого объекта.

>try {

> dev1_ = new Device(devno1);

> dev2_ = new Device(devno2);

>} catch (...) {

> delete dev1_;

> throw;

>}

Многоточие в обработчике >catch означает, что любое выброшенное исключение будет перехвачено. В данном случае вам следует поступать именно так, поскольку вы лишь освобождаете память, если что-то не получилось, и затем повторно выбрасываете исключение независимо от его типа. Вам необходимо повторно выбросить исключение, чтобы клиентская программа, которая пытается инстанцировать объект >Broker, могла сделать что-то полезное с исключением, например записать куда-нибудь соответствующее сообщение об ошибке.

В >catch-обработчике я удаляю лишь >dev1_, так как последнее выбрасывание исключения возможно только в операторе >new для >dev2_. Если он выбрасывает исключение, то переменной >dev2_ не будет присвоено никакого значения и, следовательно, мне не нужно удалять объект >dev2_. Однако, если вы что-то делаете после инициализации >dev2_, вам потребуется выполнить зачистку этого объекта. Например:

>try {

> dev1_ = new Device(devno1);

> dev2_ = new Device(devno2);

> foo_ = new MyClass(); // Может выбросить исключение

>} catch (...) {

> delete dev1_;

> delete dev2_;

> throw;

>}

В этом случае вам не следует беспокоиться об удалении указателей, которым никогда не присваивались реальные значения (если изначально вы не инициализировали их соответствующим образом), поскольку удаление указателя >NULL не дает никакого эффекта. Другими словами, если присваивание значения переменной >dev1_ приводит к выбрасыванию исключения, ваш >catch-обработчик все же выполнит оператор >delete dev2_, однако все будет нормально, если вы инициализировали его значением >NULL в списке инициализации.

Как я говорил в рецепте 9.1, рассматривая основы обработки исключений, для обеспечения гибкой стратегии обработки исключений может потребоваться особая ловкость, и то же самое относится к обеспечению безопасности исключений. Подробное рассмотрение методов проектирования программного кода, безопасного при исключениях, приводится в книге «Exceptional С++», написанной Гербом Саттером (Herb Sutter) (издательство «Addison Wesley»).

Смотри также

Рецепт 9.3.

9.3. Создание безопасного при исключениях списка инициализации

Проблема

Необходимо инициализировать ваши данные-члены в списке инициализации конструктора, и поэтому вы не можете воспользоваться подходом, описанным в рецепте 9.2.

Решение

Используйте специальный формат блоков >try и >catch, предназначенный для перехвата исключений, выбрасываемых в списке инициализации. Пример 9.3 показывает, как это можно сделать.

Пример 9.3. Обработка исключений в списке инициализации

>#include

>#include


>using namespace std;


>// Некоторое устройство

>class Device {

>public:

> Device(int devno) {

>  if (devno == 2)

>   throw runtime error("Big problem");

> }

> ~Device() {}

>private:

> Device();

>};


>class Broker {

>public:

> Broker (int devno1, int devno2)

>  try : dev1_(Device(devno1)), // Создать эти объекты в списке

>   dev2_(Device(devno2)) {}    // инициализации

>  catch (...) {

>   throw; // Выдать сообщение в журнал событий или передать ошибку

>          // вызывающей программе (см. ниже обсуждение)

> }

> ~Broker() {}

>private:

> Broker();

> Device dev1_;

> Device dev2_;

>};


>int main() {

> try {

>  Broker b(1, 2);

> } catch(exception& e) {

>  cerr << "Exception: " << e.what() << endl;

> }

>}

Обсуждение

Синтаксис обработки исключений в списках инициализации немного отличается от традиционного синтаксиса С++, потому что здесь блок >try используется в качестве тела конструктора. Критической частью примера 9.3 является конструктор класса >Broker.

>Broker(int devno1, int devno2) // Заголовок конструктора такой же Constructor

> try :                         // Действует так же, как try {...}

>  dev1_(Device(devno1)),       // Затем идут операторы списка


Рекомендуем почитать
Изучаем Java EE 7

Java Enterprise Edition (Java EE) остается одной из ведущих технологий и платформ на основе Java. Данная книга представляет собой логичное пошаговое руководство, в котором подробно описаны многие спецификации и эталонные реализации Java EE 7. Работа с ними продемонстрирована на практических примерах. В этом фундаментальном издании также используется новейшая версия инструмента GlassFish, предназначенного для развертывания и администрирования примеров кода. Книга написана ведущим специалистом по обработке запросов на спецификацию Java EE, членом наблюдательного совета организации Java Community Process (JCP)


Pro Git

Разработчику часто требуется много сторонних инструментов, чтобы создавать и поддерживать проект. Система Git — один из таких инструментов и используется для контроля промежуточных версий вашего приложения, позволяя вам исправлять ошибки, откатывать к старой версии, разрабатывать проект в команде и сливать его потом. В книге вы узнаете об основах работы с Git: установка, ключевые команды, gitHub и многое другое.В книге рассматриваются следующие темы:основы Git;ветвление в Git;Git на сервере;распределённый Git;GitHub;инструменты Git;настройка Git;Git и другие системы контроля версий.


Java 7

Рассмотрено все необходимое для разработки, компиляции, отладки и запуска приложений Java. Изложены практические приемы использования как традиционных, так и новейших конструкций объектно-ориентированного языка Java, графической библиотеки классов Swing, расширенной библиотеки Java 2D, работа со звуком, печать, способы русификации программ. Приведено полное описание нововведений Java SE 7: двоичная запись чисел, строковые варианты разветвлений, "ромбовидный оператор", NIO2, новые средства многопоточности и др.


Создаем порт для FreeBSD своими руками. Часть II

Система сборки программ, используемая во FreeBSD, имеет значительно большие возможности, чем те, которые мы задействовали. Какие это возможности и как их использовать в своих портах?


Питон — модули, пакеты, классы, экземпляры

Python - объектно-ориентированный язык сверхвысокого уровня. Python, в отличии от Java, не требует исключительно объектной ориентированности, но классы в Python так просто изучить и так удобно использовать, что даже новые и неискушенные пользователи быстро переходят на ОО-подход.


Как пасти котов. Наставление для программистов, руководящих другими программистами

«Как пасти котов» – это книга о лидерстве и руководстве, о том, как первое совмещать со вторым. Это, если хотите, словарь трудных случаев управления IT-проектами. Программист подобен кошке, которая гуляет сама по себе. Так уж исторически сложилось. Именно поэтому так непросто быть руководителем команды разработчиков. Даже если вы еще месяц назад были блестящим и дисциплинированным программистом и вдруг оказались в роли менеджера, вряд ли вы знаете, с чего надо начать, какой выбрать стиль руководства, как нанимать и увольнять сотрудников, проводить совещания, добиваться своевременного выполнения задач.