- Любовные романы
- Фантастика и фэнтези
- Ироническое фэнтези
- Научная Фантастика
- Фэнтези
- Ужасы и Мистика
- Боевая фантастика
- Альтернативная история
- Космическая фантастика
- Попаданцы
- Юмористическая фантастика
- Героическая фантастика
- Детективная фантастика
- Социально-психологическая
- Боевое фэнтези
- Русское фэнтези
- Киберпанк
- Романтическая фантастика
- Городская фантастика
- Технофэнтези
- Мистика
- Разная фантастика
- Иностранное фэнтези
- Историческое фэнтези
- LitRPG
- Эпическая фантастика
- Зарубежная фантастика
- Городское фентези
- Космоопера
- Разное фэнтези
- Книги магов
- Любовное фэнтези
- Постапокалипсис
- Бизнес
- Историческая фантастика
- Социально-философская фантастика
- Сказочная фантастика
- Стимпанк
- Романтическое фэнтези
- Ироническая фантастика
- Детективы и Триллеры
- Проза
- Феерия
- Новелла
- Русская классическая проза
- Современная проза
- Повести
- Контркультура
- Русская современная проза
- Историческая проза
- Проза
- Классическая проза
- Советская классическая проза
- О войне
- Зарубежная современная проза
- Рассказы
- Зарубежная классика
- Очерки
- Антисоветская литература
- Магический реализм
- Разное
- Сентиментальная проза
- Афоризмы
- Эссе
- Эпистолярная проза
- Семейный роман/Семейная сага
- Поэзия, Драматургия
- Приключения
- Детская литература
- Загадки
- Книга-игра
- Детская проза
- Детские приключения
- Сказка
- Прочая детская литература
- Детская фантастика
- Детские стихи
- Детская образовательная литература
- Детские остросюжетные
- Учебная литература
- Зарубежные детские книги
- Детский фольклор
- Буквари
- Книги для подростков
- Школьные учебники
- Внеклассное чтение
- Книги для дошкольников
- Детская познавательная и развивающая литература
- Детские детективы
- Домоводство, Дом и семья
- Юмор
- Документальные книги
- Бизнес
- Тайм-менеджмент
- Кадровый менеджмент
- Экономика
- Менеджмент и кадры
- Управление, подбор персонала
- О бизнесе популярно
- Интернет-бизнес
- Личные финансы
- Делопроизводство, офис
- Маркетинг, PR, реклама
- Поиск работы
- Бизнес
- Банковское дело
- Малый бизнес
- Ценные бумаги и инвестиции
- Краткое содержание
- Бухучет и аудит
- Ораторское искусство / риторика
- Корпоративная культура, бизнес
- Финансы
- Государственное и муниципальное управление
- Менеджмент
- Зарубежная деловая литература
- Продажи
- Переговоры
- Личная эффективность
- Торговля
- Научные и научно-популярные книги
- Биофизика
- География
- Экология
- Биохимия
- Рефераты
- Культурология
- Техническая литература
- История
- Психология
- Медицина
- Прочая научная литература
- Юриспруденция
- Биология
- Политика
- Литературоведение
- Религиоведение
- Научпоп
- Психология, личное
- Математика
- Психотерапия
- Социология
- Воспитание детей, педагогика
- Языкознание
- Беременность, ожидание детей
- Транспорт, военная техника
- Детская психология
- Науки: разное
- Педагогика
- Зарубежная психология
- Иностранные языки
- Филология
- Радиотехника
- Деловая литература
- Физика
- Альтернативная медицина
- Химия
- Государство и право
- Обществознание
- Образовательная литература
- Учебники
- Зоология
- Архитектура
- Науки о космосе
- Ботаника
- Астрология
- Ветеринария
- История Европы
- География
- Зарубежная публицистика
- О животных
- Шпаргалки
- Разная литература
- Боевые искусства
- Прочее
- Периодические издания
- Фанфик
- Военное
- Цитаты из афоризмов
- Гиды, путеводители
- Литература 19 века
- Зарубежная образовательная литература
- Военная история
- Кино
- Современная литература
- Военная техника, оружие
- Культура и искусство
- Музыка, музыканты
- Газеты и журналы
- Современная зарубежная литература
- Визуальные искусства
- Отраслевые издания
- Шахматы
- Недвижимость
- Великолепные истории
- Музыка, танцы
- Авто и ПДД
- Изобразительное искусство, фотография
- Истории из жизни
- Готические новеллы
- Начинающие авторы
- Спецслужбы
- Подростковая литература
- Зарубежная прикладная литература
- Религия и духовность
- Старинная литература
- Справочная литература
- Компьютеры и Интернет
- Блог
C++ - Страустрап Бьярн
Шрифт:
Интервал:
Закладка:
void manager::print() (* employee::print(); cout «„ „tуровень“ «« level «« «n“; // ... *)
Функция print_employee() теперь не нужна, поскольку ее место заняли функции члены print(), и теперь со списком слжащих можно работать так:
void f(employee* ll) (* for (; ll; ll=ll-»next) ll-»print(); *)
Каждый служащий будет печататься в соответствии с его типом. Например:
main() (* employee e; e.name = «Дж.Браун»; e.department = 1234; e.next = 0; manager m; m.name = «Дж.Смит»; e.department = 1234; m.level = 2; m.next = amp;e; f( amp;m); *)
выдаст
Дж.Смит 1234 уровень 2 Дж.Браун 1234
Заметьте, что это будет работать даже в том случае, если f() была написана и откомпилирована еще до того, как проиводный класс manager был задуман! Очевидно, при реализации этого в каждом объекте класса employee сохраняется некоторая информация о типе. Занимаемого для этого пространства (в ткущей реализации) как раз хватает для хранения указателя. Это пространство занимается только в объектах классов с виртуалными функциями, а не во всех объектах классов и даже не во
всех объектах производных классов. Вы платите эту пошлину только за те классы, для которых описали виртуальные функции.
Вызов функции с помощью операции разрешения области вдимости ::, как это делается в manager::print(), гарантирует, что механизм виртуальных функций применяться не будет. Иначе manager::print() подвергалось бы бесконечной рекурсии. Примнение уточненного имени имеет еще один эффект, который может оказаться полезным: если описанная как virtual функция описна еще и как inline (в чем ничего необычного нет), то там, где в вызове применяется ::, может применяться inline-подстновка. Это дает программисту эффективный способ справляться с теми важными специальными случаями, когда одна виртуальная функция вызывает другую для того же объекта. Поскольку тип объекта был определен при вызове первой виртуальной функции, обычно его не надо снова динамически определять другом вызове для того же объекта.
7.3 Альтернативные интерфейсы
После того, как описаны средства языка, которые относяся к производным классам, обсуждение снова может вернуться к стоящим задачам. В классах, которые описываются в этом раздле, основополагающая идея состоит в том, что они однажды нписаны, а потом их используют программисты, которые не могут изменить их определение. Физически классы состоят из одного или более заголовочных файлов, определяющих интерфейс, и оного или более файлов, определяющих реализацию. Заголовочные файлы будут помещены куда-то туда, откуда пользователь может взять их копии с помощью директивы #include. Файлы, определющие реализацию, обычно компилируют и помещают в библиотеку.
7.3.1 Интерфейс
Рассмотрим такое написание класса 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'ов, а видно только в закрытой части и определниях функций.
7.3.2 Реализация
Реализующие slist функции в основном просты. Единственая настоящая сложность – что делать в случае ошибки, если, например, пользователь попытается get() что-нибудь из пустого списка. Мы обсудим это в #7.3.4. Здесь приводятся определения членов 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 (его описание можно найти в #7.3.4). Этот указатель на имя функции используется точно так же, как если бы он был именем функции. Это является краткой формой более явной записи вызова:
(*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 как друга, поэтому мы можем описать подходящий итератор. Вот один, написанный в духе #6.8:
class slist_iterator (* slink* ce; slist* cs; public: slist_iterator(slist amp; s) (* cs = amp;s; ce = cs-»last; *)
ent operator()() (* // для индикации конца итерации возвращает 0 // для всех типов не идеален, хорош для указателей ent ret = ce ? (ce=ce-»next)-»e : 0; if (ce == cs-»last) ce= 0; return ret; *) *);
7.3.3 Как этим пользоваться
Фактически класс slist в написанном виде бесполезен. В конечном счете, зачем можно использовать список указателей void*? Штука в том, чтобы вывести класс из slist и получить список тех объектов, которые представляют интерес в конкреной программе. Представим компилятор языка вроде С++. В нем широко будут использоваться списки имен; имя name – это нечто вроде
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*, которые используются в качестве фактических параметров (#2.3.4).
Списки имен можно использовать в классе, который предтавляет определение класса:
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( amp;friends,n)) error(«friend redeclared»); // friend переописан else if (find( amp;members,n)) error(«friend redeclared as member»); // friend переописан как member else friends.append(n); *) if (n-»is_operator()) operators.append(n); // ... *)