Поскольку вы уже прочитали главу 17, то вам должно быть очевидно, что аргумент Address
должен иметь тип void*
. И, разумеется, функция reference_to
должна каким-то образом создавать ссылку на объект класса Simple_window
из указателя addr
, имеющего тип void*
. Однако, если у вас нет опыта программирования, то ничто для вас не “очевидно” и не “разумеется”, пока вы не прочтете главу 17, поэтому рассмотрим и использование адресов подробнее.
Как описано в разделе A.17, язык C++ предлагает способ для указания имени типа. Рассмотрим пример.
typedef void* Address; // Address — это синоним типа void*
Это значит, что мы можем использовать имя Address
вместо void*
. В данном случае, используя имя Address
, мы хотим подчеркнуть, что передаем адрес, и скрыть тот факт, что void*
— это имя типа указателя на объект, тип которого неизвестен.
Итак, функция cb_next()
получает указатель типа void*
с именем addr
в качестве аргумента и — каким-то образом — немедленно преобразовывает его в ссылку Simple_window&
:
reference_to
Функция reference_to
является шаблонной (раздел A.13).
template
// интерпретирует адрес как ссылку на объект класса W
{
return *static_cast
}
Здесь мы использовали шаблонную функцию, для того чтобы самостоятельно написать операции, действующие как приведение типа void*
к типу Simple_window&
. Это приведение типа static_cast
описано в разделе 17.8.
Компилятор не имеет возможности проверить наши предположения о том, что аргумент addr
ссылается на объект класса Simple_window
, но правила языка требуют, чтобы компилятор в этом вопросе доверял программисту. К счастью, мы оказались правы. Об этом свидетельствует от факт, что система FLTK возвращает нам обратно указатель, который мы ей передавали. Поскольку, передавая указатель системе FLTK, мы знали его тип, можно использовать функцию reference_to
, чтобы “получить его обратно”. Все это немного запутанно, не проходит проверку и не больше характерно для низкоуровневого программирования.
Получив ссылку на объект класса Simple_window
, мы можем использовать ее для вызова функции-члена класса Simple_window
. Рассмотрим пример (раздел 16.3).
void Simple_window::cb_next(Address, Address pw)
// вызов функции Simple_window::next() для окна,
// расположенного по адресу pw
{
reference_to
}
Мы использовали довольно сложную функцию обратного вызова cb_next()
, просто чтобы согласовать типы, необходимые для вызова совершенно обычной функции-члена next()
.
Д.2. Реализация класса Widget
Наш интерфейсный класс Widget
выглядит следующим образом.
class Widget {
// Класс Widget — это дескриптор класса Fl_widget,
// а не сам класс Fl_widget;
// мы пытаемся не смешивать наши интерфейсные классы с FLTK
public:
Widget(Point xy, int w, int h, const string& s, Callback cb)
:loc(xy), width(w), height(h), label(s), do_it(cb)
{ }
virtual ~Widget() { } // деструктор
virtual void move(int dx,int dy)
{ hide(); pw–>position(loc.x+=dx, loc.y+=dy); show(); }
virtual void hide() { pw–>hide(); }
virtual void show() { pw–>show(); }
virtual void attach(Window&) = 0; // каждый объект класса
// Widget определяет хотя бы
// одно действие над окном
Point loc;
int width;
int height;
string label;
Callback do_it;
protected:
Window* own; // каждый объект класса Widget
// принадлежит объекту классу Window
Fl_Widget* pw; // каждый объект класса Widget о "своем"
// классе Fl_Widget
};