Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Курс лекцій.doc
Скачиваний:
15
Добавлен:
03.11.2018
Размер:
1.12 Mб
Скачать

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нтерфейс вводу/виводу.

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