Чтение онлайн

на главную

Жанры

Эффективное использование C++. 55 верных способов улучшить структуру и код ваших программ

Майерс Скотт

Шрифт:

Mutex mutex; // мьютекс объекта

Image *bgImage; // текущая фоновая картинка

int imageChanges; // сколько раз картинка менялась

};

Рассмотрим следующую возможную реализацию функции-члена change-Background:

void PrettyMenu::changeBackground(std::istream& imgSrc)

{

lock(&mutex); // захватить мьютекс

delete bgImage; //
избавиться от старой картинки

++imageChanges; // обновить счетчик изменений картинки

bgImage = new Image(imgSrc); // установить новый фон

unlock(&mutex); // освободить мьютекс

}

С точки зрения безопасности исключений, эта функция настолько плоха, насколько вообще возможно. К безопасности исключений предъявляется два требования, и она не удовлетворяет ни одному из них.

Когда возбуждается исключение, то безопасная относительно исключений функция:

Не допускает утечки ресурсов. Приведенный код не проходит этот тест, потому что если выражение «new Image(imgSrc)» возбудит исключение, то вызов unlock никогда не выполнится, и мьютекс окажется захваченным навсегда.

Не допускает повреждения структур данных. Если «new Image(imgSrc)» возбудит исключение, в bgImage останется указатель на удаленный объект. Кроме того, счетчик imageChanges увеличивается, несмотря на то что новая картинка не установлена. (С другой стороны, старая картинка уже полностью удалена, так что трудно сделать вид, будто ничего не изменилось.)

Справиться с утечкой ресурсов легко – в правиле 13 объяснено, как пользоваться объектами, управляющими ресурсами, а в правиле 14 представлен класс Lock, гарантирующий своеременное освобождение мьютексов:

void PrettyMenu::changeBackground(std::istream& imgSrc)

{

Lock ml(mutex); // из правила 14: захватить мьютекс

// и гарантировать его последующее освобождение

delete bgImage;

++imageChanges;

bgImage = new Image(imgSrc);

}

Одним из преимуществ классов для управления ресурсами, подобных Lock, является то, что обычно они уменьшают размер функций. Заметили, что вызов unlock уже не нужен? Общее правило гласит: чем меньше кода, тем лучше, потому что меньше возможностей для ошибок и меньше путаницы при внесении изменений.

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

Безопасные относительно исключений функции предоставляют одну из трех гарантий.

• Функции, предоставляющие базовую гарантию, обещают, что если исключение будет возбуждено, то все в программе остается в корректном состоянии. Никакие объекты или структуры данных не повреждены, и все объекты находятся в непротиворечивом состоянии (например, все инварианты классов не нарушены). Однако точное состояние программы может быть непредсказуемо. Например, мы можем написать функцию change-Background так, что при возникновении исключения объект PrettyMenu сохранит старую фоновую картинку либо у него будет какой-то фон по умолчанию, но пользователи не могут заранее знать, какой. (Чтобы выяснить это, им придется вызвать какую-то функцию-член, которая сообщит, какая сейчас используется картинка.)

• Функции, предоставляющие строгую гарантию, обещают, что если исключение будет возбуждено, то состояние программы не изменится. Вызов такой функции является атомарным; если он завершился успешно, то все запланированные действия выполнены до конца, если же нет, то программа останется в таком состоянии, как будто функция никогда не вызывалась.

Работать с функциями, представляющими такую гарантию, проще, чем с функциями, которые дают только базовую гарантию, потому что после их вызова может быть только два состояния программы: то, которое ожидается в результате ее успешного завершения, и то, которое было до ее вызова. Напротив, если исключение возникает в функции, представляющей только базовую гарантию, то программа может оказаться в любом корректном состоянии.

• Функции, предоставляющие гарантию отсутствия исключений, обещают никогда не возбуждать исключений, потому что всегда делают то, что должны делать. Все операции над встроенными типами (например, целыми, указателями и т. п.) обеспечивают такую гарантию. Это основной строительный блок безопасного относительно исключений кода. Разумно предположить, что функции с пустой спецификацией исключений не возбуждают их, но это не всегда так. Например, рассмотрим следующую функцию:

int doSomething throw; // обратите внимание на пустую

// спецификацию исключений

Это объявление не говорит о том, что doSomething никогда не возбуждает исключений. Утверждается лишь, что если doSomething возбудит исключение, значит, произошла серьезная ошибка и должна быть вызвана функция unexpected [3] . Фактически doSomething может вообще не представлять никаких гарантий относительно исключений. Объявление функции (включающее ее спецификацию исключений) ничего не сообщает относительно того, является ли она корректной, переносима, эффективной, какие гарантии безопасности исключений она предоставляет и предоставляет ли их вообще. Все эти характеристики определяются реализацией функции, а не ее объявлением.

3

Более подробную информацию о функции unexpected вы можете найти, воспользовавшись своим любимым поисковым сервисом или в полном руководстве по языку C++ (возможно, стоит поискать информацию о функции set_unexpected, которая специфицирует unexpected).

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

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

Совок 2

Агарев Вадим
2. Совок
Фантастика:
альтернативная история
7.61
рейтинг книги
Совок 2

Сама себе хозяйка

Красовская Марианна
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Сама себе хозяйка

СД. Том 17

Клеванский Кирилл Сергеевич
17. Сердце дракона
Фантастика:
боевая фантастика
6.70
рейтинг книги
СД. Том 17

Первый пользователь. Книга 3

Сластин Артем
3. Первый пользователь
Фантастика:
боевая фантастика
рпг
5.00
рейтинг книги
Первый пользователь. Книга 3

Хочу тебя навсегда

Джокер Ольга
2. Люби меня
Любовные романы:
современные любовные романы
5.25
рейтинг книги
Хочу тебя навсегда

Адъютант

Демиров Леонид
2. Мания крафта
Фантастика:
фэнтези
6.43
рейтинг книги
Адъютант

Убивать, чтобы жить

Бор Жорж
1. УЧЖ
Фантастика:
героическая фантастика
боевая фантастика
рпг
5.00
рейтинг книги
Убивать, чтобы жить

Измена. Испорченная свадьба

Данич Дина
Любовные романы:
современные любовные романы
короткие любовные романы
5.00
рейтинг книги
Измена. Испорченная свадьба

Я – Орк. Том 3

Лисицин Евгений
3. Я — Орк
Фантастика:
юмористическое фэнтези
попаданцы
5.00
рейтинг книги
Я – Орк. Том 3

Крестоносец

Ланцов Михаил Алексеевич
7. Помещик
Фантастика:
героическая фантастика
попаданцы
альтернативная история
5.00
рейтинг книги
Крестоносец

Совок – 3

Агарев Вадим
3. Совок
Фантастика:
фэнтези
детективная фантастика
попаданцы
7.92
рейтинг книги
Совок – 3

С Д. Том 16

Клеванский Кирилл Сергеевич
16. Сердце дракона
Фантастика:
боевая фантастика
6.94
рейтинг книги
С Д. Том 16

Назад в СССР: 1986 Книга 5

Гаусс Максим
5. Спасти ЧАЭС
Фантастика:
попаданцы
альтернативная история
5.75
рейтинг книги
Назад в СССР: 1986 Книга 5

Ретроградный меркурий

Рам Янка
4. Серьёзные мальчики в форме
Любовные романы:
современные любовные романы
5.00
рейтинг книги
Ретроградный меркурий