Обратите внимание на то, что наш класс Widget
следит за “своим” компонентом библиотеки FLTK и классом Window
, с которыми он связан. Кроме того, отметьте, что для этого нам необходимы указатели, поскольку объект класса Widget
на протяжении времени своего существования может быть связан с разными объектами класса Window
. Ссылки или именованного объекта для этого недостаточно. (Объясните почему?)
Объект класса Widget
имеет местоположение (loc
), прямоугольную форму (width
и height
), а также сметку (label
. Интересно, что он также имеет функцию обратного вызова (do_it
), т.е. связывает образ объекта класса Widget
на экране с фрагментом своего кода. Смысл операций move()
, show()
, hide()
и attach()
должен быть очевидным.
Класс Widget
выглядит незаконченным. Он спроектирован как класс реализации, который пользователи не должны видеть слишком часто. Его стоит переделать. Мы подозреваем, что все эти открытые члены и “очевидные” операции содержат подводные камни.
Класс Widget
имеет виртуальную функцию и может быть использован как базовый класс, поэтому в нем предусмотрен виртуальный деструктор (см. раздел 17.5.2).
Д.3. Реализация класса Window
Когда следует использовать указатели, а когда ссылки? Мы обсудили этот общий вопрос в разделе 8.5.6. Здесь мы лишь отметим, что некоторые программисты любят указатели и что нам нужны указатели, когда мы хотим сослаться на разные объекты в разные моменты времени.
До сих пор мы скрывали главный класс в нашей графической библиотеке — класс Window
. Основная причина этого заключалась в том, что он использует указатели, а его реализация с помощью библиотеки FLTK опирается на использование свободной памяти. Вот как описан этот класса в заголовочном файле Window.h
.
class Window : public Fl_Window {
public:
// позволяет системе выбрать место в памяти:
Window(int w, int h, const string& title);
// верхний левый угол в точке xy:
Window(Point xy, int w, int h, const string& title);
virtual ~Window() { }
int x_max() const { return w; }
int y_max() const { return h; }
void resize(int ww, int hh) { w=ww, h=hh; size(ww,hh); }
void set_label(const string& s) { label(s.c_str()); }
void attach(Shape& s) { shapes.push_back(&s); }
void attach(Widget&);
void detach(Shape& s); // удаляет элемент w из фигур
void detach(Widget& w); // удаляет элемент w из окна
// (отключает обратные вызовы)
void put_on_top(Shape& p); // помещает объект p поверх
// всех других фигур
protected:
void draw();
private:
vector
int w,h; // размер окна
void init();
};
Итак, когда мы связываем фигуру с окном, используя функцию attach()
, мы храним указатель в объектах класса Shape
, поэтому объект класса Window
может рисовать соответствующую фигуру. Поскольку впоследствии мы можем отсоединить фигуру от окна с помощью функции detach()
, поэтому нам нужен указатель. По существу, присоединенная фигура принадлежит своему коду; мы просто передаем объекту класса Window
ссылку на нее. Функция Window::attach()
преобразовывает свой аргумент в указатель, чтобы его можно было сохранить. Как показано выше, функция attach()
является тривиальной; функция detach()
немного сложнее. Открыв файл Window.cpp
, мы видим следующее.
void Window::detach(Shape& s)
// определяет, что первой должна быть удалена
// последняя присоединенная фигура
{
for (unsigned int i = shapes.size(); 0
if (shapes[i–1]==&s) shapes.erase(&shapes[i–1]);
}
Функция-член erase()
удаляет (стирает) значение из вектора, уменьшая его размер на единицу (раздел 20.7.1). Класс Window
используется как базовый, поэтому он содержит виртуальный деструктор (раздел 17.5.2).
Д.4. Реализация класса Vector_ref
По существу, класс Vector_ref
имитирует вектор ссылок. Мы можем инициализировать его ссылками или указателями.