Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Cpp_Страуструп.doc
Скачиваний:
16
Добавлен:
03.05.2015
Размер:
3.2 Mб
Скачать

10.2.2 Вывод пользовательских типов

Рассмотрим пользовательский тип данных:

class complex {

double re, im;

public:

complex(double r = 0, double i = 0) { re=r; im=i; }

friend double real(complex& a) { return a.re; }

friend double imag(complex& a) { return a.im; }

friend complex operator+(complex, complex);

friend complex operator-(complex, complex);

friend complex operator*(complex, complex);

friend complex operator/(complex, complex);

//...

};

Для нового типа complex операцию << можно определить так:

ostream& operator<<(ostream&s, complex z)

{

return s << '(' real(z) << ',' << imag(z) << ')';

};

и использовать как operator<< для встроенных типов. Например,

main()

{

complex x(1,2);

cout << "x = " << x << '\n';

}

выдаст

x = (1,2)

Для определения операции вывода над пользовательскими типами данных

не нужно модифицировать описание класса ostream, не требуется и доступ

к структурам данных, скрытым в описании класса. Последнее очень кстати,

поскольку описание класса ostream находится среди стандартных

заголовочных файлов, доступ по записи к которым закрыт для большинства

пользователей, и изменять которые они вряд ли захотят, даже если бы

могли. Это важно и по той причине, что дает защиту от случайной порчи

этих структур данных. Кроме того имеется возможность изменить

реализацию ostream, не затрагивая пользовательских программ.

10.3 Ввод

Ввод во многом сходен с выводом. Есть класс istream, который реализует

операцию ввода >> ("ввести из" - "input from") для небольшого набора

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

operator>>.

10.3.1 Ввод встроенных типов

Класс istream определяется следующим образом:

class istream : public virtual ios {

//...

public:

istream& operator>>(char*); // строка

istream& operator>>(char&); // символ

istream& operator>>(short&);

istream& operator>>(int&);

istream& operator>>(long&);

istream& operator>>(float&);

istream& operator>>(double&);

//...

};

Функции ввода operator>> определяются так:

istream& istream::operator>>(T& tvar)

{

// пропускаем обобщенные пробелы

// каким-то образом читаем T в`tvar'

return *this;

}

Теперь можно ввести в VECTOR последовательность целых, разделяемых

пробелами, с помощью функции:

int readints(Vector<int>& v)

// возвращаем число прочитанных целых

{

for (int i = 0; i<v.size(); i++)

{

if (cin>>v[i]) continue;

return i;

}

// слишком много целых для размера Vector

// нужна соответствующая обработка ошибки

}

Появление значения с типом, отличным от int, приводит к прекращению

операции ввода, и цикл ввода завершается. Так, если мы вводим

1 2 3 4 5. 6 7 8.

то функция readints() прочитает пять целых чисел

1 2 3 4 5

Символ точка останется первым символом, подлежащим вводу. Под пробелом,

как определено в стандарте С, понимается обобщенный пробел, т.е.

пробел, табуляция, конец строки, перевод строки или возврат каретки.

Проверка на обобщенный пробел возможна с помощью функции isspace()

из файла <ctype.h>.

В качестве альтернативы можно использовать функции get():

class istream : public virtual ios {

//...

istream& get(char& c); // символ

istream& get(char* p, int n, char ='n'); // строка

};

В них обобщенный пробел рассматривается как любой другой символ и

они предназначены для таких операций ввода, когда не делается никаких

предположений о вводимых символах.

Функция istream::get(char&) вводит один символ в свой параметр.

Поэтому программу посимвольного копирования можно написать так:

main()

{

char c;

while (cin.get(c)) cout << c;

}

Такая запись выглядит несимметрично, и у операции >> для вывода символов

есть двойник под именем put(), так что можно писать и так:

main()

{

char c;

while (cin.get(c)) cout.put(c);

}

Функция с тремя параметрами istream::get() вводит в символьный вектор

не менее n символов, начиная с адреса p. При всяком обращении к get()

все символы, помещенные в буфер (если они были), завершаются 0, поэтому

если второй параметр равен n, то введено не более n-1 символов. Третий

параметр определяет символ, завершающий ввод. Типичное использование

функции get() с тремя параметрами сводится к чтению строки в буфер

заданного размера для ее дальнейшего разбора, например так:

void f()

{

char buf[100];

cin >> buf; // подозрительно

cin.get(buf,100,'\n'); // надежно

//...

}

Операция cin>>buf подозрительна, поскольку строка из более чем 99

символов переполнит буфер. Если обнаружен завершающий символ, то он

остается в потоке первым символом подлежащим вводу. Это позволяет

проверять буфер на переполнение:

void f()

{

char buf[100];

cin.get(buf,100,'\n'); // надежно

char c;

if (cin.get(c) && c!='\n') {

// входная строка больше, чем ожидалось

}

//...

}

Естественно, существует версия get() для типа unsigned char.

В стандартном заголовочном файле <ctype.h> определены несколько

функций, полезных для обработки при вводе:

int isalpha(char) // 'a'..'z' 'A'..'Z'

int isupper(char) // 'A'..'Z'

int islower(char) // 'a'..'z'

int isdigit(char) // '0'..'9'

int isxdigit(char) // '0'..'9' 'a'..'f' 'A'..'F'

int isspace(char) // ' ' '\t' возвращает конец строки

// и перевод формата

int iscntrl(char) // управляющий символ в диапазоне

// (ASCII 0..31 и 127)

int ispunct(char) // знак пунктуации, отличен от приведенных выше

int isalnum(char) // isalpha() | isdigit()

int isprint(char) // видимый: ascii ' '..'~'

int isgraph(char) // isalpha() | isdigit() | ispunct()

int isascii(char c) { return 0<=c && c<=127; }

Все они, кроме isascii(), работают с помощью простого просмотра,

используя символ как индекс в таблице атрибутов символов. Поэтому

вместо выражения типа

(('a'<=c && c<='z') || ('A'<=c && c<='Z')) // буква

которое не только утомительно писать, но оно может быть и ошибочным

(на машине с кодировкой EBCDIC оно задает не только буквы), лучше

использовать вызов стандартной функции isalpha(), который к тому

же более эффективен.

В качестве примера приведем функцию eatwhite(), которая читает из

потока обобщенные пробелы:

istream& eatwhite(istream& is)

{

char c;

while (is.get(c)) {

if (isspace(c)==0) {

is.putback(c);

break;

}

}

return is;

}

В ней используется функция putback(), которая возвращает символ в

поток, и он становится первым подлежащим чтению.