Вот пример того, как обобщенный (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() {}
};