Строго говоря, функция copy в действительности ничего не выбрасывает, но это делает оператор T::operator=. Это происходит из-за того, что функция copy и остальные алгоритмы стандартной библиотеки в целом являются нейтральными по отношению к исключениям; это значит, что при выбрасывании исключений во время выполнения каких-либо внутренних операторов это исключение будет передано вызывающей программе, а не будет обработано полностью (перехвачено в блоке catch без повторного выбрасывания этого исключения). Это сохраняет возможность перехвата исключений в блоке catch, выполнения некоторой подчистки с последующим их повторным выбрасываний, но в конце концов все исключения, выброшенные в классе или функции стандартной библиотеки, дойдут до вызывающей программы.

Создание безопасных при исключениях функций-членов — трудоемкая работа. Для этого вам необходимо выявить все места, где могут выбрасываться исключения, и убедиться, что вы правильно их обрабатываете. Когда исключение может выбрасываться? При любом вызове функции. Операторы для встроенных типов данных не могут выбрасывать исключения, а деструкторы никогда не должны выбрасывать исключения, не все остальное, будь это отдельная функция, функция-член, оператор, конструктор и т.д., является потенциальным источником исключения. В приводимых примерах 9.5 и 9.6 в классах и функциях используются исключения с ограниченной областью действия. Классы содержат очень мало переменных-членов, и поведение класса носит дискретный характер. По мере увеличения количества функций-членов и переменных-членов, использования наследования и виртуальных функций задача обеспечения их строгой безопасности при исключениях становится более сложной.

Наконец, как и для большинства других требований, предъявляемых к программному обеспечению, вам требуется обеспечить только тот уровень безопасности исключений, который вам необходим. Другими словами, если вы создаете диалоговый мастер по генерации веб-страниц, график вашей разработки, вероятно, не позволит провести необходимое исследование и тестирование обеспечения в нем строгой безопасности исключений. Так, для вашего заказчика может быть приемлемой ситуация, когда пользователи встречаются иногда с сообщением о неопределенной ошибке: «Неизвестная ошибка, аварийное завершение программы» («Unknown error, aborting»). С другой стороны, если вы создаете программное обеспечение для управления углом ротора вертолета, ваш заказчик, вероятно, будет настаивать на обеспечении более существенных гарантий безопасности, чем вывод сообщения «Неизвестная ошибка, аварийное завершение программы».

9.5. Безопасное копирование объекта

Проблема

Требуется иметь безопасные при исключениях конструктор копирования и оператор присваивания базового класса.

Решение

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

Пример 9.6. Безопасные при исключениях оператор присваивания и конструктор копирования

#include <iostream>

#include <string>

const static int DEFAULT_BUF_SIZE = 3;

const Static int MAX_SIZE = 4096;

class Message {

public:

 Message(int bufSize = DEFAULT_BUF_SIZE) :

  bufSize_(bufSize), initBufSize_(bufSize), msgSize_(0), key_('') {

   buf_ = new char[bufSize]; // Примечание: теперь это делается в теле

                            // конструктора

 }

 ~Message() {

  delete[] buf_;

 }

 // Безопасный при исключениях конструктор копирования

 Message(const Message& orig) :

  bufSize_(orig.bufSize_), initBufSize_(orig.initBufSize_),

  msgSize_(orig.msgSize_), key_(orig.key_) {

  // Эта функция может выбросить исключение

  buf_ = new char[orig.bufSize_]; // ...здесь может произойти то же

                                  // самое

  copy(orig.buf_, orig.buf_+msgSize_, buf_); // Здесь нет

 }

 // Безопасный при исключениях оператор присваивания использующий

 // конструктор копирования

 Message& operator=(const Message& rhs) {

  Message tmp(rhs); // Копировать сообщение во временную переменную,

                    // используя конструктор копирования

  swapInternals(tmp); // Обменять значения переменных-членов и членов

                      // временного объекта

  return(*this); // После выхода переменная tmp уничтожается вместе

                 // с первоначальными данными

 }

 const char* data() {

  return(buf_);

 }

private:

 void swapInternals(Messages msg) {

  // Поскольку key_ не является встроенным типом данных, он может

  // выбрасывать исключение, поэтому сначала выполняем действия с ним

  swap(key_, msg.key_);

  // Если предыдущий оператор не выбрасывает исключение, то выполняем

  // действия со всеми переменными-членами, которые являются встроенными

  // типами

  swap(bufSize_, msg.bufSize_);

  swap(initBufSize_, msg.initBufSize_);

  swap(msgSize_, msg.msgSize_);

Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

Вы можете отметить интересные вам фрагменты текста, которые будут доступны по уникальной ссылке в адресной строке браузера.

Отметить Добавить цитату