Программирование >>  Обобщенные обратные вызовы 

1 2 [ 3 ] 4 5 6 ... 84


При выполнении этого кода, вероятно, результатом будет 1. Это связано с тем, что программа просто пишет в паметь и читает из нее -- поступая при этом совсем не так, как положено, что тем не менее не приводит к немедленным фатальным сбоям (а жаль!).

V.reserveC 100 );

assertC v.capacityC) == 100 );

Здесь также должна выполняться проверка с использованием оператора >=, а не ==, и даже такая проверка будет излишней (.мы уже рассма1ривали этот вопрос раньше).

cout v[0];

А вот тут нас ждет сюрприз! На этот раз, скорее всего, инструкция cout v[0] ; приведет к результату 0: значение 1 таинственно исчезает...

Почему? Будем считать, что вызов reserve(100) приводит к запуску перераспределения памяти для внутреннего буфера v (если только в результате первоначального вызова reserveC?) не было вьщелено достаточного количества памяти для размещения 100 или большего числа элементов). При этом контейнер v копирует в новый буфер только те элементы, которые он в действительности содержит - но на самом деле он не содержит ни одного элемента! Новый буфер изначально заполнен неопределенными значениями (чаще всего - нулями), что и становится очевидным при выполнении cout v[0];.

= 3; . . = 4;

v[99i = 100;

Думаю, теперь у вас нет никаких сомнений в ошибочности этого кода. Это неправильный, плохой, неверный код... но поскольку operator[] не требует выполнения проверки диапазона, в большинстве реализаций стандартной библиотеки этот код будет молча работать, не приводя к исключениям или аварийному завершению программы.

Если же вместо приведенного выше фрагмента написать

v.at(2) = 3; v.atCS) = 4;

...

v.at(99) = 100;

то проблема станет очевидной, так как первый же вызов приведет к генерации исключения out.....ofLrange.

for(vector<int>:literator i = v.beginC); i<v,endC); i++){ cout *i endl;

Здесь опять ничего не будет выведено, а я опять предложу вам заменить этот цикл на

copyCv.begiп(), v.endC), ostream iterator<int>(cout, \n ));

Еще раз замечу, что такой код автоматически решает все проблемы, связанные с использованием оператора сравнения !=, префиксной формы ++, вызовом endC) в теле цикла и использованием endl. К тому же повторное использование стандартных алгоритмов зачастую автоматически делает код более быстрым и безопасным.

Резюме

Теперь вы знаете, в чем разница между размером и емкостью, а также между оператором operator[] и функцией at(). Если необходима проверка диапазона, всегда используйте функцию at С), благодаря которой вы сэкономите немало времени, которое в противном случае придется потратить на работу в отладчике.



Задача 2. Строчный двор. Часть 1: sprintf Сложность: 3

В этой и следующей задачах мы рассмотрим чудеса sprintf и разберемся, почему альтернативные варианты всегда (да, всегда) лучше.

Вопрос для новичка

1. Что такое sprintf? Перечислите как можно больше стандартных альтернатив spri ntf.

Вопрос для профессионала

2. В чем сильные и слабые стороны spri ntf? Будьте конкретны в своем ответе.

Решение

Все животные равны, но некоторые животные равнее других . - Джордж Оруэлл, Скотный двор

1. Что такое spri ntf? Перечисли-ге как можно больше ста1здартньс4 альтернатив spri ntf.

Рассмотрим следующий код С, который использует sprintf для преобразования целого числа в удобочитаемое строковое представление.

Пример 2-1: Строковое представление данных в С с использованием sprintf. Функция PrettyFormat получает в качестве параметра целое число и преобразует его в строку в предоставленный буфер. Результат должен иметь размер не менее 4 символов.

void PrettyFormatC int i, char* buf ) { код, простой и понятный: sprintfС buf, %4d , i );

Вопрос на засыпку: как сделать то же на С++?

Впрочем, это не совсем корректный вопрос, так как, в конце концов, этот же код вполне корректен и в С++. Вопрос на засыпку надо сформулировать так: если отбросить все офаничения стандарта С (если это и в самом деле Офзничения), то имеется ли лучший способ выполнить эти же действия в С++ с его классами, шаблонами и прочим?

Этот вопрос интересен тем, что пример 2-1 можно выполнить по крайней мере четырьмя разными стандартными способами, каждый из которых представляет собой определенный компромисс между ясностью, безопасностью типов, безопасностью времени исполнения и эффективностью. Кроме того, перефразируя свиней-ревизионистов из произведения Оруэлла, все четыре способа стандартны, но некоторые из них стандартнее других , да и взяты они из разных стандартов. Они приведены ниже в том порядке, в котором мы будем их рассматривать.

1. sprintf [С99, С++03]

2. snprintf [С99]

3. std: :stringstream [С++03]

4. std: :strstream fC++03]

Перевод с англ. Д. Иванова, В. Недошивина (Дж. Оруэлл. Скотный двор. - Пермь, Изд. КАПИК , [992.)



и наконец, если этого вам покажется мало, есть еще пятый, нестандартный (но, возможно, станет таковым) способ для простых преобразований, не требующих специального форматирования.

5. boost:: 1 exical......cast [Boost]

Итак, приступим к рассмотрению.

РадосI и и печали sprintf

2. В чем сильные и слабые стороны sprintf? Будьте конкретны в своем ответе.

Рассмотренный код функции PrettyFormat - всего лишь один из примеров использования sprintf. Я использовал его лишь как повод для обсуждения, так что не надо придавать слишком большое значение этой однострочной функции. Вопрос гораздо шире - мы выбираем способ для представлена нсстроковых значений в виде строк.

Давайте проанализируем функцию sprintf более детально. У нее есть два наиболее важных достоинства и три недостатка.

/. Простота использования и ясность. Как только вы изучите, как флаги форматирования и их комбинации влияют на форматирование строки, использование sprintf становится простым и очевидным. Очень трудно превзойти семейство функций pri ntf в задачах по форматированию текста. (Да, запомнить все флаги не просто, но обычно это относится к достаточно редко используемым флагам форматирования.)

2. Максимальная эффективность (возможность непосредственного использования существующих буферов). При использовании функции sprintf результат помешается непосредственно в заранее подготовленный буфер, так что функция PrettyFormat выполняет свою работу без динамического выделения памяти или других побочных действий. Она получает уже выделенную память и записывает результирующую строку непосредственно в эту память.

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

потом........ если в этом появится необходимость. В нашем случае необходимо учесть, что

эффективность достигается за счет инкапсуляции управления памятью.

Увы, как известно большинству пользователей функции sprintf, у нее есть и значительные недостатки.

3. Безопасность. Функция spri ntf - распространенный источник ошибок, связанных с переполнением буфера, когда размера выделенного буфера не хватает для размещения выводимой строки. Рассмотрим, например, следующий код.

char smal1Buf[5]; int value = 42;

PrettyFormat( value, smallBuf ); вроде бы все в порядке assertC value ==42 );

В этом случае значение 42 достаточно мало для того, чтобы результат 42\0 полностью разместился в пяти байтах smal 1 Buf. Но представим, что однажды код изменится на такой, как показано ниже.

char smal1Buf[5]; int value = 12108642 ;

В настоящее время - [C99], стандарт С++ [С++031 основан на более ранней версии С. Распространенная ошибка начинающих программистов - полагаться на спецификатор ширины вывода, в нашем случае - 4, который не работает так, как они рассчитывают, поскольку этот спецификатор ука.зывает минимальную длину вывода, а не максимальную.



1 2 [ 3 ] 4 5 6 ... 84

© 2006 - 2024 pmbk.ru. Генерация страницы: 0.002
При копировании материалов приветствуются ссылки.
Яндекс.Метрика