Вход/Регистрация
Программирование. Принципы и практика использования C++ Исправленное издание
вернуться

Страуструп Бьерн

Шрифт:

• Жесткая гарантия. Если кроме базовой гарантии, функция также гарантирует, что все наблюдаемые значения (т.е. все значения, не являющиеся локальными по отношению к этой функции) после отказа восстанавливают свои предыдущие значения, то говорят, что такая функция дает жесткую гарантию (strong guarantee). Жесткая гарантия — это идеал для функции: либо функция будет выполнена так, как ожидалось, либо ничего не произойдет, кроме генерирования исключения, означающего отказ.

• Гарантия отсутствия исключений (no-throw guarantee). Если бы мы не могли выполнять простые операции без какого бы то ни было риска сбоя и без генерирования исключений, то не могли бы написать код, соответствующий условиям базовой и жесткой гарантии. К счастью, практически все встроенные средства языка С++ поддерживают гарантию отсутствия исключений: они просто не могут их генерировать. Для того чтобы избежать генерирования исключений, просто не выполняйте оператор

throw
,
new
и не применяйте оператор dynamic_cast к ссылочным типам (раздел A.5.7).

Для анализа правильности программы наиболее полезными являются базовая и жесткая гарантии. Принцип RAII играет существенную роль для реализации простого и эффективного кода, написанного в соответствии с этими идеями. Более подробную информацию можно найти в приложении Д книги Язык программирования С++.

Естественно, всегда следует избегать неопределенных (и обычно опасных) операций, таких как разыменования нулевого указателя, деление на нуль и выход за пределы допустимого диапазона. Перехват исключений не отменяет фундаментальные правила языка.

19.5.4. Класс auto_ptr

Итак, функции, такие как

make_vec
, подчиняются основным правилам корректного управления ресурсами с использованием исключений. Это обеспечивает выполнение базовой гарантии, которую должны давать все правильные функции при восстановлении работы программы после генерирования исключений. Если не произойдет чего-либо катастрофического с нелокальными данными в той части программы, которая ответственна за заполнение вектора данными, то можно даже утверждать, что такие функции дают жесткую гарантию. Однако этот блок
try ... catch
по-прежнему выглядит ужасно. Решение очевидно: нужно как-то применить принцип RAII; иначе говоря, необходимо предусмотреть объект, который будет владеть объектом класса
vector<int>
и сможет его удалить, если возникнет исключение. В заголовке
<memory>
стандартной библиотеки содержится класс
auto_ptr
, предназначенный именно для этого.

vector<int>* make_vec // создает заполненный вектор

{

auto_ptr< vector<int> > p(new vector<int>); // выделяет свободную

// память

// ...заполняем вектор данными;

// возможна генерация исключения...

return p.release; // возвращаем указатель,

// которым владеет объект p

}

Объект класса

auto_ptr
просто владеет указателем в функции. Он немедленно инициализируется указателем, созданным с помощью оператора
new
. Теперь мы можем применять к объектам класса
auto_ptr
операторы
–>
и
*
как к обычному указателю (например,
p–> at(2)
или
(*p).at(2)
), так что объект класса
auto_ptr
можно считать разновидностью указателя. Однако не спешите копировать класс
auto_ptr
, не прочитав соответствующей документации; семантика этого класса отличается от семантики любого типа, который мы до сих пор встречали. Функция
release
вынуждает объект класса
auto_ptr
вернуть обычный указатель обратно, так что мы можем вернуть этот указатель, а объект класса
auto_ptr
не сможет уничтожить объект, на который установлен возвращаемый указатель. Если вам не терпится использовать класс
auto_ptr
в более интересных ситуациях (например, скопировать его объект), постарайтесь преодолеть соблазн. Класс
auto_ptr
предназначен для того, чтобы владеть указателем и гарантировать уничтожение объекта при выходе из области видимости. Иное использование этого класса требует незаурядного мастерства. Класс
auto_ptr
представляет собой очень специализированное средство, обеспечивающее простую и эффективную реализацию таких функций, как
make_vec
. В частности, класс
auto_ptr
позволяет нам повторить наш совет: с подозрением относитесь к явному использованию блоков
try
; большинство из них вполне можно заменить, используя одно из применений принципа RAII.

19.5.5. Принцип RAII для класса vector

Даже использование интеллектуальных указателей, таких как

auto_ptr
, может показаться недостаточно безопасным. Как убедиться, что мы выявили все указатели, требующие защиты? Как убедиться, что мы освободили все указатели, которые не должны были уничтожаться в конце области видимости? Рассмотрим функцию
reserve
из раздела 19.3.5.

template<class T, class A>

void vector<T,A>::reserve(int newalloc)

{

if (newalloc<=space) return; // размер никогда не уменьшается

T* p = alloc.allocate(newalloc); // выделяем новую память

for (int i=0; i<sz; ++i) alloc.construct(&p[i],elem[i]);

// копируем

for (int i=0; i<sz; ++i) alloc.destroy(&elem[i]); // уничтожаем

  • Читать дальше
  • 1
  • ...
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • ...

Ебукер (ebooker) – онлайн-библиотека на русском языке. Книги доступны онлайн, без утомительной регистрации. Огромный выбор и удобный дизайн, позволяющий читать без проблем. Добавляйте сайт в закладки! Все произведения загружаются пользователями: если считаете, что ваши авторские права нарушены – используйте форму обратной связи.

Полезные ссылки

  • Моя полка

Контакты

  • chitat.ebooker@gmail.com

Подпишитесь на рассылку: