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.10 Новый метакласс — PointClass

6.10 Новый метакласс — PointClass

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

Object и Class являются основой нашей иерархии классов. Каждый класс является подклассом Object и наследует его методы, каждый метакласс является подклассом Class и взаимодействует с его конструктором. Any в разделе 6.4 показал, как может быть создан простой подкласс путём замены динамически скомпонованных методов своего суперкласса и, возможно, определением новых статически скомпонованных методов.

Теперь мы переходим к созданию классов с большей функциональностью. В качестве примера мы подключаем в нашу иерархию классов Point и Circle. Эти классы имеют новый динамически скомпонованный метод draw(); поэтому для размещения этой связи нам нужен новый метакласс. Вот файл интерфейса Point.h:

 

#include "Object.h"

 

extern const void * Point;      /* new(Point, x, y); */

 

void draw (const void * self);

void move (void * point, int dx, int dy);

 

extern const void * PointClass; /* добавление draw */

 

Подкласс всегда подключает суперкласс и определяет указатель на описание класса и на описание метакласса, если есть нечто новое. После того, как мы ввели в употребление метаклассы, можно наконец объявить селектор для динамически компонуемого метода, с которым он связан: в том же файле интерфейса, что и указатель метакласса.

Как и раньше, файл представления Point.r содержит структуру объекта struct Point со своими макросами доступа и селекторы суперкласса вместе со структурой для метакласса:

 

#include "Object.r"

 

struct Point { const struct Object _; /* Point : Object */

    int x, y;                         /* координаты */

};

 

#define x(p) (((const struct Point *)(p)) -> x)

#define y(p) (((const struct Point *)(p)) -> y)

 

void super_draw (const void * class, const void * self);

 

struct PointClass {

    const struct Class _;            /* PointClass : Class */

    void (* draw) (const void * self);

};

 

Файл реализации Point.c содержит move(), Point_draw(), draw() и super_draw(). Эти методы написаны как и прежде; в предыдущем разделе мы увидели технику для селектора суперкласса. Конструктор должен вызывать конструктор суперкласса:

 

static void * Point_ctor (void * _self, va_list * app) {

    struct Point * self = super_ctor(Point, _self, app);

 

    self -> x = va_arg(* app, int);

    self -> y = va_arg(* app, int);

    return self;

}

 

Одна из новых идей в этом файле это конструктор метакласса. Для выполнения наследования он вызывает конструктор суперкласса, а затем чтобы переписать новый динамически компонуемый метод draw() использует тот же цикл, что и Class_ctor():

 

static void * PointClass_ctor (void * _self, va_list * app) {

    struct PointClass * self

                    = super_ctor(PointClass, _self, app);

    typedef void (* voidf) ();

    voidf selector;

    va_list ap = * app;

 

    while ((selector = va_arg(ap, voidf))) {

        voidf method = va_arg(ap, voidf);

 

        if (selector == (voidf) draw)

            * (voidf *) & self -> draw = method;

    }

    return self;

}

 

Обратите внимание, что мы распределяем пары селектор / метод в списке аргументов с помощью конструктора суперкласса: ap предпринимает все, что Class_ctor() возвращает в * app, и после этого запускает цикл.

С помощью этого конструктора можно совместно динамически проинициализировать описания новых классов: PointClass создаётся с описанием Class, а затем Point создаётся с описанием класса PointClass:

 

void initPoint (void)

{

    if (! PointClass)

        PointClass = new(Class, "PointClass",

                Class, sizeof(struct PointClass),

                ctor, PointClass_ctor,

                0);

 

    if (! Point)

        Point = new(PointClass, "Point",

                Object, sizeof(struct Point),

                ctor, Point_ctor,

                draw, Point_draw,

                0);

}

 

Написание инициализации прямолинейно: мы указываем имена классов, отношения наследования и размер структур объектов, а затем добавляем пары селектор / метод для всех динамически компонуемых методов, определённых в файле. Ноль завершает каждый список аргументов.

В главе 9 мы будем выполнять эту инициализацию автоматически. На данный момент к интерфейсу в Point.h добавляется initPoint() и эта функция обязательно должна быть вызвана прежде чем мы сможем создавать точки или подклассы. Функция выполняет внутреннюю проверку, поэтому её можно вызывать многократно — она создаст лишь одно описание класса PointClass и Point.

До тех пор, пока initPoint() вызывается из main(), можно использовать тестовую программу points из раздела 4.1, и мы получаем тот же результат:

 

$ points p

"." at 1,2

"." at 11,22

 

Circle является подклассом Point, представленным в главе 4. Добавив его в иерархию классов, можно удалить уродливый код в конструкторе, показанный в разделе 4.7:

 

static void * Circle_ctor (void * _self, va_list * app) {

    struct Circle * self = super_ctor(Circle, _self, app);

 

    self -> rad = va_arg(* app, int);

    return self;

}

 

Конечно, надо добавить функцию инициализации initCircle(), которая будет вызываться из main() до того, как можно будет создавать окружности:

 

void initCircle (void)

{

    if (! Circle) {

        initPoint();

        Circle = new(PointClass, "Circle",

                Point, sizeof(struct Circle),

                ctor, Circle_ctor,

                draw, Circle_draw,

                0);

    }

}

 

Поскольку Circle зависит от Point, до инициализации Circle вызывается initPoint(). Все эти функции делают свою реальную работу только один раз и их можно вызывать в любом порядке до тех пор, как мы учитываем взаимозависимости внутри самой такой функции.

 

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