6.6 Реализация — Class |
Предыдущая Содержание Следующая |
Class является подклассом Object, поэтому мы можем просто унаследовать методы для сравнения и отображения. Деструктор возвращает пустой указатель, чтобы не дать delete() освободить память, занимаемую описанием класса:
static void * Class_dtor (void * _self) { struct Class * self = _self;
fprintf(stderr, "%s: cannot destroy class\n", self—>name); return 0; }
Вот функция доступа для получения суперкласса из описания класса:
const void * super (const void * _self) { const struct Class * self = _self;
assert(self && self -> super); return self -> super; }
Единственной трудной частью является реализация конструктора Class, потому что это то месте, где инициализируется новое описание класса, где происходит наследование, и где наши четыре основные методы могут быть перезаписаны. Напомним из раздела 6.4, как создается описание нового класса:
const void * Any = new(Class, "Any", Object, sizeOf(o), differ, Any_differ, 0);
Это означает, что наш конструктор Class получает имя, суперкласс и размер объекта для описания нового класса. Начинаем перенося их из списка аргументов:
static void * Class_ctor (void * _self, va_list * app) { struct Class * self = _self;
self -> name = va_arg(* app, char *); self -> super = va_arg(* app, struct Class *); self -> size = va_arg(* app, size_t);
assert(self -> super);
self не может быть нулевым указателем, потому что мы бы иначе не нашли этот метод. super, однако, может оказаться нулём и это было бы очень плохо. Следующим шагом является наследование. Надо скопировать конструктор и все другие методы из описания суперкласса в super в наше описание нового класса в self:
const size_t offset = offsetof(struct Class, ctor); ... memcpy((char *) self + offset, (char *) self -> super + offset, sizeOf(self -> super) - offset);
Предполагая, что конструктор является первым методом в struct Class, мы используем макрос ANSI-C offsetof(), чтобы определить начало нашего копирования. К счастью, описание класса в super является подклассом Object и наследует sizeOf(), поэтому можно вычислить, как много байт копировать. Хотя это решение не вполне безопасно, оно выглядит лучшим компромиссом. Конечно, можно было бы скопировать всю область в super и сохранить после этого новое имя и так далее; тем не менее, всё равно пришлось бы спасать struct Object в начале описания нового класса, так как new() уже сохранила там указатель описания указателя описания класса. Последняя часть конструктора Class ответственна за перезапись методов, указанных в списке аргументов, переданного в new(). ANSI-C не позволяет присваивать указатели на функции для и из void *, так что необходимо определённое количество приведений :
{ typedef void (* voidf) (); /* указатель родовой функции */ voidf selector; va_list ap = * app;
while ((selector = va_arg(ap, voidf))) { voidf method = va_arg(ap, voidf);
if (selector == (voidf) ctor) * (voidf *) & self -> ctor = method; else if (selector == (voidf) dtor) * (voidf *) & self -> dtor = method; else if (selector == (voidf) differ) * (voidf *) & self -> differ = method; else if (selector == (voidf) puto) * (voidf *) & self -> puto = method; } return self; }}
Как мы увидим в разделе 6.10, эту часть списка аргументов лучше распределить среди всех конструкторов класса, чтобы пары селектор / метод могли быть указаны в любом порядке. Мы достигаем этого, больше не увеличивая * app; вместо в va_arg() передаётся копия этого значения ap. Сохранение методов таким способом имеет несколько последствий: Если в селекторе ни один конструктор класса не подходит, пара селектор / метод молча игнорируется, но, по крайней мере, он не добавляется к описанию класса, которому не принадлежит. Если метод не имеет надлежащего типа, компилятор ANSI-C не обнаружит ошибку, поскольку список аргументов переменной длины и наше приведение предотвращают проверки типов. Здесь мы надеемся на программиста, что селектор соответствует методу, поставляемому вместе с ним, но они должны быть определены как пара и это должно привести к определённой доле доверия.
|
Предыдущая Содержание Следующая |