Б. Страуструп - Язык программирования С++. Специальное издание, 3-изд. Бином. 2004 (1160791), страница 237
Текст из файла (страница 237)
С одним незначительным исключением для ассоциативных контейнеров, относительно функций зшар() стандартных контейнеров гарантируется, что оии не генерируют исключений. В сущности контейнеры переставляют элементы, меняя местами структуры данных, которые действуют как дескрипторы элементов Я 13.5, ч 17.1.3). Так как сами элементы ие перемегцаются, конструкторы и присваивания не вызываются, так что им и не сгенерировать исключений.
Кроме того стандарт гарантирует, что никакая функция заар() стандартной библиотеки не лишает законной силы ссылки, указатели или итераторы, ссылающиеся на элементы переставляемых конте!1неров. Имеется лишь один потенциальный источник исключений: объект сравнения в ассоциативном контейнере копируется как часть дескриптора. Единственное возможное исключение в зшар() стандартных контейнеров гснерирустсякопирующим конструктором или присваиванием объекта сравнения контейнера Ц 17.1А.1). К счастью, операции копирования объектов сравнения обычно тривиальны и не имеют возможности сгенерировать исключение.
Предоставленная пользователем яшар() должна быть написана так, чтобы обеспечить те же самые гарантии. Это относительно просто сделать, если автор не забывает менять местами типы, представленные лескрипторами, перестановкой дескрипторов, а не медленным и детальным копированием информации, на которую упомянутые дескрипторы ссылаются (ф 13.5, () 16.3.9, 6 17.1.3). Д.4.4. Инициализация и итераторы Распределение памяти для элементов и инициализация выделенной памяти — основные части реализации контейнера Я Д.З), Поэтому гарантируется, что стандартные алгоритмы для построения объектов в неинициализированной памяти— ип!и!!са!!ге!! Я!(), ип!и!!!а!!гег! ~И п() и ип7п!!7а!!гет! сору() !Ь' 19.4.4) — не оставляют после себя созданных объектов в случае генерации ими исключений.
При этом обеспечивается сильная гарантия Я Д.2). Поскольку инициализация памяти иногда 1045 Д.4. Гарантии стандартных контейнеров включает уничтожение элементов, требование к деструкторам не генерировать исключений является существенным для алгоритмов данного типа; см, ф Д.81141. Кроме того требуется чтобы итераторы, передаваемые в качестве аргументов этих алгоритмов, вели себя корректно. То есть они должны быть действительными итераторами, ссылаться на действительные последовательности, а итераторным операциям (типа++,!= и *), примененным к действительным итераторам, не разрешается генерировать исключений.
Итераторы — примеры объектов, которые свободно копируются стандартными алгоритмами и операциями со стандартными контейнерами. Соответственно, копирующие конструкторы и копирующие присваивания итераторов не должны генерировать исключений. В частности стандарт гарантирует, что копирующий конструктор или оператор присваивания итератора, возвращаемого стандартным контейнером, не генерируют исключений, Например итератор, возвращаемый вес/ог«Т>зЬеут[~, можно копировать, не опасаясь исключений. Обратите внимание, что ++ и — для итсратора могут генерировать исключения. Например, и/геатЬи/ Шега1ог(5 19.2.6) может обоснованно сгенерировать исключение, чтобы указать на ошибку ввода, а итератор с проверкой диапазона вправс сгенерировать исключение в ответ на попытку покинуть разрешенный для него диапазон (ф 19.3).
Однако операции инкремента и декремента не могут сгенерировать исключения при перемещении итератора от одного элемента последовательности к другому, не нарушив этим определение ++ и — для итсратора. Таким образом, ишш11а/(аее1 ЯЩ, ишп/6а//вес( ЯП п~~ и ип(пШа!/аег1 сору(~ предполагают, что -ни — для их итераторных параметров не будут генерировать исключений; если вес же генерируют, значит либо переданные «итераторы», согласно стандарту, — вовсе и не итераторы, либо итерируемая ими «последовательность» не является настоящей последовательностью.
И вновь стандартные контейнеры не защищают пользователя от его собственного неопределенного поведения Я /1.2), Д.4.5. Ссылки на элементы Когда ссылка, указатель или итсратор на элемент передается некоторому коду, этот код может испортить какой-либо контейнер, повредив один нз его элементов. Например: оо1е/Дсопв1 ХГ х) 1М<Х> 1вй 1в/ривЬ ЬасМ(с) увт«Х>:.Геега/ог1 = Ы.Ьеу/п~~; *1 = х, // копирование к в список //- Если х испорчен, деструктор списка, возможно, будет не способен должным образом , уничтожить /вб Например: вписГХ1 1пГ* р; Хй( р = пеш шц ) Приложение Д. Безопасность исключений и стандартная библиотека 1046 -Щ ( с(е(еге р; ) 0- !' // элонал~еренная функ~(ия ооЫ ола!(с(оив(( Хх; х р - ге!лсегрге! сов!<!л!*> (7(; /(х); // исаоргаили х // бал~ба с часовал~ л~еха ниэлэои Когда выполнение доходит до концаЯ], вызывается деструктор ((в!<Х>, а он в свою очередь вызывает деструктор Х для испорченного значения.
Резулътат выполнения с(е!е!е р, когда р пе 0 и не указывает на Х, не определен, вплоть до немедленного краха. Или в результате свободная память может оказаться испорченной так, что много позже возникнут трудно отслеживаемые проблемы в очевидно независимой части программы. Возможность подобных разрушений не должна останавливать нас от манипулирования элементами контейнеров через ссылки и итераторы; именно этот образ действий зачастую оказывается самым простым и наиболее эффективным. Однако будет мудро проявить дополнительную предосторожность по отношению к ссылкам на элементы контейнеров. Когда целостность контейнера имеет решающее значение, заслуживает внимания мысль предложить менее опытным пользователям безопасную альтернативу.
Например мы могли бы предоставить операцию, которая проверяет действительность нового элемента перед его копированием в важный контейнер. Естественно, такая проверка возможна только при знании прикладных типов. Вообще если элемент контейнера испорчен, последующие операции с контейнером могут потерпеть неудачу самым скверным образом. Это — не особенность контейнеров. Любой объект, оставленный в плохом состоянии, может вызывать последующий сбой. Д.4.6.
Предикаты Многие стандартные алгоритмы и операции со стандартными контейнерами полагаются на предикаты, которые предоставляются пользователями. В частности, все ассоциативные контейнеры зависят от предикатов как при поиске, так и при вставке. Нредикат. используемый операцией стандартного контейнера, может сгенерировать исключение. На этот случай каэкдая операция стандартной библиотеки обеспечивает основную гарантию, а некоторые операции, наподобие вставки отдельно~о элсмента, обеспечивают сильную гарантию (~ ДАЛ).
Если предикат генерирует исключение из операции с контейнером, окончательный набор элементов контейнера может оказаться не совсем таким, какой хотел пользователь, но это будет набор действительных элементов. Например, если = генерирует исключение, когда вызывается пз !(в!сали/ие() (б 17.2.2.3), пользователь не вправе полагать, что в списке нет дубликатов. Каждый элемент в списке действителен — и это все, что пользователь может уверенно ожидать (см. 5 Л.53). К счастью, предикаты редко делают что-нибудеь что могло бы сгенерировать исключение. Однако при рассмотрении безопасности исключений гледует принять во внимание определяемые пользователем предиктты <, =-, и (ек 1047 Д.б.
Другие части стандартной библиотеки При обращении к зшар)) копируется объект сравнения ассоциативного контейнера (й ДА.З). Следовательно, имеет смысл обеспечить, чтобы операции копирования предикатов, которые моглн бы использоваться как объекты сравнения, не генерировали исключений. Д.5. Другие части стандартной библиотеки Решающий элемент обеспечения безопасности исключений состоит в поддержке согласованности объектов: мы должны соблюдать основные инварианты для индивидуальных объектов н согласованность наборов объектов.
В контексте стандартной библиотеки объекты, для которых труднее всего обеспечить безопасность исключений, — это контейнеры. С точки зрения безопасности исключений остальная часть стандартной библиотеки менее интересна, Однако отметим, что в свете безопасности исключений встроенный массив является контейнером, который может быть испорчен опасной операцией. Вообще функции стандартной библиотеки генерируют только исключения, дозволенные им спецификациями, плюс генерируемые пользовательскими операциямн, которые вызываются из библиотечных функций.