|
Программирование >> Обобщенные обратные вызовы
пример 3-1: преобразование данных в строку с использованием функции snprintf. void PrettyFormatC int i, char* Этот код столь же прост, более безопасен: snprintfС buf, buflen, %4d buf, int buflen ) { как и ранее, но уже i ); Заметим, что при этом все равно остается возможность для ошибки --- вызываю-щая функция может передать неверный размер буфера. Это означает, что 100% безопасности в плане переполнения буфера не обеспечивает и snprintf, но она гораздо безопаснее функции sprintf. На вопрос Безопасна ли эта функция в смысле переполнения буфера? следует однозначно ответить Да . Заметим, что некоторые достандартные версии функции snpri ntf вели себя несколько иначе. В частности, в одной из распространенных реализаций при полном заполнении буфера он не завершался нулевым символом. В такой ситуации наша функция PrettyFormat должна немного отличаться от исходного варианта, чтобы учесть такое нестандартное поведение. преобразование данных в строку с использованием функции snpri ntf, которая не полностью отвечает стандарту С99. void PrettyFormatC int i, char* buf, i nt buflen ) { Этот код столь же прост, как и ранее, но уже более безопасен: ifС buflen > О ) { snprintfC buf, buflen-l, buf[buflen-l] = \0; %4d , i ); Bo всех остальных отношениях функции sprintf и snpri ntf идентичны. Приведем следующую таблицу сравнения snpri ntf и spri ntf. snprintf sprintf
Да: [С90], 1С++031, [С99] Из этого сравнения вполне логично вытекает следующая рекомендация. > Рекомендация Никогда не используйте функцию spri ntf. Если вы решите использовать возможности стандартного ввода-вывода из С, всегда используйте только тс функции, которые проверяют размер буфера, такие как snprintf, даже если эти функции в вашем компиляторе доступны только в качестве нестандартного расширения. При использовании snpri ntf вместо sprintf нет никаких неприятностей, зато есть очень важное преимушество. Когда я представлял этот материал на нескольких конференциях, я был шокирован тем, что только примерно каждый десятый знал о сушсствовании такой функции, как snpri ntf. Зато на каждой конференции кто-нибудь рассказывал, как в его проекте были обнаружены несколько случаев переполнения буфера, после чего sprintf были повсеместно заменены на snpri ntf. В результате тестирования оказывалось, что кроме явных ошибок, связанных с переполнением буфера и известных программистам, чудесным образом пропадали и ошибки, на которые им указывали годами, но которые они никак не могли локализовать. Итак, еще раз: забудьте о существовании функции sprintf. Альтернатива №2: std;:stringstream б) std::stringstream Наиболее распространенным средством для строкового представления данных в С++ является семейство stri ngstream. Ниже представлен код примера 3-1 при использовании ostri ngstream вместо spri ntf. пример 3-2: строковое представление данных в С++ с использованием ostringstream. void PrettyFormatC int i, stri ng& s ) { He так уж понятно и просто: ostringstream temp; temp setwC4) i; s = temp.strO ; Обратите внимание, что использование stri ngstream меняет достоинства и недостатки spri ntf местами. /. Простота использования и ясность. Как видите, изменения свелись не только к замене одной строки тремя, но и к введению временной переменной: Эта версия кода превосходит предыдущую по ряду параметров, но простота и ясность не входят в их число. Сложность не в том, чтобы изучить все манипуляторы потоком, - в конце концов, это задача той же сложности, что и изучить флаги форматирования sprintf; дело в том, что эти манипуляторы гораздо более громоздкие. Мне кажется, что код с длинными именами, например, setprecisiопС9) и setwC14), читается не так легко, как флаги форматирования в snpri ntf, где то же форматирование достигается при помощи простой строки %14.9, даже если аккуратно отформатировать исходный текст программы. 2. Эффективность (возможность непосредственного использования существующих буферов), stri ngstream работает в дополнительном буфере, так что при его использовании обычно необходимо дополнительное выделение памяти для рабочего буфера и вспомогательных объектов. Я провел небольшой эксперимент, скомпилировав пример 3-2 двумя популярными компиляторами, изменив при этом : :operator new для подсчета выполняемых при работе кода вьщелений памяти. На одной платформе я получил два динамических выделения памяти, а на другой - три. Однако там, где у spri ntf начинают проявляться недостатки, stri ngstream радует своими достоинствами. 3. Безопасность в плане переполнения буфера. Внутренний буфер basi c stri ngbuf потока stri ngstream автоматически увеличивается, если в этом возникает необходимость. 4. Безопасность типов. Использование перегруженного оператора и разрешения перегрузки обеспечивает корректную работу с разными типами, включая пользовательские типы, которые могут иметь собственные операторы вывода в поток. При ис- пользовании stringstream никакие ошибки времени выполнения, связанные с несоответствием типов, попросту невозможны. 5. Возможность работы в шаблонах. Поскольку теперь всегда автоматически вызывается корректный оператор , обобщить функцию PrettyFormat адя работы с произвольными типами данных - тривиальная задача. tempiate<typename т> void PrettyFormatC Т value, string* s ) { ostringstream temp; temp setw(4) value; s = temp. strQ ; Итак, в результате мы получили следующую таблицу сравнения stringstream и spri ntf.
Альтернатива №3: std::strstream в) std::strstream Хорошо это или нет, но в связи с тем, что strstream в стандарте fC++03j называют устаревшим и не рекомендуемым для использования, в книгах по С++ либо в лучшем случае дастся краткое описание этого класса ([Josuttis99]), либо он практически проиг!ю-рирован ([StroustrupOO]), или в книге явно указано, что из-за второсортности strstream его описание отсутствует ([LangerOO]). Однако несмотря на то, что комитет по стандарту С-Ы- отдал предпочтение stri ngstream, в котором лучше инкапсулировано управление памятью, strstream остается официальной частью стандарта, которую обязаны поддерживать все реализации С-Ы-. Поскольку strstream все еще остается частью стандарта, для полноты рассмотрения мы обязаны изучить этот класс. В случае eio использования пример 3-1 выглядит следующим образом. пример 3-3: строковое представление данных в С++ с использованием ostrstream. void PrettyFormatC int i, char* buf, int buflen ) { Статус strstream - нежелателен (deprecated), что означает, что комитет по стандарту С + + предупреждает: этот класс может исчезнуть из стандарта в любой момент, возможно, уже в след>пошей версии стандарта. Но удалить нечто из стандарта - практически очень сложная задача. Ведь как только та или иная возможность появляется в стандарте, появляется и множество кода с ее использованием, и удаление из стандарта грозит потерей обратной совместимости. Даже при официальном удалении чего-либо из стандарта конкретные реализации зачастую продолжают поддерживать это в целях обеспечения обратной совместимости. Нередко неже.чатель-ные возможности так никогда и не удаляются из стандарта. Так, в стандарте Fonran они присутствуют десятилетиями.
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0.001
При копировании материалов приветствуются ссылки. |