Мюррей Хилл - C++ Страница 44
Мюррей Хилл - C++ читать онлайн бесплатно
8.2.2 Вывод Определяемых Пользователем Типов
Рассмотрим определяемый пользователем тип:
class complex (* double re, im; public: complex(double r = 0, double i = 0) (* re=r; im=i; *)
friend double real(complex amp; a) (* returna.re; *) friend double real(complex amp; a) (* returna.re; *)
friend complex operator+(complex, complex); friend complex operator-(complex, complex); friend complex operator*(complex, complex); friend complex operator/(complex, complex); // ... *);
Операцию «« для нового типа complex можно определить так:
ostream amp; operator««(ostream amp;s, complex z) (* return s «« "(" «« real(z) «« "," «« imag(z) «« ")"; *)
и использовать точно так же, как для встроенного типа:
complex x(1,2); // ... cout «„ "x = " «« x «« «\n“;
получая при этом
x = (1,2)
Определение действия вывода для определяемого пользовтелем типа не требует ни модификации описания класса ostream, ни доступа к структуре данных (скрытой), которую этот класс поддерживает. Очень удачно, что имеет место первое, потому что описание класса ostream находится в стандартных заголвочных файлах, к которым у обычного пользователя нет доступа на запись. Последнее также важно, потому что обеспечивает зщиту от случайной порчи структуры данных. Это также позволяет менять реализацию ostream не влияя на пользовательские прораммы.
8.2.3 Некоторые Подробности Разработки
Операция вывода используется, чтобы избежать той многоловности, которую дало бы использование функции вывода. Но почему ««?
Возможности изобрести новый лексический символ нет (#6.2). Операция присваивания была кандидатом одновременно и на ввод, и на вывод, но оказывается, большинство людей препочитают, чтобы операция ввода отличалась от операции вывода. Кроме того, = не в ту сторону связывается (ассоциируется), то есть cout=a=b означает cout=(a=b).
Делались попытки использовать операции « и », но значния «меньше» и «больше» настолько прочно вросли в сознание людей, что новые операции ввода/вывода во всех реальных слчаях оказались нечитаемыми. Помимо этого, "«" находится на большинстве клавиатур как раз на ",", и у людей получаются операторы вроде такого:
cout « x , y , z;
Для таких операторов непросто выдавать хорошие сообщения об ошибках.
Операции «„ и “» к такого рода проблемам не приводят, они асимметричны в том смысле, что их можно проассоциировать с "в" и «из», а приоритет «« достаточно низок, чтобы можно было не использовать скобки для арифметических выражений в роли операндов. Например:
cout «„ „a*b+c=“ «« a*b+c «« «\n“;
Естественно, при написании выражений, которые содержат операции с более низкими приоритетами, скобки использовать надо. Например:
cout «„ „a^b!c=“ «« (a^b!c) «« «\n“;
Операцию левого сдвига тоже можно применять в операторе вывода:
cout «„ „a««b=“ «« (a««b) «« «\n“;
В С++ нет выражений с символьными значениями, в частноти, '\n' является целым (со значением 10, если используется набор символов ASCII), поэтому
cout «« "x = " «« x «« '\n';
напечатает число 10, а не ожидаемый символ новой строки. Эту и аналогичные проблемы можно сгладить, определив несколко макросов (в которых используются стандартные имена симвлов ASСII):
#define sp «„ " " #define ht „« «\t“ #define nl «« «\n“
Теперь предыдущий пример запишется в виде:
cout «« "x = " «« x nl;
Для печати символов предоставляются функции ostream::put (char) и chr(int) (см. #8.2.4). Хотя в некоторых кругах нсинтаксические макросы считаются худшим видом макросов, мне они нравятся.
Рассмотрим примеры:
cout «„ x „« " " «« y «« " " «« z «« «\n“; cout «« "x = " «« x «« ", y = " «« y «« «\n“;
Люди находят их трудно читаемыми из-за большого числа кавычек и того, что операция вывода внешне выглядит слишком непривычно. Здесь могут помочь приведенные выше макросы и несколько отступов:
cout «« x sp «« y sp «« z nl; cout «« "x = " «« x «« ", y = " «« y nl;
8.2.4 Форматированный Вывод
Пока «« применялась только для неформатированного вывда, и на самом деле в реальных программах она именно для этго главным образом и применяется. Помимо этого существует также несколько форматирующих функций, создающих представление своего параметра в виде строки, которая используется для вывода. Их второй (необязательный) параметр указывает, сколко символьных позиций должно использоваться.
char* oct(long, int=0); // восьмеричное представление char* dec(long, int=0); // десятичное представление char* hex(long, int=0); // шестнадцатиричное представление char* chr(int, int=0); // символ char* str(char*, int=0); // строка
Если не задано поле нулевой длины, то будет производится усечение или дополнение; иначе будет использоваться столко символов (ровно), сколько нужно. Например:
cout «« "dec(" «« x «« ") = oct(" «« oct(x,6) «« ") = hex(" «« hex(x,4) «« ")";
Если x==15, то в результате получится:
dec(15) = oct( 17) = hex( f);
Можно также использовать строку в общем формате:
char* form(char* format ...);
cout««form() эквивалентно применению стандартной функции вывода языка C printf()*. form() возвращает строку, получамую в результате преобразования и форматирования ее парамеров, которые стоят после первого управляющего параметра – строки формата format. Строка формата состоит из объектов двух типов: обычных символов, которые просто копируются в пток вывода, и спецификаций преобразования, каждая из которых влечет преобразование и печать следующего из параметров. Кадая спецификация преобразования начинается с символа %. Наример:
– * Объяснение того, как применяются строки формата, – это слегка отредактированный вариант спецификации printf(). (прим. автора)
cout«„form(«there were %d members present“,no_of_members);
Здесь %d указывает, что no_of_members должно рассматрваться как int и печататься в виде соответствующей последовтельности десятичных цифр. Если no_of_members==127, вывод бдет такой:
there were 127 members present
Множество спецификаций преобразования довольно велико и обеспечивает высокую степень гибкости. После % может стоять:
– необязательный знак минус, который задает выравнивание преобразованного значения влево в указанном поле;
d необязательная строка цифр, задающая ширину поля. Если преобразованное значение имеет меньше цифр, чем ширина поля, оно будет дополняться пробелами слева (или справа, если был задан индикатор выравнивания влево) до заполнния всей ширины поля; если ширина поля начинается с нля, то вместо дополнения пробелами будет делаться допонение нулями;
. необязательная точка, для отделения ширины поля от
следующей строки цифр;
d необязательная строка цифр, специфицирующая точность, которая задает число цифр после десятичной точки для преобразований e и f или печатаемых символов для строки;
* в ширине поля или точности вместо строки цифр может стять *. В этом случае ширина поля и точность задается цлым параметром;
h необязательный символ h; указывает на то, что идущие за ним d, o, x или y соответствуют параметру короткое цлое;
l необязательный символ h; указывает на то, что идущие за ним d, o, x или y соответствуют параметру длинное целое;
% указывает, что должен быть напечатан символ %, никакие параметры при этом не затрагиваются;
c символ, указывающий, какой тип преобразования должен применяться. Символы преобразования и их значения таквы:
d целый параметр преобразуется в десятичную запись;
o целый параметр преобразуется в восьмеричную запись;
x целый параметр преобразуется в шестнадцатиричную запись;
f параметр float или double преобразуется в десятичную запись вида [-]ddd.ddd, где число, задаваемое цифрами d после десятичной точки, эквивалентно спецификации тоности для параметра. Если точность опущена, дается шесть цифр; если точность явно задана как 0, то не печатается десятичная точка и не печатается ни одной цифры;
e параметр float или double преобразуется в десятичную запись вида [-]d.ddde+dd, где перед десятичной точкой стоит одна цифра, а число, задаваемое цифрами после дсятичной точки, эквивалентно спецификации точности для параметра; когда точность опущена, выдается шесть цифр;
g параметр float или double печатается в том из видов d, f или e, который обеспечивает полную точность при минмальной затрате места;
c печатается символьный параметр, пустые символы игнорруются;
s параметр воспринимается как строка (указатель на сивол), и печатаются символы из строки до пустого символа или до тех пор, пока не будет достигнуто число символов, указанное спецификацией точности; но если точность равна нулю, печатаются все символы до пустого;
u беззнаковый целый параметр преобразуется в десятичную запись.
Несуществующая или недостаточная ширина поля никогда не приводит к обрезанию поля; дополнение поля записи имеет место только в том случае, если указанная ширина поля превышает фактическую ширину.
Вот более сложный пример:
char* src_file_name;
int line; char* line_format = «\n#line %d \»%s\"\n"; //... cout «„ „int a;\n“; cout «« form(line_format,line,src_file_name); cout «« «int b;\n“;
который печатает
int a;
#line 13 «С++/main.c» int b;
Применение form() небезопасно в смысле того, что не вполняется проверка типа. Вот, например, хорошо хорошо извесный способ получить непредсказуемый вывод и/или дамп (core dump):
char x; // ... cout«„form(«bad input char: %s“,x);
Правда, она дает большую гибкость в том виде, который хорошо знаком программистам на C. Потоковый вывод можно смшивать с выводом в стиле printf().
Жалоба
Напишите нам, и мы в срочном порядке примем меры.