The website "dmilvdv.narod.ru." is not registered with uCoz.
If you are absolutely sure your website must be here,
please contact our Support Team.
If you were searching for something on the Internet and ended up here, try again:

About uCoz web-service

Community

Legal information

6.6 Реализация — Class

6.6 Реализация — Class

Предыдущая  Содержание  Следующая V*D*V

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 не обнаружит ошибку, поскольку список аргументов переменной длины и наше приведение предотвращают проверки типов. Здесь мы надеемся на программиста, что селектор соответствует методу, поставляемому вместе с ним, но они должны быть определены как пара и это должно привести к определённой доле доверия.

 

Предыдущая  Содержание  Следующая