Анимация
JavaScript
|
Главная Библионтека snprintf, даже если эти функции в вашем компиляторе доступны только в качестве нестандартного расширения. При использовании snprintf вместо sprintf нет никаких неприятностей, зато есть очень важное преимущество. Когда я представлял этот материал на нескольких конференциях, я был шокирован тем, что только примерно каждый десятый знал о существовании такой функции, как snpri ntf. Зато на каждой конференции кто-нибудь рассказывал, как в его проекте были обнаружены несколько случаев переполнения буфера, после чего sprintf были повсеместно заменены на snprintf. В (результате тестирования оказывалось, что кроме явных ошибок, связанных с переполнением буфера и известных программистам, чудесным образом пропадали и ошибки, на которые им указывали годами, но которые они никак не могли локализовать. Итак, еще раз: забудьте о существовании функции sprintf. Альтернатива №2: std::stringstream б) std::stringstream Наиболее распространенным средством для строкового представления данных в С++ является семейство stringstream. Ниже представлен код примера 3-1 при использовании ostringstream вместо spri ntf. пример 3-2: строковое представление данных в С++ с использованием ostringstream. void PrettyFormatC int i, stri ng& s ) { He так уж понятно и просто: ostringstream temp; temp « setwC4) « i; s = temp.StrQ ; Обратите внимание, что использование stri ngstream меняет достоинства и недостатки spri ntf местами. /. Простота использования и ясность. Как видите, изменения свелись не только к замене одной строки тремя, но и к введению временной переменной: Эта версия кода превосходит предыдущую по ряду параметров, но простота и ясность не входят в их число. Сложность не в том, чтобы изучить все манипуляторы потоком, - в конце концов, это задача той же сложности, что и изучить флаги форматирования sprintf; дело в том, что эти манипуляторы гораздо более громоздкие. Мне кажется, что код с длинными именами, например, « setprecisiопС9) и « setwC14), читается не так легко, как флаги форматирования в snprintf, где то же форматирование достигается при помощи простой строки %14.9, даже если аккуратно отформатировать исходный текст программы. 2. Эффективность (возможность непосредственного использования существующих буферов), stringstream работает в дополнительном буфере, так что при его использовании обычно необходимо дополнительное выделение памяти для рабочего буфера и вспомогательных объектов. Я провел небольшой эксперимент, скомпилировав пример 3-2 двумя популярными компиляторами, изменив при этом : :operator new для подсчета выполняемых при работе кода вьщелений памяти. На одной платформе я получил два динамических выделения памяти, а на другой - три. Однако там, где у spri ntf начинают проявляться недостатки, stringstream радует своими достоинствами. 3. Безопасность в плане переполнения буфера. Внутренний буфер basi c stri ngbuf потока stringstream автоматически увеличивается, если в этом возникает необходимость. 4. Безопасность типов. Использование перегруженного оператора « и разрешения перегрузки обеспечивает корректную работу с разными типами, включая пользовательские типы, которые могут иметь собственные операторы вывода в поток. При ис- пользовании stringstream никакие ошибки времени выполнения, связанные с несоответствием типов, попросту невозможны. 5. Возможность работы в шаблонах. Поскольку теперь всегда автоматически вызывается корректный оператор «, обобилить функцию PrettyFormat лпя работы с произвольными типами данных - тривиальная задача. tempiate<typename т> void PrettyFormatC Т value, string* s ) { ostringstream temp; temp « setwC4) « value; s = temp. strQ ; Итак, в результате мы получили следуюшую таблицу сравнения stringstream и spri ntf.
Альтернатива №3: std::strstream в) std::strstream Хорошо это или нет, но в связи с тем, что strstream в стандарте [С-Ь+ОЗ] называют устаревшим и не рекомендуемым лля использования, в книгах по С++ либо в лучшем случае дастся краткое описание этого класса ([Josuttis99]), либо он практически проиг!ю-рирован ([StroustrupOO]), или в книге явно указано, что из-за "второсортности" strstream его описание отсутствует ([LangerOO]). Однако несмотря на то, что комитет по стандарту С-Ы- отдал предпочтение stringstream, в котором лучше инкапсулировано управление памятью, strstream остается официальной частью стандарта, которую обязаны поддерживать все реализации С-Ы-. Поскольку strstream все еше остается частью стандарта, для полноты рассмотрения .мы обязаны изучить этот класс, В случае eio использования пример 3-1 выглядит следующим образом. пример 3-3: строковое представление данных в С++ с использованием ostrstream. void PrettyFormatC int i, char* buf, int buflen ) { Статус strstream - нежелателен (deprecated), что означает, что комитет по стандарту С + + предупреждает: этот класс может исчезнуть из стандарта в любой момент, возможно, уже в след>лошей версии стандарта. Но удалить нечто из стандарта - практически очень сложная задача. Ведь как только та или иная возможность появляется в стандарте, появляется и множество кода с ее использованием, и удаление из стандарта грозит потерей обратной совместимости. Даже при официальном удалении чего-либо из стандарта конкретные реализации зачастую продолжают поддерживать это в целях обеспечения обратной совместимости. Нередко нежелательные возможности так никогда и не удаляются из стандарта. Так. в стандарте Fonran они присутствуют десятилетиями. неплохо, но надо не забывать об ends: ostrstream temp( buf, buf1 en ); temp « setw(4) « i « ends; /. Простота использования и ясность, strstream немного уступает stri ngstream в плане простоты использования и ясности кода. Они оба требуют создания временного объекта. При использовании strstream вы до.тжны не забывать добавлять ends для завершения строки, и эту его особенность можно определить как нечто между "безвкусно" и "опасно". Если вы забудете сделать это, возникнет опасность чтения после конца буфера (если вы полагаетесь только на завершающий нулевой символ). Даже раскритикованная нами функция sprintf не поступает так, и всегда завершает строку нулевым символом. Но зато использование strstream таким образом, как это показано в примере 3-3, не требует вызова функции .strC) для получения конечного результата. (Конечно, если вы поступите иначе и позволите strstream со.здать собственный буфер, вам потребуется не только вызов . str() для получения конечного результата, но и вызов -freeze(false), поскольку иначе strstreambuf не будет освобождать выделенную память.) 2. Эффективность (возможность непосредственного использования существующих буферов). При создании объекта ostrstream с передачей ему указателя на существующий буфер нам не требуется выполнять никакого дополнительного распределения памяти; ostrstream будет записывать результат непосредственно в этот буфер. Это очень важное отличие от stringstream, в котором нет никаких подобных возможностей для помещения результата непосредственно в существующий буфер во избежание излишнего перераспределения памяти". Конечно, ostrstream может использовать и собственный динамически выделенный буфер, если у вас нет своего - для этого вам наао просто воспользоваться конструктором ostrstream по умолчанию". Из всех рассматриваемых в этом разделе альтернатив такая возможность эффективной работы есть только у strstream. 3. Безопасность в плане переполнения буфера. Как видно из примера 3-3, у ostrstream его внутренний буфер strstreambuf автоматически проверяет свою длину, чтобы обеспечить невозможность записи за пределами вьщеленной памяти. Если же мы используем конструктор ostrstream по умолчанию, то его внутренний буфер при необходимости будет автоматически увеличиваться. 4. Безопасность типов. Так же, как и stri ngstream, strstream полностью безопасен в этом отношении. 5. Возможность работы в шаблонах. Так же, как и stringstream, обеспечивает такую возможность. Вот небольшой пример, иллюстрирующий ее. tempiate<typename т> void PrettyFormatC Т value, char* buf, int buf1 en ) { ostrstream temn( buf, buflen ); temp « setw(4) « value << ends; Подводя итоги, получаем следующую таблицу сравнения strstream с spri ntf. У stri ngstream есть конструктор, который получает в качестве параметра stri ng&, ио on просто делает копию содержимого переданной строки вместо непосредственного использования этой строки в качестве рабочей области, " В табл. 3.1 исследования производительности strstream показаны всего лишь для двух компиляторов - Borland С++ 5.5.1 и Visual С++ 7. Дело в том, что в этих реализациям С++ по каким-то причинам при каждом вызове функции PrettyFormatC) из примера 3-3 выполняются дополнительные вьщеления памяти (хотя при передаче конструктору указ<ттеля на существующий буфер вьщелений памяти оказывается меньше, чем когда strstream создает собственный буфер). Прочие среды, как и ожидалось, работают без дополиителыюго выделения памяти, 0 1 2 3 4 5 [ 6 ] 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |