9.3 Функции для объектов

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

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

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

 

#define Point   (Point ? Point : (Point = initPoint()))

 

Макрос Point проверяет, проинициализировано ли уже описание класса Point. Если нет, для генерации описания класса вызывается initPoint(). К сожалению, если мы определяем Point как макрос без параметров, то больше не сможем использовать то же самое имя как признак структуры для объектов и для соответствующего описания класса. Следующий макрос лучше:

 

#define Class(x)    (x ? x : (x = init ## x ()))

 

Теперь мы определяем Class(Point), чтобы сослаться на описание класса. initPoint() всё ещё вызывает new() как и прежде, но теперь надо возвратить сгенерированное описание класса, то есть для каждого описания класса необходима его собственная функция инициализации:

 

const void * Point;

 

const void * initPoint (void) {

    return new(Class(PointClass),

                "Point", Class(Object), sizeof(struct Point),

                ctor, Point_ctor,

                draw, Point_draw,

                (void *) 0);

}

 

Эта конструкция всё ещё соблюдает порядок, навязанный иерархией классов: сначала описание класса PointClass передано в new(), макрорасширение Class(Point) удостоверяется, что это описание существует. Пример показывает, что для однородности мы должны будем написать пустые функции initObject() и initClass().

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

 

static const void * _Point;

 

const void * const Point (void) {

    return _Point ? _Point :

            (_Point = new(PointClass(),

                "Point", Object(), sizeof(struct Point),

                ctor, Point_ctor,

                draw, Point_draw,

                (void *) 0));

}

 

Мы могли бы переместить определение фактического указателя _Point в функцию; однако, глобальное определение необходимо, если мы всё ещё хотим реализовать munch для System V.

Замена статических объектов функциями не должна быть менее эффективной, чем использование макроса. ANSI-C не разрешает декларацию результата для функции как const или volatile, то есть выделенный жирным шрифтом определитель const в данном примере. (* Первый const указывает, что результат функции указывает на постоянную величину. Только второй const указывает, что константой является значение указателя самого по себе.) GNU-C позволяет такую декларацию и использует её во время оптимизации. Если результат функции const, его значение должно зависеть только от её аргументов, и вызов не должен давать побочных эффектов. Компилятор пытается минимизировать количество вызовов такой функции и использует результаты повторно.

 

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