- •2.1 Елементи концепції ооп .. 20
- •1.1 Коментарі.
- •1.2 Прототипи функцій.
- •1.3 Операція розширення області видимості.
- •1.4 Оголошення в операторах.
- •1.5 Перегрузка функцій.
- •1.6 Значення формальних параметрів по замовчуванню.
- •1.7 Посилання та вказівники.
- •1.8 Специфікатор inline
- •1.9 Операції new та delete .
- •1.10 Вказівник на void.
- •1.11 Зв’язування із збереженням типів
- •1.12 Про структури та об’єднання.
- •2.1 Елементи концепції ооп.
- •2.3 Опис протоколу класу.
- •2.4 Передача повідомлень об’єктам.
- •3 Функції-члени.
- •3.1 Функції-члени в межах та за межами формального опису класу.
- •3.2 Про вказівник this.
- •3.3 Перевантаження функцій-членів. Параметри по замовчуванню.
- •4. Конструктори та деструктори.
- •4.1 Поняття про конструктори.
- •4.2 Деструктори.
- •4.3 Досягнення високої ефективності. Конструктор копіювання.
- •5 Глобальні та локальні об’єкти.
- •6 Статична пам’ять та класи.
- •7. Наслідування
- •7.1 Синтаксична реалізація наслідування
- •7.2 Правила доступу до полів даних
- •7.3 Конструктори та деструктори в похідних класах
- •7.4 Використання заміщуючих функцій-членів.
- •7.5 Похідні класи та вказівники.
- •7.6 Ієрархія типів
- •7.7 Множинне наслідування
- •8 Вiртуальнi функцiї та класи
- •8.1 Віртуальні функції.
- •8.2 Чисті віртуальні функції. Абстрактні класи.
- •8.3 Віртуальні деструктори.
- •8.4 Посилання як засіб для реалізації поліморфізму
- •8.5 Технічна реалізація механізму віртуальних функцій.
- •8.6 Віртуальні базові класи
- •8.6.1 Ієрархії класів та наслідування
- •8.6.2 Віртуальні базові класи
- •8.6.3 Виклик конструкторів та віртуальні базові класи.
- •9 Друзі
- •9.1 Дружні класи.
- •9.2 Дружні функції.
- •10 Перевантаження операторiв.
- •10.1 Перевантаження операторів. Загальний підхід.
- •10.2 Перетворення типів.
- •10.3 Перевантаження деяких операторів.
- •10.3.1 Оператор індексування масиву.
- •10.3.2 Перевантаження оператора виклику функції.
- •10.3.3 Оператор доступу до члена класу.
- •10.3.4 Перевантаження операторів інкремента та декремента.
- •10.3.5 Перевантаження операторів управління пам’яттю (new,delete).
- •10.3.6 Перевантаження оператора присвоювання.
- •11.1 Функціональні шаблони
- •11.1.1 Визначення та використання шаблонів функцiй.
- •11.1.2 Перевантаження шаблонiв функцiї.
- •11.1.3 Cпецiалiзованi функцiї шаблона.
- •11.2 Шаблони класів.
- •11.2.1 Визначення шаблонів класу
- •11.2.2 Константи та типи як параметри шаблону
- •11.2.3 Використання шаблонних класів
- •11.2.4 Спецiалiзацiя шаблонiв класу.
- •11.3 Шаблони та конфiгурацiя компiлятора.
- •11.3.1 Шаблони Smart.
- •11.3.2 Шаблони Global I External.
- •12.2 Переадресація вводу-виводу
- •12.3 Розширення потоків для типів кориcтувача
- •12.4 Операції роботи з потоком як дружні
- •12.5 Форматований ввід-вивід
- •12.5.1 Ширина поля
- •12.5.2 Заповнюючий символ
- •12.5.3 Число цифр дійсних чисел
- •12.5.4 Прапорці форматування
- •12.5.5 Маніпулятори
- •12.6 Стан потоку
- •12.7 Файловий ввід-вивід
- •12.7.1 Конструктори файлових потокiв
- •12.7.2 Вiдкриття файлу
- •12.8 Неформатований ввід-вивід
- •12.9 Деякі функції вводу-виводу
- •12.10 Форматування в пам’яті
- •13 Управління виключеннями
- •13.1 Виключення та стек
- •13.2.1 Синтаксис основних конструкцій
- •13.2.1.1 Використання try та сatch
- •13.2.1.2 Використання throw
- •13.2.2 Тип виключення та конструктор копії
- •13.2.3 Пошук відповідного типу виключення
- •13.2.4 Використання terminate() та некеровані виключення
- •13.2.5 Робота з специфікаціями виключень
- •13.2.6 Робота з непередбаченими виключеннями
- •13.2.7 Робота з конструкторами та виключеннями
- •13.2.8 Динамічні об’єкти
- •13.2.9 Передача значень з конструктора та деструктора
- •13.2.10 Робота з ієрархіями виключень
- •13.2.11 Робота з специфічними класами виключень
- •13.3 Структурне управління виключеннями
- •13.3.1 Використання кадрованого управління виключеннями
- •13.3.1.1 Синтаксис
- •13.3.1.2 Про функцію RaiseException()
- •13.3.1.3 Фільтруючий вираз
- •13.3.1.4 Перехоплення виключення процесора
- •13.3.2 Використання завершуючих обробників виключень
11.3.2 Шаблони Global I External.
Якщо створюються чи використовуються представники шаблона до того, як його повне визначення стане вiдомим компiлятору, необхiдно комбiноване застосування опцiй щаблонiв Gobal i External. Цi опцiї повиннi застосовуватися i в тому випадку , коли необхiдно, щоб компiлятор генерував код для деякого шаблонного представника, не створюючи сам представник.
Для правильного використання опцiй Global i External необхiдно
переконатись, що:
-для кожної комбiнацiї шаблон/тип тiльки один модуль, що створює її
представникiв, компiлюється з дозволом опцiї шаблонiв global.
-всi інші модулi, що створюють представникiв даної комбiнацiї
шаблон/тип, використовують опцiю external.
Приклад:
#pragma option -Jgd
template <class T>
class C
{
public:
C();
};
template <class T>
C<T>::C()
{
// тiло конструктора
}
C<int> CofInt; // створений представник
pragma option -Jgx
template <class T>
class C
{
public:
C();
};
int main( void )
{
C<int> iC;
return 0;
}
Недолiки шаблонiв:
-Програма буде мiстити повний код представникiв шаблона для кожного iз породжуючих типiв. Це може сильно збiльшити розмiр виконуваного модуля.
-Часто реалiзацiя шаблона добре працює з деякими типами, але далека вiд оптимальних в iнших випадках *. Для специфiчних типiв для пiдвищення ефективностi даних бажано використовувати спецiальнi шаблони.
Контрольні запитання
1. Як задається шаблон функції ?
2. Чи можна перевантажувати функціональні шаблони ?
3. Що таке спеціалізована функція шаблона?
4. Як визначаються шаблони класів ?
5. Як використовуються шаблонні класи ?
6. Які опції компілятору необхідно задавати при роботі з шаблонами класів?
7. Нехай визначена фукція f() з деякими типами параметрів та функціональний шаблон f(), з якого можна згенерувати функцію з такими самими типами параметрів, але іншою дією. Яка функція f() буде викликана?
Завдання 11:
1. Вивчити роботу наведеного нижче шаблону для різних типів даних з точки зору швидкодії програми:
template <class T> inline const T& min ( const T& t1, const T& t2 )
{
return t1>t2 ? t2 : t1;
}
template <class T> inline const T& max ( const T& t1, const T& t2 )
{
return t1>t2 ? t1 :t2;
}
2. Написати функціональний шаблон сортування масиву елементів довільного базового типу. Порівняти розмір виконуваного коду шаблону та відповідних функцій.
3. Написати функціональний шаблон сортування масиву елементів довільного типу (включаючи класи користувача ).
4. Доповнити та протестувати приклад шаблонного класу Tarray .
5. Описати шаблон деякого класу з опцією Global та відповідний шаблон з опцією External в іншому модулі. Скомпілювати та протестувати програму.
6. Написати програму для ілюстрації дій компілятора при виклиці функції. Для цього визначити деяку функцію, функціональний шаблон та розглянути випадки:
а) параметри при виклиці функції точно відповідають тим, що вказані при її описі ;
б) параметри при виклиці в точності не відповідають вказаним при описі, але існує шаблон, який дозволяє згенерувати функція з точною відповідністю параметрів;
в) не існує функції чи шаблону з точною відповідністю параметрів, але знаходимось в межах правил перетворення типів.
12 ВВIД-ВИВIД В С++
.1 2.1 Система вводу-виводу С++.
С++ має свою досить складну систему вводу-виводу, що грунтується на роботі з класами. Можна сказати, що на функціональному рівні ( відповідні функції вводу-виводу) система вводу- виводу даних ANSI C збереглася і в С++. Проте на рівні самої організації вона принципово змінилася. З чим пов’язані ці зміни ? Це зумовлено, мабуть, специфікою самої мови. Адже С++ орієнтується на роботу з с типами даних, створених користувачем. Наявність в С++ апарату перевантаження функцій та операторів відкриває нові можливості для створення засобів вводу-виводу. Використання цих можливостей привело до утворення цілої ієрархії класів, кожен з яких містить свої поля даних та функції-члени.
ios
istream ostream fstreambase
iostream ostream_withassign
iostream_whithassign ofstream
istream_withassign fstream
ifstream
мал.1
ios
istream ostream strstreambase
iostream ostrstream
strstream
istrstream
мал. 2
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:
// stream status bits
enum io_state {
goodbit = 0x00, // no bit set: all is ok
eofbit = 0x01, // at end of file
failbit = 0x02, // last I/O operation failed
badbit = 0x04, // invalid operation attempted
hardfail = 0x80 // unrecoverable error
};
// stream operation mode
enum open_mode {
in = 0x01, // open for reading
out = 0x02, // open for writing
ate = 0x04, // seek to eof upon original open
app = 0x08, // append mode: all additions at eof
trunc = 0x10, // truncate file if already exists
nocreate = 0x20, // open fails if file doesn't exist
noreplace= 0x40, // open fails if file already exists
binary = 0x80 // binary (not text) file
};
// stream seek direction
enum seek_dir { beg=0, cur=1, end=2 };
// formatting flags
enum {
skipws = 0x0001, // skip whitespace on input
left = 0x0002, // left-adjust output
right = 0x0004, // right-adjust output
internal = 0x0008, // padding after sign or base indicator
dec = 0x0010, // decimal conversion
oct = 0x0020, // octal conversion
hex = 0x0040, // hexadecimal conversion
showbase = 0x0080, // use base indicator on output
showpoint = 0x0100, // force decimal point (floating output)
uppercase = 0x0200, // upper-case hex output
showpos = 0x0400, // add '+' to positive integers
scientific= 0x0800, // use 1.2345E2 floating notation
fixed = 0x1000, // use 123.45 floating notation
unitbuf = 0x2000, // flush all streams after insertion
stdio = 0x4000 // flush stdout, stderr after insertion
};
// constants for second parameter of seft()
static const long basefield; // dec | oct | hex
static const long adjustfield; // left | right | internal
static const long floatfield; // scientific | fixed
// constructor, destructor
_Cdecl ios(streambuf _FAR *);
virtual _Cdecl ~ios();
// for reading/setting/clearing format flags
long _Cdecl flags();
long _Cdecl flags(long);
long _Cdecl setf(long _setbits, long _field);
long _Cdecl setf(long);
long _Cdecl unsetf(long);
// reading/setting field width
int _Cdecl width();
int _Cdecl width(int);
// reading/setting padding character
char _Cdecl fill();
char _Cdecl fill(char);
// reading/setting digits of floating precision
int _Cdecl precision(int);
int _Cdecl precision();
// reading/setting ostream tied to this stream
ostream _FAR * _Cdecl tie(ostream _FAR *);
ostream _FAR * _Cdecl tie();
// find out about current stream state
int _Cdecl rdstate(); // return the stream state
int _Cdecl eof(); // non-zero on end of file
int _Cdecl fail(); // non-zero if an operation failed
int _Cdecl bad(); // non-zero if error occurred
int _Cdecl good(); // non-zero if no state bits set
void _Cdecl clear(int = 0); // set the stream state
_Cdecl operator void _FAR * (); // zero if state failed
int _Cdecl operator! (); // non-zero if state failed
streambuf _FAR * _Cdecl rdbuf(); // get the assigned streambuf
// for declaring additional flag bits and user words
static long _Cdecl bitalloc(); // acquire a new flag bit, value returned
static int _Cdecl xalloc(); // acquire a new user word, index returned
long _FAR & _Cdecl iword(int); // return the nth user word as an int
void _FAR * _FAR & _Cdecl pword(int); // return the nth user word as a pointer
static void _Cdecl sync_with_stdio();
// obsolete, for streams 1.2 compatibility
int _Cdecl skip(int);
protected:
// additional state flags for ispecial and ospecial
enum { skipping = 0x100, tied = 0x200 };
streambuf _FAR * bp; // the associated streambuf
ostream _FAR * x_tie; // the tied ostream, if any
int state; // status bits
int ispecial; // istream status bits
int ospecial; // ostream status bits
long x_flags; // formatting flag bits
int x_precision; // floating-point precision on output
int x_width; // field width on output
int x_fill; // padding character on output
int isfx_special; // unused
int osfx_special; // unused
int delbuf; // unused
int assign_private; // unused
/*
* The data members marked with above are not documented in the AT&T
* release of streams, so we cannot guarantee compatibility with any
* other streams release in the use or values of these data members.
* If you can document any expected behavior of these data members, we
* will try to adjust our implementation accordingly.
*/
_Cdecl ios(); // null constructor, does not initialize
void _Cdecl init(streambuf _FAR *); // the actual initialization
void _Cdecl setstate(int); // set all status bits
static void _Cdecl (*stdioflush)();
private:
// for extra flag bits and user words
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 &); // declared but not defined
void _Cdecl operator= (ios _FAR &); // declared but not defined
};
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(); }
Він містить, ряд функцій, такі як :
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сть байт. Концепц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ставання з потоку : >>.
Чому для перевантаження були використані саме оператори зсуву вправо/влiво? В процесі проектування , звичайно, пророблялись різні варіанти. Припустимо, що для перевантаження використана, наприклад, операція присвоювання. Зразу ж кидається в очі однаковість символів операцій вводу та виводу. Це, звичайно, не бажано . Розглянемо, наприклад, таку послідовність дій: cout=a=b; Виходячи з специфіки оператора присвоювання, такий запис розшифрується як cout=(a=b). Очевидно, що така конструкція приведе до помилки.
Операцiя зсуву має досить низький прiорiтет, що дозволяє не використовувати дужки у випадках, коли в ролі операндів виступають арифметичні вирази:
cout<< a*b+c;
cout<< (a^b/c).
Очевидно, що при виводі значень виразів, що використовують операції більш низького пріоритету, дужки необхідні:
cout<<“a^b|c=“<<( a^b|c)<<“\n”;
На перший погляд, система вводу-виводу С++ є досить громіздкою. Проте, “класовий” ввід-вивід має ряд переваг:
1) надiйнiсть пов'язана з пiдтримкою контролю типiв. Сімейства функцій printf/scanf не передбачають ніякого контролю типів даних. Компілятор не попереджає про невідповідність типів значень та відповідних специфікацій. Механiзм потокiв у С++ , який грунтується на перегрузцi функцій та операпорiв, забезпечує для кожного типу даних свою перегружену функцiю.
2) розширюванiсть. Завдяки полiморфiзму однi i тi ж самi оператори можуть взаємодiяти з потоками рiзних типiв. Легко можна написати і перевантаженi функцiї , які будуть працювати з специфічними типами даних користувача.
3) простота та послiдовнiсть. Використання перевантажених функцiй дає можливiсть використовувати однотипний iнтерфейс вводу/виводу.
Розглянемо, як можна використовувати класи вводу-виводу.