osn_progr_final
.pdf{ifs.read((char*)& i,sizeof(i)); ifs.read((char*)& l,sizeof(l)); ifs.read((char*)& d,sizeof(d)); ifs.close();}
return 0;}
Функція get()
Призначена для читання символа. istream & istream::get(char&)
Приклад (читається символ потоку): while(cin.get(ch))
{if(ch=='#') break;}
або: while(ch=cin.get())!='#')
{if(ch==EOF) break)}
Використовують також один з перевантажених варіантів функції get: istream& istream::get(char&);
istream& istream::get(signed char&); istream& istream::get(unsigned char&);
Приклад:
int main(void) {char ch;
cout<<“введіть число, за яким слідує #”; while(cin.get(ch))
{if(ch=='#') break; cout<<(char)ch; } return 0; }
Є ще функцiї для читання рядка get та getline: istream & istream::get (char*p,int l,char t);
Функцiя зчитує символи з потоку доти, поки не буде знайдений обмежувач t , не буде прочитано l символiв, або не зустрiнеться кiнець файлу.
Функція getline()
istream & istream::getline(char*p, INT l,char t)
Дiє аналогiчно до функцiї get. Рiзниця мiж функцiями get i getline полягає в
тому, що getline зчитує обмежувач, але не записує його в буфер.
Функція ignore()
Функцiя зчитує не бiльше n символiв з потоку, поки не зустрiнеться обмежувач (по замовчанню - EOF).
istream & istream::ignore(int n=1,int d=EOF) Функція grount()
351
Повертає число символiв, якi були прочитанi останньою функцiєю неформатованого вводу.
int istream::grount();
Функція peek()
Повертає значення чергового символу, не дiстаючи його з вхiдного потоку.
int istream::peek(); Функція putback()
Повертає в потiк останнiй символ, який був прочитаний. istream & istream::putback(charch)
Функції seekg та seekp
Можуть використовуватись для позиціонування вказівника зчитування /поміщення відповідно вхідного та вихідного потоку:
istream& istream::seekg(streampos);
istream& istream::seekg(streampos,ios::seek_dir); istream& ostream::seekp(streampos);
istream& ostream::seekp(streampoff,ios::seek_dir); Функції tellg() та tellp()
Дозволяють знайти біжучу позицію відповідно вхідного та вихідногої потока:
streampos istream::tellg(); streampos istream::tellp();
1.10 ФОРМАТУВАННЯ В ПАМ’ЯТІ
Бібліотека потоків С++ передбачає операції вводу-виводу над даними в пам’яті (резидентними потоками), що реалізовані в класах istrstream та ostrstream. Клас istrstream забезпечує iнтерфейс для фор-
матого читання з пам'ятi, ostrstream - запису в пам'ять, strstream підтримує обидва типи операцій.
Приклад:
char info[ ]="вiдмiнникiв 5", "двiйочникiв 2";
istrstream stat(info); char name[20];
int mark;
for(int i=0; i<2; i++) {stat >>name;
stat >>mark; cout <<"в ПМ 21"
<<name
352
<<mark
<<endl;}
Клас ostrstream надає інтерфейс для форматованого виводу в пам’ять. В класі є конструктор по замовчуванню, який виділяє буфер та динамічно змінює його розмір під час виконання. Також є конструктор, який дозволяє задавати буфер та його розмір. Розглянемо приклад:
#include<strstream.h> int main(void)
{//створюється потік з динамічним буфером ostrstream osstr_a;
char buffer[100];
//створюється потік з заданим буфером ostrstream osstr_b(buffer, sizeof(buffer)); //...return 0;}
Крім конструкторів та деструктора ostrstream має такі методи: char*ostrstream::str()- повертає вказівник на буфер ostrstream. Крім того, він “заморожує ” масив, робить динамічний буфер власністю користувача. Після виклику str() необхідно знищити буфер або передати його у власність потока ostrstream так:
oss.rdbuf()->freeze(0);
int ostrstream::pcount()-повертає кількість байт, записаних в буфер. Розглянемо приклад:
#include<strstream.h> int main(void) {ostrstream oss;
int i=10;
char *str=“значення рівне”; oss<<str<<i<<endl;
//вивести результат форматування cout<<oss.str();
//повернути буфер для очистки у власність oss oss.rdbuf()->freeze();
return 0;}
Відмітимо, що при роботі з об’єктами ostrstream необхідно в явному вигляді добавити до потоку нуль-символ. В противному випадку метод str() поверне вказівник на рядок, не обмежений нуль-символом.
Можна створити потiк ostrstream в режимi доповнення, встановивши бiт в ios::ate або ios::app в опціональному третьому параметрі конструктора.Це дозволить передати в якості буфера обмежений нулем рядок і поміщення в буфер почнуться з позиціії нульового символа. Такий режим можна використати для конкатенації рядків. При-
353
клад:
char msg[100]=“Час=”; ostrstream
oss(msg,sizeof(msg),ios::out|ios::app); char time=“11:20:34”;
oss<<time;
cout<<oss.str(); oss.rdbuf()->freeze();
ЗАВДАННЯ ДЛЯ САМОСТІЙНОЇ РОБОТИ
1.Переадресовуючи стандартний ввід-вивід, написати програму копіювання вмісу деякого текстового файлу у інший файл.
2.Описати клас complex та перегрузити оператори виводу в потік та вводу, визначивши їх як
а) члени класу б) дружні
3.Написати програму, в якій
а) відкривається файл та прикріплюється до об’єкта; б) конструюється об’єкт та прикріплюється до вже відкритого файла; в) відкривається файл за допомогою метода open(). Продемонструвати різні режими доступу
4.Описавши клас myfile, полем якого виступає змінна типу *FILE, визначити ряд власних функцій роботи з файлом (відкриття в текстовому та бінарному режимах, запису та зчитування інформації, закриття), використовуючи відповідні бібліотечні функції С++. Для відкриття файлу використовувати конструктори файлових потоків.
5.Дописати на початок деякого виконуваного файлу інший ехе-файл, який виконується замість вихідного.
6.Написати функцію, яка, відкриваючи файл, що є С++-програмою,
а) дописує в неї перегружену до однієї з функцій так, щоб вона викликалась замість вихідної (змінюється сигнатура);
б) знищує всі коментарі; в) перевіряє відповідність відкриваючих та закриваючих
дужок;
г) використовуючи набір певних директив define, кодує
окремі фрагменти програми (з підключенням відповідного файлу директив
препроцессору щоб зберігалась правильність програми); д) дописує на початок програми фрагмент коду, який видає
певне повідомлення та перериває роботу програми на деякий час;
354
7. Вивести числоа) з заданою шириною поля б) з заданою точністю в) з заданим заповнюючим символом г) з вказаною основою д) з вказаною нотацією
е) з вирівнюванням по лівому(правому) краю поля є) з вказанням знаку числа Використати прапорці форматування та методи роботи з ними.
8.Виконати попередню вправу з використанням маніпуляторів.
9.Визначити екземпляр класу ostrstream з динамічним буфером. Записати в буфер різнотопні дані та вивести результат форматування за допомогою метода str(). Повернути буфер для очистки у власність об’єкта за допомогою метода freeze().
Перевірити, що знаходиться в буфері після виклику цього методу. Чи можна ще дописувати якісь дані в буфер після виклику freeze()?
2 УПРАВЛІННЯ ВИКЛЮЧЕННЯМИ
Під управлінням виключеннями(exeption handling) розуміють стандартний інтерфейс для виявлення та обробки незвичайних, непередбачених та виключних станів чи подій. Воно представляє формальний спосіб відхилити потік управління функції на неспецифіковану секцію кода, готову прийняти контроль над даною виключною ситуацією.
Виключення мають ряд переваг над традиційними методами повідомлень про помилкові стани:
єдинообразність стилю, яка покращує читабельність програми;
відсутність глобальних змінних та визначених користувачем процедур, що використовуються в таких цілях;
розширення можливостей відладки; механізм виключень є інтегрованою частиною мови та використовує підтримку бібліотек;відладчик забезпечує спеціалізовану підтримку, дозволяючи відслідковувати виключні ситуації;
неможливість ігнорування помилок та продовження виконання програм.
2.2 РОБОТА З УПРАВЛІННЯМ ВИКЛЮЧЕННЯМИ МОВИ С++
2.2.1 Синтаксис основних конструкцій
Управління виключеннями С++ є частиною стандарта ANSI
355
C++. Цей стандарт підтримує кінцеву модель управління: після того, як управління було зафіксоване, процедура обробки не може вимагати, щоб виконання було продовжене з точки виключення. Виключення С++ також не підтримують обслуговування асинхронних подій, таких, як помилки обладнання чи обробку переривань. Обслуговуються лише виключення, які в явному вигляді сигналізуються деякою функцією. В контексті управління виключеннями в С++ використовуються три ключових слова: try, catch та throw.
2.2.1.1 Використання try та сatch
Ключове слово try служить для позначення блока кода, який може генерувати виключення. Блок поміщується в фігурні дужки:
try
{cout<<“in try-block . . .”<<endl;
func(); //функція може генерувати виключення
}
Ключове слово catch слідує за try-блоком і позначає секцію кода, в яку може бути передано управління в тому випадку, коли відбудеться виключення (називають catch-обробник чи обробник виключення). За ключовим словом слідує опис виключення, поміщений у фігурних дужках, який складається з імені типу та необов’язкової змінної. Ім’я типу ідентифікує тип виключення, яке даний код може обслуговувати. Можна розглянути опис виключення як параметр функції . За try-блоком може слідувати кілька операторів catch. Розглянемо приклад:
#include<iostream.h> void func(void){}; int main(void)
{try
{ cout <<“in try=block . . .”<<endl; func();//може генерувати виключення
}
catch(int i){ } catch( const char*){ }
//обробляє всі необслужені виключення catch(...){ }
return 0;}
Оператор catch з трьома крапками (...) перехоплює виключення будьякого типу і повинен бути останням з операторів catch, що слідують за try-блоком.
356
2.2.1.2 Використання throw
Ключове слово throw викидає виключення і викликає перехід управління до обробника. За ключовим словом може слідувати вираз.
throw з операндом
Вираз, що слідує за throw, зводиться до значення змінної певного типу. Можна розглядати операнд throw як аргумент виклику функції. Тип операнда визначає, який з обробників може перехопити виключення. Місцезнаходження оператора throw називають точкою викиду.
Приклад:
int m=1,n=2; void func1()
{if (m) throw " виявлена по-милка"; } void func2()
{if(n)
{char *str="Oй !!!!!!!";
throw str;}
}
main()
{
try{
func1();
func2();
}
catch(...){cout<<"dffhs";}
}
Відмітимо, що викидатись можуть як стандартні типи, так і типи користувача. Тоді можна передати обробнику об’єкт, навантажений інформацією.
Перехоплення throw
Коли виконується оператор throw , функції виконуючої бібліотеки С++ виконують наступні дії:
створюють копію викинутого об’єкта-змінної;
Розмотують стек, викликаючи деструктори локальних об’єктів, які виходять з області видимості;
передають управління найближчому обробнику catch , який приймає параметр, сумісний за типом з викинутим об’єктом. Копія
об’єкта передається обробнику в якості параметра. Розглянемо приклад:
#include<fstream.h>
357
class Test {public: Test(char* s)
{ cout<<“Утворився екземпляр”<<s<<” класу
Test”<<endl;}
~Test()
{cout<<“Екземпляр класу Test знище-
ний...”<<endl;}
};
void function1(void)
{ifsteram ifs(“\\invalid\\file\\name”); if (!ifs)
{cout<<“викидаємо виключення”<<endl; throw “помилка при відкритті файла”;}
}
void function2(void)
{//створимо локальний об’єкт, щоб перевірити виклик деструктора при //розмотуванні стека
Test tell(“tell”);
//викликається функція, що викидає виключення function1();}
int main(void)
{try {cout<<“увійшли в try-блок”<<endl; function2();
cout<<“виходимо з try-блока”<<endl;} catch(int i){cout<<“викликаний обробник int”<<endl;
return -1;} catch(const char *p)
{ cout<<“викликаний обробник char*”<<endl;; return -1;}
catch(...)
{cout<<“викликаний обробник catch_all”<<endl; return -1;}
return 0; // обійшлося без пригод}
Результати роботи:
увійшли в try-блок
Утворився екземпляр ptell класу Test викидаємо виключення
Екземпляр класу Test знищений...
викликаний обробник char*
Відмітимо наступні моменти: деструктор локальної змінної ptell був викликаний вірно, хоч і з функції function1 управління було передане безпосередньо обробнику виключення; оператор з main() , який міс-
358
тить повідомлення “виходимо з try-блока “ так і не був виконаним.
2.2.2 Тип виключення та конструктор копії
Якщо конструктор копії викинутого об’єкта в точці викиду недоступний, генерується повідомлення про помилку. Не можна викинути тип чи вказівник на тип , що не має public-конструктора копіювання ( якщо тільки функція, що викидає виключеня, не є дружньою).
Приклад:
#include<iostream.h> class Top
{public:
Top(){};
protected: Top(const Top& a){};
friend void main(void); };
void funcA(void) { if(1){
Top np;
throw np;//повідомлення про помилку: //Top::Top(const
Top&) is not accessible in func-tion //funcA()} //...
}}
void main(void) { if(2)
try{ Top np;
throw np;// все нормально}//...
}
catch(...){cout<<"yes";}
}
Відмітимо, що в прикладі при генерації виключення в функції funcA видається повідомленння про помилку. Цікаво, що функція main() також може бути оголошеною дружньою до класу.
2.2.3 Пошук відповідного типу виключення
Після того, як виключення вже викинуто, процедури виконуючої бібліотеки С++ шукають відповідний обробник. Обробник вважається знайденим, якщо:
Тип викинутого об’єкта точно співпадає з типом, який заданий в
359
обробнику. Іншими словами, якщо викидається Т, то йому відповідає обробник, який перехоплює T,const T, T& чи const T&.
Тип обробника є public-базовим класом викинутого об’єкта
обробник чекає вказівник і викинутий об’єкт є вказівником, який
може бути перетворений до типу обробника за стандартними правилами перетворення вказівників.
Необхідно пам’ятати, що послідовність слідування обробників є суттєвою. Обробник, який чекає виключення базового класу, автоматично ховає обробник похідного класу:
#include<iostream.h> class Base{ };
class Derived:public Base{ }; void funcA()
{Derived d; throw d;} void funcb()
{throw “помилка в funcB()”;}
int main(void) { try{funcA();} catch(Base &)
{cout<<“перехоплений Base&”<<endl;} catch(Derived&)
{cout<<“перехоплений Derived&”<<endl;}
При такій послідовності обробник Derived& ніколи не зможе отримати управління. Треба розмістити оператори catch навпаки. Аналогічно в наступному прикладі:
try{
funcB();}
catch(void*)
{cout<<“перехоплений void*”<<endl;} catch(const char*)
{cout<<“перехоплений const char*”<<endl;} return 0;}
2.2.4 Використання terminate() та некеровані виключення
Якщо для деякого виключення не знайдено відповідного обробника, викликається функція terminate(), яка в свою чергу викликає функцію abort(), що аварійно завершує біжучий процес. Можна встановити свою власну функцію завершення за допомогою функції set_terminate, визначеної в файлі exept.h:
typedef void (_RTLENTRY * terminate_function)()//...
360
