Допомога у написанні освітніх робіт...
Допоможемо швидко та з гарантією якості!

Альтернативные Інтерфейси

РефератДопомога в написанніДізнатися вартістьмоєї роботи

Обратите увагу, як set_slist_hanlder () повертає попередній slist_hanlder (). Це робить зручним встановлення та перевстановлення оброблювачів помилок подібно до стека. Це можливо, у основному корисним у великих програмах, у яких slist може використовуватися у різних ситуаціях, у кожному у тому числі можуть, таким чином, задаватися свої власні підпрограми обробки помилок. Реализующие slist функції… Читати ще >

Альтернативные Інтерфейси (реферат, курсова, диплом, контрольна)

Альтернативные Интерфейсы

После того, як описані кошти мови, які стосуються до похідним класам, обговорення знову може повернутися до поставленим завданням. У класах, які описуються у цьому підрозділі, основна ідея полягає у тому, що вони свого часу написані, і потім їх використовують програмісти, які можуть невпізнанно змінити їх визначення. Фізично класи складаються вже з чи більше заголовочных файлів, визначальних інтерфейс, і самого чи більше файлів, визначальних реалізацію. Заголовкові файли будуть можна побачити кудись туди, звідки користувач може їх копії з допомогою директиви #include. Файли, що визначають реалізацію, зазвичай компілюють і вміщують у бібліотеку.

Интерфейс

Рассмотрим таке написання класу slist для одноразово пов’язаного списку, з допомогою якого створювати як однорідні, і неоднорідні списки об'єктів тих типів, що ще слід визначити. Спочатку ми визначимо тип ent:

typedef void* ent;

Точная сутність типу ent незначна, але потрібно, щоб у ньому міг зберігатися покажчик. Тоді ми визначимо тип slink:

class slink {.

friend class slist;

friend class slist_iterator;

slink* next;

ent e;

slink (ent a, slink* p) { e=a; next=p;}.

};

В одній ланці може зберігатися один ent, і з допомогою нього реалізується клас slist:

class slist {.

friend class slist_iterator;

slink* last; // last->next — голова списка.

public:

int insert (ent a); // додати на думку списка.

int append (ent a); // додати в хвіст списка.

ent get (); // повернутися, і прибрати голову списка.

void clear (); // прибрати все звенья.

slist () { last=0; }.

slist (ent a) { last=new slink (a, 0); last->next=last; }.

~slist () { clear (); }.

};

Хотя список вочевидь реалізується як пов’язаний список, реалізацію можна змінити те щоб використовувався вектор з ent «вв, не вплинувши у своїй на користувачів. Тобто, застосування slink «вв неможливо видно в описах відкритих функцій slist «вв, а, а є лише у зачиненій частини й визначеннях функцій.

Реализация

Реализующие slist функції переважно прості. Єдина справжня складність — що робити у разі помилки, якщо, наприклад, користувач спробує get () щось із порожнього списку. Тут наводяться визначення членів slist. Зверніть увагу, як зберігання покажчика на останній елемент кругового списку дає можливість просто реалізувати протягом обох дій append () і insert ():

int slist: insert (ent a).

{.

if (last).

last->next = new slink (a, last->next);

else {.

last = new slink (a, 0);

last->next = last;

}.

return 0;

}.

int slist: append (ent a).

{.

if (last).

last = last->next = new slink (a, last->next);

else {.

last = new slink (a, 0);

last->next = last;

}.

return 0;

}.

ent slist: get ().

{.

if (last == 0) slist_handler («get fromempty list »);

// узяти з порожнього списка.

slink* f = last->next;

ent r f->e;

if (f == last).

last = 0;

else.

last->next = f->next;

delete f;

return f;

}.

Обратите увагу, як викликається slist_handler. Цей покажчик з ім'ям функції використовується точно як і, коли б він був ім'ям функції. Це є короткої формою очевиднішою записи виклику:

(*slist_handler)(«get fromempty list »);

И slist: clear (), нарешті, видаляє зі списку все элементы:

void slist: clear ().

{.

slink* l = last;

if (l == 0) return;

do {.

slink* ll = l;

l = l->next;

delete ll;

} while (l≠last);

}.

Класс slist не забезпечує способу зазирнути у список, але кошти на вставления і видалення елементів. Проте обидва класу, і slist, і slink, описують клас slist_iterator як друга, тому ми можемо описати підходящий итератор. Ось одне, написаний дусі цього пункту:

class slist_iterator {.

slink* ce;

slist* cs;

public:

slist_iterator (slist& p. s) { cs = &p.s; ce = cs->last; }.

ent operator ()() {.

// для індикації кінця ітерації повертає 0.

// всім типів ідеальний, хороший для указателей.

ent ret = ce? (ce=ce->next)->e: 0;

if (ce == cs->last) ce= 0;

return ret;

}.

};

Как Цим Користуватися

Фактически клас slist в написаному вигляді непотрібний. У кінцевому підсумку, навіщо можна використовувати список покажчиків void*? Штука у цьому, щоб вивести клас з slist й одержати список тих об'єктів, які цікаві у певній програмі. Уявімо компілятор мови на кшталт З++. У ньому широко використовуватимуться списки імен; ім'я — це щось на кшталт.

struct name {.

char* string;

// …

};

В список будуть поміщатися покажчики на імена, а чи не самі об'єкти імена. Це дозволяє вживати невеличке інформаційне полі e slist «чи дає можливість імені перебувати одночасно більш ніж одному списку. Ось визначення класу nlist, дуже просто виводиться з класу slist:

#include «slist.h «.

#include «name.h «.

struct nlist: slist {.

void insert (name* a) { slist: insert (a); }.

void append (name* a) { slist: append (a); }.

name* get () {}.

nlist (name* a): (a) {}.

};

Функции нового класу, або успадковуються від slist безпосередньо, або нічого роблять крім перетворення типу. Клас nlist — це ніщо інше, як альтернативний інтерфейс класу slist. Оскільки самому справі тип ent є void*, не потрібно явно перетворювати покажчики name*, які у ролі фактичних параметрів .

Списки імен можна залучити до класі, який представляє визначення класу:

struct classdef {.

nlist friends;

nlist constructors;

nlist destructors;

nlist members;

nlist operators;

nlist virtuals;

// …

void add_name (name*);

classdef ();

~classdef ();

};

и імена можуть додаватися до цих списками приблизно так:

void classdef: add_name (name* n).

{.

if (n->is_friend ()) {.

if (find (&friends, n)).

error («friend redeclared »);

else if (find (&members, n)).

error («friend redeclared as member »);

else.

friends.append (n);

}.

if (n->is_operator ()) operators. append (n);

// …

}.

где is_iterator () і is_friend () є функціями членами класу name. Фукнцию find () написати так:

int find (nlist* ll, name* n).

{.

slist_iterator ff (*(slist*)ll);

ent p;

while (p=ff ()) if (p==n) return 1;

return 0;

}.

Здесь застосовується явне перетворення типу, щоб застосувати slist_iterator до nlist. Більше хороше рішення, — зробити итератор для nlist «вв. Друкувати nlist може, наприклад, така функція:

void print_list (nlist* ll, char* list_name).

{.

slist_iterator count (*(slist*)ll);

name* p;

int n = 0;

while (count ()) n++;

cout << list_name << «n «<< n << «membersn » ;

slist_iterator print (*(slist*)ll);

while (p=(name*)print ()) cout << p->string << «n » ;

}.

Обработка Помилок

Есть чотири підходи до проблеми, що робити, коли у час виконання общецелевое засіб на кшталт slist стикається з помилкою (в З++ немає жодних спеціальних коштів мови для обробці помилок):

Возвращать неприпустиме значення й дозволити користувачеві його перевіряти;

Возвращать додаткового значення гніву й дозволити користувачеві перевіряти його;

Вызывать функцію помилок, задану як частину класу slist; чи.

Вызывать функцію помилок, яку може бути надає користувач.

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

Первый підхід, повертати неприпустиме значення, нездійсненний. Ні цілком жодного способу дізнатися, що якийсь конкретне значення буде неприпустимим переважають у всіх цілях slist.

Второй підхід, повертати значення стану, можна залучити до деяких класах (одне із варіантів цього плану застосовується у стандартних потоках ввода/вывода istream і ostream). Тут, проте, є серйозні проблеми, раптом користувач не подбає перевірити значення стану, якщо засіб не часто підводить. З іншого боку, засіб може використовуватися в сотнях і навіть тисячах місць програми. Перевірка значення кожному місці вельми утруднить читання програми.

Третьему підходу, надавати функцію помилок, бракує гнучкості. Той, хто реалізує общецелевое засіб, неспроможна дізнатися, як користувачі захочуть, щоб оброблялися помилки. Наприклад, користувач може віддавати перевагу повідомлення датською чи угорському.

Четвертый підхід, дозволити користувачеві ставити функцію помилок, має деяку привабливість за умови, що розробник надає клас у вигляді бібліотеки (#4.5), де є стандартні функції обробки помилок. Рішення 3 і 4 можна зробити гнучкішими (й еквівалентними), поставивши покажчик на функцію, а чи не саму функцію. Це дозволить розробникові такого кошти, як slist, надати функцію помилок, діючу за умовчанням, і навіть програмістам, що використовуватимуть списки, буде легко поставити свої власні функції помилок, коли потрібно, де він, де потрібна.

Например:

typedef void (*PFC)(char*); // покажчик на той тип функция.

extern PFC slist_handler;

extern PFC set_slist_handler (PFC);

Функция set_slist_hanlder () дозволяє користувачеві замінити стандартну функцію. Загальноприйнята реалізація надає діючу за умовчанням функцію обробки помилок, що спочатку пише повідомлення про помилці в cerr, після чого завершує програму з допомогою exit ():

#include «slist.h «.

#include.

void default_error (char* s).

{.

cerr << p. s << «n » ;

exit (1);

}.

Она описує також покажчик на функцію помилок, і, для зручності записи, функцію на її установки:

PFC slist_handler = default_error;

PFC set_slist_handler (PFC handler);

{.

PFC rr = slist_handler;

slist_handler = handler;

return rr;

}.

Обратите увагу, як set_slist_hanlder () повертає попередній slist_hanlder (). Це робить зручним встановлення та перевстановлення оброблювачів помилок подібно до стека. Це можливо, у основному корисним у великих програмах, у яких slist може використовуватися у різних ситуаціях, у кожному у тому числі можуть, таким чином, задаватися свої власні підпрограми обробки помилок.

Например:

{.

PFC old = set_slist_handler (my_handler);

// код, у якому разі помилок в slist.

// використовуватиметься мій оброблювач my_handler.

set_slist_handler (old); // восстановление.

}.

Чтобы зробити управління більш витонченим, slist_hanlder міг бути зроблено членом класу slist, що дозволило б різним списками мати одночасно різні оброблювачі.

Обобщенные Классы

Очевидно, можна було визначити інших типів (classdef*, int, char* тощо.) точно як і, як було визначено клас nlist: простим висновком з класу slist. Процес визначення таких нових типів стомлює (і тому загрожує помилками), але з допомогою макросів може бути «механізувати ». На жаль, користуючись стандартним З препроцесором, це також має здатність виявитися прикрим. Проте одержаними у результаті макросами користуватися досить просто.

Вот приклад того, як узагальнений (generic) клас slist, під назвою gslist, може бути поставлене як макрос. Спочатку для написання такого роду макросів включаються деякі інструменти з :

.html#include «slist.h «.

#ifndef GENERICH.

#include.

#endif.

Обратите увагу до використання #ifndef у тому, щоб гарантувати, що у однієї компіляції нічого очікувати включений двічі. GENERICH визначено у .

После цього з допомогою name2(), макросу з для конкатенації імен, визначаються імена нових узагальнених класів:

#define gslist (type) name2(type, gslist).

#define gslist_iterator (type) name2(type, gslist_iterator).

И, нарешті, написати класи gslist (тип) і gslist_iterator (тип):

#define gslistdeclare (type).

struct gslist (type): slist {.

int insert (type a).

{ return slist: insert (ent (a)); }.

int append (type a).

{ return slist: append (ent (a)); }.

type get () { return type (slist:get ()); }.

gslist (type)() { }.

gslist (type)(type a): (ent (a)) { }.

~gslist (type)() { clear (); }.

};

struct gslist_iterator (type): slist_iterator {.

gslist_iterator (type)(gslist (type)& a).

: ((slist&)s) {}.

type operator ()().

{ return type (slist_iterator:operator ()()); }.

}.

на кінці рядків вказує, що така рядок є частиною що визначається макросу.

С допомогою цього макросу список покажчиків з ім'ям, аналогічний застосованому раніше класу nlist, можна визначити так:

#include «name.h «.

typedef name* Pname;

declare (gslist, Pname); // описати клас gslist (Pname).

gslist (Pname) nl; // описати один gslist (Pname).

Макрос declare (описати) визначено у. Він конкатенирует свої параметри і макрос з цим ім'ям, у разі gslistdeclare, описане вище. Параметр ім'я типу для declare має бути простим ім'ям. Використовуваний метод макровизначення неспроможна обробляти імена типів на кшталт name*, тому застосовується typedef.

Использования виведення класу гарантує, що це приватні випадки узагальненого класу поділяють код. Цей метод можна застосовувати лише для створення класів об'єктів тієї самої розміру менше, ніж базовий клас, що використовується в макросе.

Ограниченные Інтерфейси

Класс slist — досить загального характеру. Іноді така спільність непотрібен і навіть небажана. Обмежені види списків, такі як стеки і першої черги, навіть більше звичні, ніж сам узагальнений список. Такі структури даних можна поставити, не описавши базовий клас як відкритий. Наприклад, чергу цілих можна визначити так:

#include «slist.h «.

class iqueue: slist {.

//предполагается sizeof (int)<=sizeof (void*).

public:

void put (int a) { slist: append ((void*)a); }.

int det () { return int (slist:get ()); }.

iqueue () {}.

};

При такому виведення здійснюються два логічно розділених дії: поняття списку обмежується поняттям черги (зводиться щодо нього), і задається тип int, аби поняття черги до типу даних чергу цілих, iqueue. Ці дві дії можна виконувати і роздільно. Тут перша частина — це список, обмежений тож він може використовуватися лише як стік:

#include «slist.h «.

class stack: slist {.

public:

slist:insert;

slist:get;

stack () {}.

stack (ent a): (a) {}.

};

который потім використовується до створення типу «стік покажчиків на символи » :

#include «stack.h «.

class cp: stack {.

public:

void push (char* a) { slist: insert (a); }.

char* pop () { return (char*)slist:get (); }.

nlist () {}.

};

Список литературы

Для підготовки даної праці були використані матеріали із сайту internet.

Показати весь текст
Заповнити форму поточною роботою