Анимация
JavaScript
|
Главная Библионтека Оператор превратился в шаблон функции, параметризованный для всех разновидностей потоков данных. Проблема с шириной поля решается записью в строковый поток данных без указания конкретной ширины. Сконструированная строка затем передается в поток данных, переданный в аргументе. В результате символьное представление дроби выводится одной операцией записи, к которой применяется ширина поля. Например, рассмотрим такой фрагмент: Fraction vat(16.100); В Германии действует единая ставка НДС=16Ж std::COut « "VAT; \"" « Std::left « std::Setw(B) « vat « " « std::endl: Этот фрагмент выведет следующий результат: VAT: "16/100 " Реализация операторов ввода Операторы ввода реализуются по тому принципу, что и операторы вывода. Тем не менее при вводе приходится учитывать возможные ошибки чтения. Обычно Эта программа выведет следующий результат: VAT: "16 /100" В следующей версии решены обе проблемы: io/frac2out.cpp #include <1ostream> #1nclude <sstream> template <class charT. class traits> Inline std::bas1c ostream<charT.tra1ts>& operator « (std:;baslc ostream<charT.tra1ts>& strm. const Fractions f) /* Строковый поток * - с тем же форматом * - без специальной ширины поля */ std;:bas1c ostr1ngstream<charT.tra1ts> s; s.copyfmt(strm): s.w1dth(0): Заполнение строкового потока s « f.numeratorO « 7 « f.denom1nator(): print string stream strm « s.strO; return strm; в фзт1кциях ввода предусматривается особая обработка ситуаций, когда ввод завершается неудачей. При реализации функции ввода приходится выбирать между простотой и гибкостью. Например, в следующей функции используется упрощенный подход - дробь читается без проверки возможных ошибок: io.fracl1n.cpp #1nclude <iostream> inline std::1stream& operator » (std::1stream& strm. Fractions f) { int n. d: strm » n; Ввод числителя strm.ignoreO: Пропуск 7 strm » d: Ввод знаменателя f = Fraction(n.d): Присваивание всей дроби return strm; Во-первых, такая реализация подходит только для потоков данных с типом символов char. Во-вторых, она не проверяет, действительно ли два числа разделяются символом /. Другая проблема возникает при вводе неопределенных значений. Если знаменатель прочитанной дроби равен О, ее поведение не определено. Проблема обнаруживается в конструкторе класса Fraction, вызываемом выражением Fraction(n,d). Но зто означает, что ошибки форматирования автоматически обрабатываются внутри класса Fraction. Так как на практике ошибки форматирования обычно регистрируются на уровне потоков данных, лучше установить в этом случае флаг ios base::failbit Наконец, даже неудачная операция чтения может модифицировать дробь, переданную по ссылке. Допустим, числитель был прочитан успешно, а при чтении знаменателя произошла ошибка. Такое поведение противоречит общепринятым правилам, установленным стандартными операторами ввода, и поэтому считается нежелательным. Операция чтения должна либо завершаться успешно, либо не вносить изменений. Ниже приведена усовершенствованная реализация программы, избавленная от этих недостатков. Кроме того, она более универсальна, поскольку благодаря параметризации подходит для любых типов потоков данных: io/frac21n.hpp #include <iostream> template <class charT. class traits> inline * std;:basic istream<charT.traits>& operator » (std; :bas1cjstream<charT,traits>& strm. Fract1on& f) int n, d; Ввод числитепя strm » n; /* Если числитель прочитан успешно * - прочитать 7 и знаменатель */ 1f (strm.peekO == 7) { strm.ignoreO; strm » d; else { d - 1; /* Если знаменатель равен нулю * - установить failbit как признак ошибки форматирования ввода-вывода */ 1f (d == 0) { strm.setstate(std;:1os:;fa1lbit); return strm; /* Если все прошло успешно. * изменить значение дроби */ 1f (strm) { f = Fraction(n.d); return strm; Ha этот раз знаменатель читается только в том случае, если за первым числом следует символ /; в противном случае по золчанию используется знаменатель, равный 1, а целое число интерпретируется как дробь. Таким образом, для целых чисел знаменатель не обязателен. Реализация также проверяет, не равен ли прочитанный знаменатель нулю. В этом случае устанавливается флаг ios base::failbit, что может привести к выдаче соответствующего исключения (см. с. 576). Разумеется, при нулевом знаменателе возможны и другие действия. Например, реализация может сама сгенерировать исключение или вообще отказаться от проверки знаменателя, чтобы исключение было сгенерировано классом Fraction. Наконец, мы проверяем состояние потока данных, и новое значение присваивается объекту дроби только в том случае, если ввод был выполнен без ошибок. Всегда выполняйте последнюю npOBepijy и изменяйте значение объекта, если чтение прошло успешно. 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 |