Главная » C++, C++ Builder » Исправляем родительское окно STL

0

Оказывается, основные исправления, относящиеся к родительскому окну, приходятся на заголовочный файл (MainForm.h). Сначала давайте  добавим  код,  описывающий  класс  для хранения точек. Просмотрите код, а затем мы обсудим, что в нем происходит:

const int MoveMode = 1;

const int DrawMode = 2;

class TScribblePoint

{

int FnX; int FnY;

int FnMode;

public: TScribblePoint(void)

{

FnX = 0;

FnY = 0;

FnMode = MoveMode;

}

TScribblePoint (int nMode, int nX, int nY )

{

FnX = nX; FnY = nY;

FnMode = nMode;

}

int GetMode(void)

{

return FnMode;

}

int GetX(void)

{

return FnX;

}

int GetY(void)

{

return FnY;

}

};

Как видите, класс TScribblePoint довольно прост. В нем содержатся  три  переменных —  члена класса для координат X и Y точки и режима, в котором происходит событие, связанное с этой точкой. Кроме класса, мы описали две константы, обозначающие два типа режима.

Кроме переменных, класс TScribblePoint содержит функции, дающие доступ к данным, что является хорошим тоном при программировании на C++. Вместо того, чтобы предоставлять конечному пользователю доступ к внутренней структуре класса, вы контролируете этот доступ и решаете, можно ли записывать или считывать данные.  Этот момент является  очень важным и будет рассмотрен ниже, во время дискуссии об определении свойств компонентов. Если вы уже привыкли работать с внутренними данными как с частной собственностью и предоставлять пользователю доступ только к необходимым данным, то вам будет гораздо легче научиться писать компоненты и их свойства.

Итак, небольшая лекция закончена, пора переходить ко второму шагу: модифицировать класс главной формы с использованием нашего нового класса TScribblePoint. Вот изменения, которые вам необходимо внести в класс, чтобы он поддерживал наш новый объект:

class TForm2 : public TForm

{

__published: // IDE-managed TMenuItem *File1; TMenuItem *New1; TMenuItem *Exit1; TMenuItem *Update1; TMenuItem *AllWindows1;

void __fastcall New1Click(TObject *Sender);

void __fastcall AllWindows1Click(TObject *Sender); void __fastcall Exit1Click(TObject *sender);

private:  // User declarations std::vector<TScribblePoint>  FvPoints;

public: // User declarations

    fastcall TForm2(TComponent *Owner); void ClearPoints(void)

{

FvPoints.erase (FvPoints.begin(), FvPoints.end() );

}

void AddPoint(int nMode, int X, int Y)

{

TScribblePoint point(nMode, X, Y); FvPoint.insert (FvPoints.end(), point );

}

int NumberOfPoints(void)

{

return FvPoints.size();

}

void GetPoint (int Index, int& X, int& Y, int& Mode )

{

X = FvPoints[Index].GetX(); Y = FvPoints[Index].GetY();

Mode = FvPoints[Index].GetMode();

}

};

Эти изменения позволят главной форме работать с вектором (массивом) точек. Заметьте, что за исключением добавления параметра Mode в методе GetPoint (что нужно для реализации новых возможностей) никакие интерфейсы методов не изменились. Это еще один важный момент в объектно-ориентированном программировании. Если вы корректно напишите методы доступа ( accessor methods) к данным, то вы можете менять внутреннюю структуру данных, о чем клиентские программы, использующие ваш объект, даже не будут знать. Если вы прослушали несколько курсов по программированию, то вы вероятно постоянно слышали об этом, но ни разу не видели реальной ситуации, где это имеет значение. Ну что ж, теперь вы знаете, где и зачем это применяется.

В данном случае проявляется преимущество использования языка C++, а не C или Visual Basic. Если бы, например, вы использовали Visual Basic, и использовали переменны для хранения данных, как мы храним точки, то вам пришлось бы найти все места, где используются эти переменные, и поменять их. На самом деле, конечно, такой проблемы у вас бы не появилось, если бы вы действительно программировали на Visual Basic, потому что STL поддерживается только в языке C++. В данном конкретном случае это также является довольно большим преимуществом

CBuilder перед Delphi. Система Delphi, написанная на Object Pascal, имеет великое множество реализаций векторов (массивов), множеств и т. д. от третьих лиц, но она не поддерживает и, вероятно, никогда не будет поддерживать стандарт STL. Занавес опускается.

Еще одно изменение, которое нам нужно сделать, касается дочерних окон MDI. Во-первых, нам нужно добавить параметр Mode в вызов AddPoint в дочерних окнах. Измените следующие методы в исходном файле Unit1.cpp в проекте:

void      fastcall TForm1::OnMouseDown(TObject *Sender, TMouseButton Button,

TShiftState Shift, int X, int Y)

{

FbMouseDown = TRUE;

// Переместиться изначально в эту точку

Canvas->MoveTo(X,Y);

// Послать новые данные в главную форму

Form2->AddPoint (MoveMode, X, Y );

}

//——————————————————-

void __fastcall TForm1::OnMouseMove(TObject *Sender, TShiftState Shift, int X, int Y)

{

if  (FbMouseDown )

{

Canvas->LineTo(X,Y);

// Посылаем новые данные в главную форму

Form2->AddPoint (DrawMode, X, Y );

}

}

Здесь на самом деле два изменения. Во-первых, мы добавили параметр Mode в вызове метода AddPoint главной формы. Во-вторых, мы убрали вызов метода ClearPoints, который находился в методе OnMouseDown. Теперь мы хотим, чтобы пользователь мог рисовать несколько линий (это основное изменение в возможностях самого приложения), так что нам больше не  требуется удалять все записанные точки, когда пользователь отпускает кнопку мыши и снова нажимает ее где-то еще. Чтобы отследить перемещения, собственно, и нужен параметр MoveMode.

И, наконец, последнее изменение для дочерних окон состоит в добавлении кода, рисующего запомненные каракули, при этом обрабатывая два случая — рисование и  простое перемещение курсора мыши, которые мы добавили в приложение. Измените метод OnPaint дочернего окна в файле Unit1.cpp:

void      fastcall TForm1::OnPaint(TObject *Sender)

{

if  (Form2->NumberOfPoints() > 0)

{

int X = 0, Y = 0, Mode = DrawMode;

// Теперь проходим по всем точкам, получаем

// их координаты из главной формы и рисуем их

for  (int i=0; i<Form2->NumberOfPoints(); ++i )

{

Form2->GetPoint (i, X, Y, Mode ); switch (Mode )

{

case DrawMode:

Canvas->LineTo (X, Y ); break;

case MoveMode:

Canvas->MoveTo (X, Y ); break;

}

}

}

}

Теперь функция будет проверять режим (рисование или перемещение) и корректно обрабатывать случаи, когда курсор мыши перемещается с нажатой кнопкой и с отпущенной кнопкой. Заметьте также, что нам больше не нужно отдельно обрабатывать первую точку. Теперь важен режим, в котором точка записана, а не порядок точек. Мы таким образом внесли в приложение  более высокий уровень абстракции.

Это все изменения, которые нужны программе Scribble для использования нового объекта, использования STL и поддержки неограниченного количества точек и линий. Со всем этим у нас получилось меньше кода, чем было раньше. Я надеюсь, вы видите, что STL можно эффективно использовать для написания качественных приложений с меньшим размером исходного кода.

Один небольшой приз для читателей, у  которых есть компакт-диск, прилагаемый к книге: на сопроводительном компакт-диске вы найдете программу, производящую  поиск  текста.  Она написана с использованием STL и поддерживает поиск по нескольких ключевым словам и сортировку по количеству совпадений. Эта программа с названием Search находится в каталоге Extras на компакт-диске. Для получения исполняемого файла программы просто откройте ее в CBuilder и скомпилируйте. Search — консольное приложение, так что вам нужно запускать его из командной строки MS-DOS в Windows 95 или NT.

Источник: Теллес М. – Borland C++ Builder. Библиотека программиста – 1998

По теме:

  • Комментарии