Основы объектно-ориентированного проектирования

         

Эмуляция объектов


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

С внешней точки зрения "каждый объект имеет доступ к операциям, применимым к нему". Возможно, это немного наивно, но не является концептуально неверным. Язык С буквально поддерживает это понятие! Экземпляр "структуры" языка С (эквивалент записи в Pascal) может содержать среди своих полей указатели на функции.


Рис. 16.1.  Объект С со ссылками на функцию

Например, структурный тип REAL_STACK можно объявить так:

typedef struct { /* Экспортируемые компоненты */ void (*remove) (); void (*put) (); float (*item) (); BOOL (*empty) (); /* Закрытые компоненты (реализация) */ int count; float representation [MAXSIZE]; } REAL_STACK;

Фигурные скобки {...} ограничивают компоненты структуры; float задает вещественный тип; процедуры объявляются как функции с типом результата void; комментарии берутся в скобки /* и *?/. Важный символ *? служит для разыменования указателей. В практике программирования на С, чтобы все работало, принято добавлять достаточное количеств указателей, если это не помогает, то всегда можно попробовать добавить один или парочку символов &. Если и это не дает результата, всегда найдется кто-нибудь, кто сможет помочь.

В структурном типе REAL_STACK два последних компонента - переменная и массив, остальные - ссылки на функции. В данном тексте комментарии предупреждают об экспортируемых и закрытых компонентах эмулируемого класса, но на уровне языка клиентам доступно все.

Каждый экземпляр типа должен инициализироваться так, чтобы поля ссылок указывали на соответствующие функции. Например, если my_stack является переменной этого типа, а C_remove - функция, реализующая выталкивание из стека, то можно присвоить полю remove объекта my_stack ссылку на эту функцию таким образом:

my_stack.remove = C_remove

В эмулируемом классе remove не имеет необходимого для нее аргумента. Для доступа к соответствующему стеку следует объявить функцию C_remove так:

C_remove (s) REAL_STACK s; { ... Реализация операции remove ... }

Тогда клиент сможет применить remove к стеку my_stack:

my_stack.remove (my_stack)

В общем случае, подпрограмма rout, имеющая n аргументов в эмулируемом классе, порождает функцию C_rout с n+1 аргументами. Вызов ОО-подпрограммы:

x.rout (arg1, arg2, ..., argn)

эмулируется как:

x.C_rout (x, arg1, arg2, ..., argn)

Содержание раздела