|
Программирование >> Разработка устойчивых систем
: C04:InputWidth.cpp Ограничения при использовании setw с потоками ввода #1nclude <cassert> #1nclude <cmath> #1 ncl ude <iomamp> #include <limits> #1nclude <sstream> #1nclude <string> using namespace std: int mainO { istringstream isCone 2.34 five ): string temp: is setw(2) temp: assert(temp == on ): is setw(2) temp: assert(temp == e ): double x: is setw(2) x: double relerr = fabs(x - 2.34) / x: assert(relerr <= numeric limits<double>::epsilon()): } III:- При чтении строк функция setw() будет успешно управлять количеством прочитанных символов... до определенного момента. При первом чтении будут получены два символа, а при втором - только один, хотя мы запрашивали два. Дело в том, что операторная функция operator () использует пропуски как ограничители ввода (хотя с этим можно бороться, сбросив флаг skipws). Но при чтении чисел невозможно ограничить количество читаемых символов при помощи setw(). В случае потоков ввода функция setw() работает только при чтении строк. Создание манипуляторов В некоторых ситуациях бывает удобно создать собственный манипулятор. Оказывается, это делается на удивление просто. Манипулятор без аргументов (аналог endl) представляет собой простую функцию, которая получает и возвращает ссылку на ostream. Объявление endl выглядит так: ostream& endl(ostream&): После этого в выражениях наподобие следующего endl интерпретируется как адрес этой функции: cout howdy endl: Компилятор спрашивает: Существует ли подходящая функция, в аргументе которой передается адрес функции? Для этого в <iostream> определены специальные функции, называемые аппликаторами. Аппликатор вызывает функцию, полученную в аргументе, и передает ей объект ostream в качестве аргумента. Чтобы создать собственный манипулятор, не нужно разбираться в принципах работы аппликаторов; достаточно знать, что они существуют. Упрощенный код аппликатора ostream выглядит так: ostreamS ostream::operator (ostream& (*pf)(ostream&)) { return pf(*this): int mainO { cout newlines nl between nl each nl word nl: } /:- При направлении nl в поток вывода (например, в cout) происходит следующая последовательность вызовов: cout.operator (nl) ® nl(cout) Показанное ниже выражение внутри п1() вызывает операторную функцию ostream::operator(char): OS \n: Поток, возвращаемый этой функций, в конечном счете возвращается манипулятором nl. Эффекторы Итак, манипуляторы без аргументов создаются легко. А если вам нужен манипулятор с аргументами? Просматривая заголовок <iomanip>, вы найдете тип smanip, возвращаемый манипуляторами с аргументами. Возникает предположение, что этот тип нужно использовать при определении собственных манипуляторов, но делать этого не стоит. Тип smanip зависит от реализации, а его использование нарушает переносимость программы. К счастью, манипуляторы с аргументами можно определять без применения специальных трюков. Для этой цели Джерри Шварц (Jerry Schwarz) предложил методику, основанную на применении эффекторов. Эффектор представляет собой класс с конструктором и перегруженным оператором . Конструктор форматирует строку, представляющую нужную операцию, а оператор направляет эту строку в поток. Здесь приводятся два эффектора: первый выводит усеченную символьную строку, а второй - число в двоичном виде. Перед включением nl в заголовочный файл объявите его подставляемой (inline) функцией. Разработчик библиотеки потоков ввода-вывода. Реальное определение выглядит несколько сложнее, потому что в нем используются шаблоны, но для пояснения достаточно и этого. Когда в поток выводится функция, похожая на *pf (которая получает и возвращает ссылку на поток), вызывается функция-аппликатор. В свою очередь, она выполняет функцию, на которую ссылается pf. Аппликаторы для ios base, basic Jos, basic ostream и basicjstream определены в стандартной библиотеке С++. Для пояснения сказанного рассмотрим тривиальный пример - манипулятор п1, эквивалентный занесению в поток символа перевода строки (то есть не выполняющий сброс потока, в отличие от endl): : С04:п1.срр Создание манипулятора linclude <iostream> using namespace std: ostreamS nl(ostream& os) { return OS \n: int mainO { string words = Things that make us happy, make us wise : for (int i = words. SizeO: --i >= 0:) { ostringstream s: s Fixw(words, i): assert(s.str() == words.substr(0. D): ostringstream xs. ys; xs Bin(OxCAFEBABEUL): assert(xs.str() == 1100 1010 1111 1110 1011 1010 1011 1110 ); ys Bin(0x76543210UL): assert(ys.str() == 0111 0110 010Г 0100 00П 0010 0001 0000 ): } III:- Конструктор Fixw создает укороченную копию своего аргумента char*, а деструктор освобождает память, выделенную для этой копии. Перегруженный оператор берет содержимое своего второго аргумента (объект Fixw), записывает его в свой первый аргумент (ostream) и возвращает ostream для последующего использования в цепочечном выражении. При использовании Fixw в выражениях следу- : С04:Effector.срр Эффекторы Джерри Шварца #include <cassert> #1nclude <l1mits> Для maxO #1 nclude <sstream> #1 nclude <string> using namespace std: Вывод префикса: class Fixw { string str; public: Fixw(const stnng& s. int width) : str(s. 0. width) {} friend ostream& operator (ostream& os. const Fixw& fw) { return OS fw.str: Вывод числа в двоичном виде: typedef unsigned long ulong: class Bin { ulong n; public: Bin(ulong nn) { n = nn: } fnend ostream& operator (ostream& os. const Bin& b) { const ulong ULMAX = numeric limits<ulong>::max(): ulong bit = -(ULMAX 1); Установлен старший бит while(bit) { OS (b.n & bit ? T : 0); bit = 1: return OS:
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0.001
При копировании материалов приветствуются ссылки. |