Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
КРАТКИЙ ОБЗОР С.doc
Скачиваний:
1
Добавлен:
26.10.2018
Размер:
2.11 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(), которая возвращает символ в поток, и он становится первым подлежащим чтению.