Поскольку объект key_не является элементарным и поэтому операции над ним могут приводить к выбрасыванию исключений, я сначала обмениваю его значения. В этом случае, если выбрасывается исключение, никакие другие переменные-члены не будут испорчены. Однако это не значит, что не будет испорчен объект key_. Когда вы работаете с членами объекта, все зависит от обеспечения ими гарантий безопасности при исключениях. Если такой член не выбрасывает исключение, то это значит, что я добился своего, так как обмен значений переменных встроенных типов не приведет к выбрасыванию исключений. Следовательно, функция swapInternalsявляется в основном и строгом смысле безопасной при исключениях.
Однако возникает интересный вопрос. Что, если у вас имеется несколько объектов-членов? Если бы вы имели два строковых члена, начало функции swapInternalsмогло бы выглядеть следующим образом.
void swapInternals(Message& msg) {
swap(key_, msg key_);
swap(myObj_, msg.myObj_);
// ...
Существует одна проблема: если вторая операция swapвыбрасывает исключение, как можно безопасно отменить первую операцию swap? Другими словами, теперь key_имеет новое значение, но операция swapдля myObj_завершилась неудачей, поэтому key_теперь испорчен. Если вызывающая программа перехватывает исключение и попытается продолжить работу, как будто ничего не случилось, она теперь будет обрабатывать нечто отличное от того, что было в начале. Одно из решений — предварительно скопировать key_во временную строку, но это не гарантирует безопасность, так как при копировании может быть выброшено исключение.
Одно из возможных решений состоит в использовании объектов, распределенных в динамической памяти.
void swapInternals(Message& msg) {
// key имеет тип string*, a myObj_ - тип MyClass*
swap(key_, msg.key_);
swap(myObj_, msg.myObj_);
Конечно, это означает, что теперь вам придется больше работать с динамической памятью, но обеспечение гарантий безопасности исключений будет часто оказывать влияние на ваш проект, поэтому будет правильно, если вы начнете думать об этом на ранних этапах процесса проектирования.
Основной лейтмотив этого рецепта не отличается от лейтмотива предыдущих рецептов, связанных с обеспечением безопасности исключений. Сначала выполняйте действия, которые могут создать проблемы, предусмотрите блок try/catchна тот случай, если что-то пойдет не так, и в последнем случае выполните подчистку за собой. Если все проходит нормально, поздравьте себя и обновите состояние объекта.
Смотри также
Рецепт 9.2 и рецепт 9.3.
Потоки (streams) являются одной из самых мощных (и сложных) компонент стандартной библиотеки С++. Их применение при простом, неформатированном вводе-выводе в целом не представляет трудностей, однако ситуация усложняется, если необходимо изменить формат с помощью стандартных манипуляторов или приходится писать свои собственные манипуляторы. Поэтому первые несколько рецептов описывают различные способы форматирования вывода потока данных. Следующие два рецепта показывают, как можно записывать объекты класса в поток и считывать их оттуда.
Затем рецепты переходят с темы чтения и записи содержимого файлов на работу с самими файлами (и каталогами). Если в вашей программе используются файлы особенно если такая программа является демоном или процессом на стороне сервера, вам, вероятно, потребуется создавать файлы и каталоги, удалять их, переименовывать и выполнять другие операции над ними. Существует ряд рецептов, которые показывают, как следует решать эти непривлекательные, но необходимые задачи в С++.
Последняя треть рецептов показывает, как можно манипулировать именами файлов и путями доступа к ним, используя многие стандартные строковые функции-члены. Стандартные строки содержат массу функций, предназначенных для анализа и манипулирования их содержимым, и если вам придется анализировать пути доступа к файлам и имена файлов, эти функции окажутся полезными. Если в этих рецептах нет того, что вам требуется, вернитесь к главе 7: возможно, там описано то, что вы ищете.
Манипулирование файлами требует прямого взаимодействия с операционной системой (ОС), но между различными ОС часто имеются тонкие отличия (а иногда вопиющие несовместимости). Многие типичные операции над файлами и каталогами выполняются с помощью вызовов системных функций стандартной библиотеки С. которые работают одинаково или аналогично в различных системах. В рецептах я отмечаю отличия версий библиотек различных ОС там, где они имеются.
Читать дальше