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

на главную

Жанры

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

Майерс Скотт

Шрифт:

... // скопировать локальное состояние

f1;

f2;

... // обменять модифицированное состояние с копией

}

Должно быть ясно, что если f1 или f2 не обеспечивают строгих гарантий безопасности исключений, то будет трудно обеспечить ее и для someFunc в целом. Например, предположим, что f1 обеспечивает только базовую гарантию. Чтобы someFunc обеспечивала строгую гарантию, необходимо написать код, определяющий состояние всей программы до вызова f1, перехватить

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

Ситуация не становится существенно лучше, если и f1, и f2 обеспечивают строгую гарантию безопасности исключений. Ведь если f1 нормально доработает до конца, состояние программы может измениться произвольным образом, поэтому если f2 возбудит исключение, то состояние программы не будет тем же, как перед вызовом someFunc, даже если f2 не изменит ничего.

Проблема в побочных эффектах. До тех пор пока функция оперирует только локальным состоянием (то есть someFunc влияет только на состояние объекта, для которого вызвана), относительно легко обеспечить строгую гарантию. Но когда функция имеет побочные эффекты, затрагивающие нелокальные данные, все становится сложнее. Если, например, побочным эффектом вызова f1 является модификация базы данных, будет трудно обеспечить строгую гарантию для someFunc. Не существует способа отменить модификацию базы данных, которая уже была совершена: другие клиенты могли уже увидеть новое состояние.

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

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

Совсем другое дело, если вы пишете функцию, которая вообще не представляет никаких гарантий безопасности исключений. Тут вступает в силу презумпция виновности: подсудимый считается виновным, пока не докажет обратного. Вы должны писать код, безопасный относительно исключений. Однако у вас есть право на защиту. Рассмотрим еще раз реализацию функции someFunc, которая вызывает f1 и f2. Предположим, что f2 не представляет никаких гарантий безопасности исключений, даже базовой. Это значит, что если f2 возбудит исключение, то возможна утечка ресурсов внутри f2. Это также означает, что f2 может повредить структуры данных, например отсортированные массивы могут стать неотсортированными, объект, который копировался из одной структуры в другую, может потеряться и т. д. Функция someFunc ничего не может с этим поделать. Если вызываемые из someFunc функции не гарантируют безопасности относительно исключений, то и someFunc не может предоставить никаких гарантий.

Вот теперь мы можем вернуться к теме беременности. Женщина либо беременна, либо нет. Невозможно быть чуть-чуть беременной. Аналогично программная система является либо безопасной по исключениям, либо нет. Нет такого понятия, как частично безопасная система. Если система имеет всего одну небезопасную относительно исключений функцию, то она небезопасна и в целом, потому что вызов этой функции может привести к утечке ресурсов и повреждению структур данных. К несчастью, большинство унаследованного кода на C++ было написано без учета требований безопасности исключений, поэтому многие системы на сегодня являются в этом отношении небезопасными. Они включают код, написанный в небезопасной манере.

Но нет причин сохранять такое положение дел навсегда. При написании нового кода или модификации существующего тщательно продумывайте способы достижения безопасности исключений. Начните с применения объектов управления ресурсами (см. правило 13). Это предотвратит утечку ресурсов. Затем определите, какую максимальную из трех гарантий безопасности исключений вы можете обеспечить для разрабатываемых функций, оставляя их небезопасными только в том случае, когда вызовы унаследованного кода не оставляют другого выбора. Документируйте ваши решения как для пользователей ваших функций, так и для сопровождения в будущем. Гарантия безопасности исключений функции – это видимая часть ее интерфейса, поэтому вы должны подходить к ней столь же ответственно, как и к другим аспектам интерфейса.

Сорок лет назад код, изобилующий операторами goto, считался вполне приемлемым. Теперь же мы стараемся писать структурированные программы. Двенадцать лет назад глобальные данные ни у кого не вызывали возражений. Теперь мы стремимся данные инкапсулировать. Десять лет назад написание функций без учета влияния исключений было нормой. А сейчас мы боремся за достижение безопасности относительно исключений.

Времена меняются. Мы живем. Мы учимся.

Что следует помнить

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

• Строгая гарантия часто может быть реализована посредством копирования и обмена, но предоставлять ее для всех функций непрактично.

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

Правило 30: Тщательно обдумывайте использование встроенных функций

Встроенные функции – какая замечательная идея! Они выглядят подобно функциям, они работают подобно функциям, они намного лучше макросов (см. правило 2). Их можно вызывать, не опасаясь накладных расходов, связанных с вызовом обычных функций. Чего еще желать?

В действительности вы получаете больше, чем рассчитывали, потому что возможность избежать затрат на вызов функции – это только полдела. Оптимизация, выполняемая компилятором, обычно наиболее эффективна на участке кода, не содержащем вызовов функций. Таким образом, вы даете компилятору возможность оптимизации тела встроенной функции в зависимости от объемлющего контекста. При использовании «обычного» функционального вызова большинство компиляторов такой оптимизации на обычных не выполняют.

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

Случайная жена для лорда Дракона

Волконская Оксана
Фантастика:
юмористическая фантастика
попаданцы
5.00
рейтинг книги
Случайная жена для лорда Дракона

Идеальный мир для Лекаря 7

Сапфир Олег
7. Лекарь
Фантастика:
юмористическая фантастика
попаданцы
аниме
5.00
рейтинг книги
Идеальный мир для Лекаря 7

Релокант. Вестник

Ascold Flow
2. Релокант в другой мир
Фантастика:
фэнтези
попаданцы
рпг
5.00
рейтинг книги
Релокант. Вестник

По дороге пряностей

Распопов Дмитрий Викторович
2. Венецианский купец
Фантастика:
фэнтези
героическая фантастика
альтернативная история
5.50
рейтинг книги
По дороге пряностей

Искушение генерала драконов

Лунёва Мария
2. Генералы драконов
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Искушение генерала драконов

Царь Федор. Трилогия

Злотников Роман Валерьевич
Царь Федор
Фантастика:
альтернативная история
8.68
рейтинг книги
Царь Федор. Трилогия

Идеальный мир для Социопата 4

Сапфир Олег
4. Социопат
Фантастика:
боевая фантастика
6.82
рейтинг книги
Идеальный мир для Социопата 4

Охота на попаданку. Бракованная жена

Герр Ольга
Любовные романы:
любовно-фантастические романы
5.60
рейтинг книги
Охота на попаданку. Бракованная жена

Решала

Иванов Дмитрий
10. Девяностые
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Решала

Приручитель женщин-монстров. Том 7

Дорничев Дмитрий
7. Покемоны? Какие покемоны?
Фантастика:
юмористическое фэнтези
аниме
5.00
рейтинг книги
Приручитель женщин-монстров. Том 7

Месть за измену

Кофф Натализа
Любовные романы:
современные любовные романы
5.00
рейтинг книги
Месть за измену

Медиум

Злобин Михаил
1. О чем молчат могилы
Фантастика:
фэнтези
7.90
рейтинг книги
Медиум

Вернуть невесту. Ловушка для попаданки 2

Ардова Алиса
2. Вернуть невесту
Любовные романы:
любовно-фантастические романы
7.88
рейтинг книги
Вернуть невесту. Ловушка для попаданки 2

Партиец

Семин Никита
2. Переломный век
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Партиец