Анимация
JavaScript
|
Главная Библионтека используют тот же потоковый буфер, что и сегг и wcerr соответственно. Таким образом, по умолчанию они синхронизируются с stderr, хотя в стандартной библиотеке С у этих потоков данных нет прямых аналогов. В зависимости от реализации синхронизация может приводить к лишним затратам. Например, если стандартные потоки данных С++ реализованы с использованием стандартных файлов С, это фактически подавляет буферизацию соответствующих потоковых буферов. Однако буферизация необходима для выполнения некоторых оптимизаций, особенно для форматированного чтения (см. далее). Чтобы программист мог переключиться на нужную реализацию, в классе ios base определена статическая функция sync with stdio() (табл. 13.46). Таблица 13.46. Синхронизация стандартных потоков данных С++ и С Статическая Описание функция syncwtth stdio() Возвращает информацию о том, синхронизируются ли стандартные объекты потоков данных со стандартными потоками данных С sync with stdio(false) Запрещает синхронизацию потоков данных С++ и С (при условии, что функция была вызвана до первой операции ввода-вывода) При вызове функции sync with stdio() передается необязательный логический аргумент, который указывает, нужно ли активизировать синхронизацию со стандартными потоками данных С. Для отключения синхронизации функция вызывается с аргументом feise: std::1os::sync wlth std1o(false); Отключение синхронизации Помните, что синхронизация отключается только до выполнения любой операции ввода-вывода. Если зто условие не выполнено, последствия от вызова функции зависят от реализации. Функция sync with stdio() возвращает значение, использованное при предыдущем вызове. Если ранее функция не вызывалась, она всегда возвращает true, отражающее состояние по умолчанию для стандартных потоков данных. Буферизация в потоковых буферах Буферизация ввода-вывода также является важным фактором эффективности. Системные вызовы обычно обходятся относительно дорого, поэтому их количество должно быть по возможности сведено к минимуму. Тем не менее существует и другая, более тонкая причина для буферизации в потоковых буферах С++ (по крайней мере, при вводе): функции форматного ввода-вывода работают с потоками данных при помощи итераторов потоковых буферов, а операции с итераторами медленнее операций с указателями. Отличия не так уж велики, но вполне достаточны для того, чтобы оправдать применение оптимизированных реализаций для частых операций (например, форматированного ввода числовых данных). Однако для этого необходимо атрименение буферизации в потоковых буферах. Итак, весь ввод-вывод осуществляется через потоковые буферы, обеспечивающие механизм буферизации. Но полагаться только на эту буферизацию недостаточно по трем причинам. О Потоки данных без буферизации часто реализуются проще. Если соответствующие потоки данных используются редко или только для вывода (для вывода различия между итераторами и указателями не столь существенны, как для ввода; основная проблема - сравнение итераторов потоковых буферов), вероятно, буферизация не играет особой роли. Но если потоковый буфер интенсивно используется, для него определенно следует реализовать буферизацию. О При установленном флаге unitbuf выходной поток данных очищает буфер после каждой операции вывода. Кроме того, очистка производится манипуляторами flush и endi. Вероятно, для оптимального быстродействия следует избегать всех трех способов. Но при выводе на консоль, например, было бы логично очищать буфер после вывода полных строк. Если вы зашли в тупик с программой, интенсивно использующей манипуляторы unitbuf, flush и endi, рассмотрите возможность применения специального потокового буфера, который в соответствующий момент вызывает не функцию sync(), а другую функцию. О Связывание потоков данных функцией tie() (см. с. 612) также требует дополнительных операций очистки потоков данных. Следовательно, связывание должно применяться только при абсолютной необходимости. При разработке новых потоковых буферов рекомендуется сначала реализовать их без буферизации. Если потоковый буфер окажется «узким местом»- в работе системы, вы сможете организовать буферизацию так, чтобы не затронуть другие компоненты приложения. Непосредственная работа с потоковыми буферами Все функции классов basicjstream и basic ostream, выполняющие чтение или запись символов, работают по одной схеме: сначала конструируется соответствующий объект sentry, а затем выполняется операция. Конструирование объекта sentry приводит к очистке буферов возможных связанных объектов, игнорированию пропусков (только при вводе) и выполнению операций, специфических для конкретных реализаций, например операций блокировки файлов в средах с параллельным функционированием нескольких потоков выполнения (threads), то есть в многопоточных средах (см. с. 631). При неформатированном вводе-выводе многие операции потоков данных все равно бесполезны, разве что операция блокировки может пригодиться при работе с потоками в средах с параллельным функционированием нескольких по-ToicoB выполнения (поскольку в С++ проблемы многопоточности не решаются). Следовательно, при неформатированном вводе-выводе прямая работа с потоковыми буферами обычно более эффективна. Для этого можно определить для потоковых буферов операторы << и ». О При получении указателя на потоковый буфер оператор << выводит все накопленные данные. Вероятно, это самый быстрый способ копирования файлов с использованием потоков данных С++. Пример: io/copyl.cpp finclude <iostreani> int main О { Копирование стандартного ввода в стандартный вывод std::cout « std::cin.rdbuf(): В этом фрагменте функция rdbufO возвращает буфер cin (см. с. 613). Следовательно, программа копирует весь стандартный входной поток данных в стандартный выходной поток. О При получении указателя на потоковый буфер оператор >> выполняет прямое чтение данных. Например, копирование стандартного входного потока данных в стандартный выходной поток также может выполняться следующим образом: 1о/сору2.срр finclude <iostream> int main О * {. * Копирование стандартного ввода в стандартный вывод std;:cin » std::noskipws » std::cout.rdbuf(): Обратите внимание на сброс флага skipws. В противном случае будут проигнорированы начальные пропуски во входных данных (см. с. 600). Впрочем, прямая работа с потоковым буфером может быть оправданна даже при форматированном вводе-выводе. Например, если программа в цикле вводит много числовых значений, может оказаться достаточным сконструировать всего один объект sentry, существующий на протяжении всего цикла. Внутри цикла пропуски игнорируются вручную (использование манипулятора ws также привело бы к конструированию объекта sentry), а прямое чтение числовых данных осуществляется фацетом num get (см. с. 677). Учтите, что потоковый буфер не обладает собственным состоянием ошибки. Он также ничего не знает о входных или выходных потоках, которые могут к нему подключиться. Следовательно, внутри следующего фрагмента состояние ошибки in не может измениться из-за сбоя или достижения конца файла: Копирование содержимого in в out out « in.rdbuf(): 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 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 [ 212 ] 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 |