Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

osn_progr_final

.pdf
Скачиваний:
43
Добавлен:
12.02.2016
Размер:
3.27 Mб
Скачать

ДОДАТКИ

1 ВВIД-ВИВIД С++

1.1 СИСТЕМА ВВОДУ-ВИВОДУ С++.

С++ має свою досить складну систему вводу-виводу, що грунтується на роботі з класами. Можна сказати, що на функціональному рівні ( відповідні функції вводу-виводу) система вводувиводу даних ANSI C збереглася і в С++. Проте на рівні самої організації вона принципово змінилася. З чим пов’язані ці зміни ? Це зумовлено, мабуть, специфікою самої мови. Адже С++ орієнтується на роботу з с типами даних, створених користувачем. Наявність в С++ апарату перевантаження функцій та операторів відкриває нові можливості для створення засобів вводу-виводу. Використання цих можливостей привело до утворення цілої ієрархії класів, кожен з яких містить свої поля даних та функції-члени.

 

ios

 

istream

ostream

fstreambase

iostream

ostream_withassign

iostream_withassign

ofstream

istream_withassign

fstream

 

ifstream

 

 

мал.1

 

 

ios

 

istream

ostream

strstreambase

iostream

 

ostrstream

 

strstream

 

istrstream

мал. 2

331

streambuf

filebuf

strstreambuf

мал. 3

Класи можуть спецiалiзуватись за напрямком руху iнформацiї (ввiд/вивiд), за джерелом iнформацiї та особливостями її обробки (класи файлового, рядкового, буферизованого, форматованого вводу/виводу).

Базовий клас ios знаходиться у файлi iostream.h. Крім цього, засоби файлового вводу-виводу містяться в файлі fstream.h , файл iomanip.h необхідний для роботи з манiпуляторами, strstream.h - для роботи з резидентними потоками.

Як бачимо з мал. 1, клас ios є базовим для побудови ієрархії класів, що забезпечують ввід-вивід. Кожен клас містить ряд функційчленів та полів даних. Наприклад, клас ios виглядає так (з точністю до коментарів, що даються англійською мовою):

class _CLASSTYPE ios { public:

// біти стану enum io_state{

goodbit=0x00,//ніякі біти не встановлені, все добре eofbit=0x01,// кінець файлу

failbit=0x02,// помилка останньої операції вводу-виводу badbit=0x04,// помилка операції доступу hardfail=0x80//апаратна помилка

};

// режими доступу enum open_mode{

in=0x01,// відкриття для читання out=0x02,// відкриття для запису

ate=0x04,//вiдкриття та встановлюється вказiвник на eof app=0x08,// відкриття для дописування trunc=0x10,//перезаписує файл, якщо він iснує (якщо нi - створює); nocreate=0x20,//вiдкриває файл, якщо вiн тiльки не iснує;

noreplace=0x40,//вiдкриває файл, якщо вiн вже iснує binary=0x80//двiйковий файл

};

// директиви позиції

enum seek_dir { beg=0, cur=1, end=2 };

// прапорці форматування enum{

332

skipws=0x0001, // ігнорує пропуски при вводі left=0x0002, // вирiвнювання даних по лiвiй границi поля right=0x0004, //вирiвнювання даних по правій границi поля internal=0x0008,//виводиться зліва знак чи основа dec=0x0010,// десяткова основа

oct=0x0020,// вісімкова hex=0x0040,// шістнадцяткова showbase=0x0080,// вивід основи

showpoint=0x0100,// force decimal point (floating output) uppercase=0x0200,// верхній регістр showpos=0x0400,//виводиться знак + для додатнього числа scientific=0x0800,//дійсні числа виводяться в науковій нотації 1.2345E2

fixed=0x1000,// форма з фіксованою крапкою 123.45 unitbuf=0x2000,// буфер потоку звільняється після кожної операції запису

stdio=0x4000// звільняються stdout, stderr після запису}; // константи для другого параметра setf()

static const long basefield;// dec | oct | hex static const long adjustfield;// left | right | internal static const long floatfield;// scientific | fixed

// конструктор, деструктор

_Cdecl ios(streambuf _FAR *); virtual _Cdecl ~ios();

//для читання/встановлення/витирання прапорців формату long _Cdecl flags();

long _Cdecl flags(long);

long _Cdecl setf(long _setbits, long _field); long _Cdecl setf(long);

long _Cdecl unsetf(long);

//читання/встановлення поля width

int _Cdecl width(); int _Cdecl width(int);

// читання/встановлення заповнюючого символу char _Cdecl fill();

char _Cdecl fill(char);

//читання/встановлення precision int _Cdecl precision(int);

int _Cdecl precision();

//встановлення зв”язку з даноим потоком ostream _FAR * _Cdecl tie(ostream _FAR *); ostream _FAR * _Cdecl tie();

//знаходження стану потоку

int _Cdecl rdstate();// повертає стан потоку

int _Cdecl eof();// ненульове значення в позиції кінця

333

файла

int _Cdecl fail(); // ненульове якщо помилка операції int _Cdecl bad(); // ненульове якщо трапилась помилка int _Cdecl good(); // ненульове якщо не встановлені прапорці

void _Cdecl clear(int = 0); // встановлює стан потоку _Cdecl operator void _FAR *(); // нуль якщо стан помил-

ковий

int _Cdecl operator! (); // нуль якщо стан помилковий streambuf _FAR *_Cdecl rdbuf();// повертає streambuf

// для визначення додаткових бітів прапорців

static long _Cdecl bitalloc();// встановлює нові біти прапорців, повертає значення

static int _Cdecl xalloc();// вимагає нове слово користу-

вача, повертає індекс

long _FAR & _Cdecl iword(int); // повертає n-те слово ко-

ристувача як значення int

void _FAR* FAR& _Cdecl pword(int); //повертає n-те слово користувача як вказівник

static void _Cdecl sync_with_stdio();

//для сумісності з застарілими версіями int _Cdecl skip(int);

protected:

//додаткові прапорці

enum { skipping = 0x100, tied = 0x200 };

streambuf _FAR

* bp;

//

відповідний

потік

ostream _FAR *

x_tie;

//

прив”язаний

ostream

int state; // біти статусу

int ispecial;// біти статусу вхідного потоку int ospecial; // біти статусу вихідного потоку long x_flags; // біти форматуючих прапорців

int x_precision; // точність операцій з плаваючою крапкою для вихідного потоку

int x_width;// поле width для вихідного потоку

int x_fill;// заповнюючий символ для вихідного потоку int isfx_special;// не використовується

int osfx_special;// не використовується int delbuf;// не використовується

int assign_private; // не використовується

_Cdecl ios(); // void-конструктор, не здійснює ініціалізації

void _Cdecl init(streambuf _FAR *); // ініціалізація void _Cdecl setstate(int); // встановлює всі біти статусу static void _Cdecl (*stdioflush)();

334

private:

//для додаткових прапорців та слів користувача static long nextbit;

static int usercount;

union ios_user_union _FAR *userwords; int nwords;

void _Cdecl usersize(int);

//these declarations prevent automatic copying of an ios

_Cdecl ios(ios _FAR &); // оголошена але не визначена void _Cdecl operator= (ios _FAR &);// оголошена але не визначена

};

inline streambuf _FAR * _Cdecl ios::rdbuf() { return bp;

}

inline ostream _FAR * _Cdecl ios::tie() { return x_tie; } inline char _Cdecl ios::fill() { return x_fill; }

inline int_Cdecl ios::precision() { return x_precision; } inline int _Cdecl ios::rdstate() { return state; }

inline int _Cdecl ios::eof() { return state & eofbit; } inline int _Cdecl ios::fail()

{ return state & (failbit | badbit | hardfail); } inline int _Cdecl ios::bad() { return state & (badbit | hardfail); }

inline int _Cdecl ios::good() { return state == 0; } inline long _Cdecl ios::flags() { return x_flags; } inline int _Cdecl ios::width() { return x_width; } inline int _Cdecl ios::width(int _w)

{ int _i = x_width; x_width = _w; return _i; } inline char _Cdecl ios::fill(char _c)

{ char _x = x_fill; x_fill = _c; return _x; } inline int _Cdecl ios::precision(int _p)

{ int _x = x_precision; x_precision = _p; return _x; } inline _Cdecl ios::operator void _FAR *()

{ return fail() ? 0 : this; }

inline int _Cdecl ios::operator! () { return fail(); }

Як бачимо, клас ios містить, ряд функцій, такі як :

int bad() - вертає ненульове значення, якщо вiдбулась помилка; void clear(int=0) - встановлює стан потоку;

int eof() - вертає ненульове значення, якщо досягнуто кiнець файлу.та інші (див. вище).

Всi функцiї бiблiотеки вводу/виводу можна розбити на двi основнi групи: функцiї нижнього та верхнього рiвнiв (аналогічно як в ANSI С). На нижньому рiвнi С++ iнтерпретує файл як послiдовнiсть байт.

335

Концепцiя типу даних зникає i вiдповiднi функцiї здiйснюють просто перенос байт.

Бiблiотечнi функцiї верхнього рiвня перетворюють типiзованi данi в послiдовнiсть байт i навпаки.

Базовi операцiї вводу в С++ пiдтримуються класом istream, виводу - класом ostream. Двонаправлений ввiд/вивiд пiдтримується класом iostream, який є похiдним вiд istream, ostream (див. мал.1).

Бiблiотека iostream має 4 об'єкти-потоки:

1. cin - екземпляр класу istream_withassign.

Цей об’єкт асоцiйований iз стандартним вводом.

2. cout - екземпляр класу ostream_withassign.

Цей об’єкт асоцiйований iз стандартним виводом.

3. cerr - екземпляр класу ostream_withassign.

Цей об’єкт асоцiйований з стандартним пристроєм помилок (екраном), виконує небуферизований вивiд в стандартний потiк stderr.

4. clog - екземпляр класу ostream_withassign. Він є аналогом попереднього, але здiйснює буферизований вивiд.

Для кожного з перерахованих потокiв(екземплярiв класiв) перевантажені два оператори: оператор запису в потiк << та читання з пото-

ку : >>.

Чому для перевантаження були використані саме оператори зсуву вправо/влiво? В процесі проектування , звичайно, пророблялись різні варіанти. Припустимо, що для перевантаження використана, наприклад, операція присвоювання. Зразу ж кидається в очі однаковість символів операцій вводу та виводу. Це, звичайно, не бажано. Розглянемо, наприклад, таку послідовність дій: cout=a=b;

Виходячи з специфіки оператора присвоювання, такий запис розшифрується як cout=(a=b). Очевидно, що така конструкція приведе до помилки.

Операцiя зсуву має досить низький прiорiтет, що дозволяє не використовувати дужки у випадках, коли в ролі операндів виступають арифметичні вирази:

cout<< a*b+c;

Очевидно, що при виводі значень виразів, що використовують операції більш низького пріоритету, дужки необхідні:

cout<<“a^b|c=“<<( a^b|c)<<“\n”;

На перший погляд, система вводу-виводу С++ є досить громіздкою. Проте, “класовий” ввід-вивід має ряд переваг:

336

1)надiйнiсть пов'язана з пiдтримкою контролю типiв. Сімейства функцій printf/scanf не передбачають ніякого контролю типів даних. Компілятор не попереджає про невідповідність типів значень та відповідних специфікацій. Механiзм потокiв у С++ , який грунтується на перевантаженні функцій та операцій, забезпечує для кожного типу даних свою перевантажену функцiю.

2)розширюванiсть. Завдяки полiморфiзму однi i тi ж самi оператори можуть взаємодiяти з потоками рiзних типiв. Легко можна написати і перевантаженi функцiї , які будуть працювати з специфічними типами даних користувача.

3)простота та послiдовнiсть. Використання перевантажених функцiй дає можливiсть використовувати однотипний iнтерфейс вводу/виводу.

Розглянемо, як можна використовувати класи вводу-виводу.

1.2 ПЕРЕАДРЕСАЦІЯ ВВОДУ-ВИВОДУ

Імена cin та сout можна перепризначати для специфічних об'єктiв-потокiв користувача. Така можливість дозволить в програмi легко переадресовувати ввід або вивiд.

Приклад:

#include<iostream.h>

#include<fstream.h> const int max_line=80; ifstream ifs;

int main(int argc,char *argv[] )

{ //якщо вказаний аргумент...

if (argc > 1)

{ //cпроба вiдкрити файл ifs.open( argv[1] );

//якщо успiшна,переадресувати ввiд if (ifs)

cin=ifs; }

//запросити ввiд...

cout<<"Введiть рядок тексту:"; //прочитати данi iз стандартного вводу char line[max_line]; cin.getline(line,sizeof(line) );

//показати введенi данi...

cout<<endl<<"Ви ввели :"<<line; return 0;}

1.3 РОЗШИРЕННЯ ПОТОКІВ ДЛЯ ТИПІВ КОРИCТУВАЧА

337

В класах istream та ostream перевантажуються операції запису та зчитування з потоку для всіх стандартних типів даних. Таке перевантаження дозволяє використовувати однаковий синтаксис для вводу та виводу даних різних типів:

int i; char c; double d;

cout<<i<<c<<d;

Можливість послідовного застосування кількох операції забезпечується тим, що перевантажені операції вводу/виводу повертають посилання на об’єкт відповідного типу.

Бiблiотека iostream легко може бути розширеною для роботи з типами даних користувача. Для цього необхідно визначити двi функцiї iз слiдуючими заголовками:

// Читання даних з потоку

istream& operator >>(istream& is, iм'я_типу

&varName);

// Запис даних в потiк

ostream& operator <<(ostream& os, iм'я_типу

&varName);

Наступний приклад ілюструє читання та запис в потік:

#include <iostream.h> #include <assert.h> struct NewType

{int x;

int y;};

// Читання:передбачений формат '(**,**)' istream& operator >> (istream &is,NewType &nt)

{char c; cin >> c;

assert(c== '(' ); cin >> nt.x;

cin >> c; assert(c== ',' ); cin >> nt.y;

cin >> c; assert(c==')' );

return is;}

//Запис:формат "(**,**)”

ostream & operator << (ostream &os, NewType &nt)

{os << '('<< nt.x<< ','<< nt.y<< ')'; return os;}

int main(void)

{cout <<"Введiть два числа”;

338

int ix; cin >> ix; int iy; cin >> iy; NewType nt; nt.x=ix; nt.y=iy;

cout << "Введено значення:"<< nt<< endl; return 0;}

1.4 ОПЕРАЦІЇ РОБОТИ З ПОТОКОМ ЯК ДРУЖНІ

При використаннi класiв прийнято оголошувати операцiї видалення i помiщення друзями класу. Таке оголошення забезпечує для операцiї доступ до закритих полів даних при форматуваннi виводу.

Приклад:

#include <іostream.h> class IOFriend

{ // ...(закриті данi) public:

//

// ...

//

friend istream& operator >> (istream&,IOFriend&); friend ostream& operator<<(ostream&,const IOFriend&); };

ostream& operator <<(ostream &os,const IOFriend &p)

{// може використовувати для форматування

//закриті данi...

return os; }

istream& operator >>(ostream &is,const IOFriend &p)

{// може використовувати для форматування

//закриті данi...

return is; }

1.5 ФОРМАТОВАНИЙ ВВІД-ВИВІД

Бiблiотека потокiв C++ передбачає три способи управлiння форматом вихiдних даних: виклик форматуючих функцiй-елементiв, використання прапорцiв та використання манiпуляторiв.

Функцiї для форматування, якi знаходяться в класi ios, перевантаженi так, щоб забезпечити можливiсть як для читання, так i для встановлення управляючого атрибуту. Часто для атрибутiв, якими можна управляти за допомогою функцiй, бiблiотека потокiв C++ передбачає

339

також манiпулятори. Нижче описані атрибути, для управління якими в класі ios описані функції-члени.

1.5.1 Ширина поля

Для читання i встановлення ширини поля потоку в класi ios є функцiї width:

int ios:: width(); -повертає поточне значення внутрiшньої ширини поля

int ios::width(int); -встановлює значення внутрiшньої змiнної ширини поля;

при вводi, метод width можна використовувати для задання максимального числа символiв для читання.

при виводi, метод задає мiнiмальну ширину поля.

якщо ширина поля менше заданої, вивiд доповнюється заповнюючими символами fill.

якщо при виводі вихiдне поле бiльше вказаного, значення width iгнорується.

по замовчуванню, значення width рiвне 0 (вивід не доповнюється і не обрізається).

width обнуляється пiсля кожного помiщення даних в потiк. Приклад 1:

char name[max];

cout <<'Введiть iм'я(max"<<max-1<<"символiв):"; cin .width(max);

cin >>name;

cout <<"hello",<<name<<,'!';

Приклад 2(вирівнювання правого поля при виводі чисел:) const int FLD=10;

int main(void) { int x1=2867; int y1=20051; int z1=017; cout.width(FLD); cout <<x1<<'\n'; cout.width(FLD); cout <<y1<<'\n'; return 0; }

1.5.2 Заповнюючий символ

Для читання або змiни бiжучого заповнюючого символа можна застосовувати функцiї ios::fill

340

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]