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

4.7 Реализация подкласса — Circle

4.7 Реализация подкласса — Circle

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

Мы готовы написать полную реализацию окружностей, где можем выбрать любую технику из предыдущих разделов, которая больше всего нравится. Объектная ориентация предписывает, что нужен конструктор, возможно, деструктор, Circle_draw() и описание типа Circle, чтобы связать всё это вместе. Чтобы заняться нашими методами, подключаем Circle.h и добавляем следующие строки к переключателю в тестовой программе раздела 4.1:

 

case ’c’:

    p = new(Circle, 1, 2, 3);

    break;

 

Теперь наблюдаем такое поведение тестовой программы:

 

$ circles p c

"." at 1,2

"." at 11,22

circle at 1,2 rad 3

circle at 11,22 rad 3

 

Конструктор окружности принимает три аргумента: сначала координаты точки окружности, а затем радиус. Инициализация компонента "точка" является работой конструктора точки. Он использует часть списка аргументов new(). Конструктору окружности остаётся со списком оставшихся аргументов, с помощью которых он инициализирует радиус.

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

Для окружностей это означает, что надо вызвать Point_ctor(). Подобно всем динамически скомпонованным методам, эта функция объявлена static и таким образом скрыта внутри Point.c. Тем не менее, мы всё ещё можем добраться до этой функции с помощью дескриптора типа Point, который доступен в Circle.c:

 

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

    struct Circle * self =

        ((const struct Class *) Point) -> ctor(_self, app);

 

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

    return self;

}

 

Теперь должно быть понятно, почему мы передаём адрес указателя списка аргументов app каждому конструктору, а не само значение va_list: new() вызывает конструктор подкласса, который вызывает конструктор своего суперкласса, и так далее. Конструктор самого базового класса является первым, кто на самом деле что-то сделает, и он первым забирает из левой части списка аргументов, переданного в new(). Остальные аргументы доступны следующему подклассу и так далее до последнего, пока крайние справа аргументы не будут использованы последний подклассом, то есть конструктором непосредственно вызванным new().

Разрушение лучше всего расположить в точном обратном порядке: delete() вызывает деструктор подкласса. Он должен разрушить свои собственные ресурсы, а затем вызвать деструктор своего непосредственного суперкласса, который сможет уничтожить следующий набор ресурсов и так далее. Конструирование происходит в порядке от суперкласса до подкласса, разрушение происходит в обратном порядке, подкласс перед суперклассом, компонент "окружность" перед компонентом "точка". Здесь, однако, ничего делать не надо.

Перед этим мы работали над Circle_draw(). Мы используем видимые компоненты и код файла представления Point.r следующим образом:

 

struct Point {

    const void * class;

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

};

 

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

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

 

Теперь в Circle_draw() можно использовать макроопределения доступа:

 

static void Circle_draw (const void * _self) {

    const struct Circle * self = _self;

 

    printf("circle at %d,%d rad %d\n",

    x(self), y(self), self -> rad);

}

 

move() имеет статическую компоновку и наследуется от реализации точек. Мы решили реализовать окружности путём определения описания типа, который является единственной глобально видимой частью Circle.c:

 

static const struct Class _Circle = {

    sizeof(struct Circle), Circle_ctor, 0, Circle_draw

};

 

const void * Circle = & _Circle;

 

Хотя всё выглядит так, как будто мы имеем жизнеспособную стратегию распределения текста программы, реализуя класс через интерфейс, представление и файл реализации, пример точек и окружностей не выявил одну проблему: если динамически скомпонованный метод, такой как Point_draw(), не будет перезаписан в подклассе, дескриптор типа подкласса должен указывать на функцию, реализованную в суперклассе. Однако, имя функции определяется там как static, так что селектор не может быть обманут. Мы увидим чистое решение этой проблемы в главе 6. В качестве временной меры, мы бы избегали использования static в этом случае, декларировали заголовок функции только в файле реализации подкласса, а также использовали имя этой функции для инициализации описания типа для подкласса.

 

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