Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

3574

.pdf
Скачиваний:
2
Добавлен:
08.01.2021
Размер:
594.89 Кб
Скачать

«Test»). Откомпилируйте ваше приложение (команда Project | Make All Projects). Если все прошло без ошибок, выделите на форме ваш компонент и взгляните на его свойства в Инспекторе объектов. Вы найдете там добавленные вами свойства EnableNum и EnableLet. Оба свойства имеют по умолчанию заданные в конструкторе значения true. Попробуйте изменить оба значения на false. Это вам не удастся сделать, так как сработают написанные вами методы записи.

Свойство Modified в Инспекторе объектов вы не увидите, так как это свойство может использоваться только во время выполнения.

3.Задания к лабораторной работе.

1.Для определения иерархии классов связать отношением наследования классы, приведенные в приложении (для заданного варианта). Из перечисленных классов выбрать один, который будет стоять во главе иерархии. Это абстрактный класс.

2.Определить в классах все необходимые конструкторы и деструктор.

3.Компонентные данные класса специфицировать как protected.

4.Пример определения статических компонентов:

static person* begin; // указатель на начало списка static void print(void); // просмотр списка

5.Статическую компоненту-данное инициализировать вне определения класса, в глобальной области.

6.Для добавления объекта в список предусмотреть метод класса, т.е. объект сам добавляет себя в список. Например, a.Add() − объект a добавляет себя в список.

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

7.Список просматривать путем вызова виртуального метода Show каждого объекта.

8.Статический метод просмотра списка вызывать не через объект, а через класс.

9.Определение классов, их реализацию, демонстрационную программу поместить в отдельные файлы.

Варианты классов:

1) студент, преподаватель, персона, зав.кафедрой; 2) служащий, персона, рабочий, инженер; 3) рабочий, кадры, инженер, администрация; 4) деталь, механизм, изделие, узел;

5) организация, страховая компания, судостроительная компания, завод;

6) журнал, книга, печатное издание, учебник; 7) тест, экзамен, выпускной экзамен, испытание; 8) место, область, город, мегаполис; 9) игрушка, продукт, товар, молочный продукт;

10) квитанция, накладная, документ, чек; 11) автомобиль, поезд, транспортное средство, экспресс;

12)двигатель, двигатель внутреннего сгорания, дизель, турбореактивный двигатель;

13)республика, монархия, королевство, государство;

14)млекопитающие, парнокопытные, птицы, животное;

15)корабль, пароход, парусник, корвет.

4. Контрольные вопросы.

1.Что такое иерархия классов? Приведите пример иерархии. 2. Как создать класс?

3.Как создается экземпляр класса?

4.Что такое наследование?

5.Что такое полиморфизм?

Лабораторная работа №4. Создание методов и событий.

1.Цель лабораторной работы.

Целью лабораторной работы является получение практических навыков разработки объектно-ориентированной программы, управляемой событиями.

2.Теоретический материал для домашнего изучения.

Методы включаются в открытый раздел класса — раздел public,

поскольку иначе их нельзя было бы использовать. Собственно один метод

— коиструктор TEditLetNum, вы уже написали. Теперь вам нужно написать метод Clear, очищающий содержимое окна редактирования и устанавливающий значение поля FModified равным false. Генерировать событие OnClear мы пока не будем. А поскольку базовый класс TEdit уже имеет метод Clear, очищающий текст, занесенный в окно редактирования,

то вы можете в вашем переопределенном методе вызвать наследуемый метод Clear и добавить задание значения FModified. Чтобы точно знать,

как выглядит объявление метода Clear в классе-предке, полезно обра-

титься к справке C++Builder и точно воспроизвести в вашем новом классе объявление переопределяемого метода. Это объявление в классе

TCustomEdit имеет вид:

virtual void __fastcall Clear(void);

Таким образом, объявление и реализация переопределенного метода

Clear может иметь вид:

class PACKAGE TEditLetNum : public TEdit

{

public:

virtual void __fastcall Clear(void);

};

void __fastcall TEditLetNum::Clear(void)

{

TEdit::Clear();

//Вызов родительского метода

FModified = false;

 

}

 

Как видно из приведенного кода, для вызова родительского метода

достаточно просто указать явным образом класс (в нашем случае TEdit),

метод которого вызывается.

Впрочем, в данном случае вы могли бы и не обращаться к

родительскому методу, а просто очистить текст в окне:

void fastcall TEditLetNum::Clear{void)

{

Text = ―‖;

FModified = false;

}

Теперь запишем главную процедуру, ради которой мы и создавали свой компонент: процедуру, разрешающую или запрещающую ввод символов того или иного типа. Для этого нам надо анализировать вводимый пользователем символ. Это можно делать при обработке события OnKeyPress. Значит, нам надо переопределить стандартную функцию генерации (т.е. стандартный обработчик) в родительском компоненте TEdit. Стандартные обработчики имеют то же имя, что и события, но без префикса Он. То есть обработчик события OnKeyPress

имеет имя KeyPress. Передаваемые в стандартный обработчик параметры те же, что вы можете видеть в заготовке процедуры обработки события,

если щелкнете на этом событии в Инспекторе Объектов, но без параметра

Sender. Например, щелкнув в Инспекторе Объектов на событии

OnKeyPress любого компонента, вы увидите, что в процедуру обработки передаются параметры:

(TObject *Sender, char SKey)

Значит в создаваемом нами обработчике и при вызове родительского обработчика надо использовать только один параметр: char &Кеу.

Впрочем, поскольку вам надо будет точно воспроизвести объявление переопределяемой функции в классе-предке, обратитесь к справке

C++Builder. Вы увидите, что функция KeyPress наследуется из класса

TWinControl и объявлена в нем следующим образом:

DYNAMIC void __fastcall KeyPress(char &Key);

Это объявление надо в точности воспроизвести в своем классе-

наследнике. Например, если вы пропустите в объявлении спецификатор

DYNAMIC, вы получите при компиляции сообщение:

[С++ Error] EditLetNum.h(24): Е2113 Virtual function '__fastcall TEditLetNum::KeyPress(char &)' conflicts with base class 'TWinControl'

Его смысл заключается в том, что возник конфликт с функцией KeyPress,

объявленной в базовом классе TWinControl. В результате компиляция не будет завершена.

Таким образом, введение в модуль компонента требуемой нам функции может свестись к включению в раздел класса protected

приведенного выше объявления и написанию в файле модуля реализации функции:

void __fastcall TEditLetNum::KeyPress(char &Key)

{

Set <char, '0', '9'> Dig;

Dig << '0' << '1' << '2' << '3' << '4' << '5' << '6' << '7' << '8' << '9'; if ((! FEnableNum) && (Dig.Contains(Key)))

Key = 0;

if ((! FEnableLet) && !(Dig.Contains(Key))) Key = 0;

if (Key != 0) FModified = true; TEdit::KeyPress (Key);

}

Эта функция сначала заменяет недопустимые символы нулевыми. Если символ допустимый, то задается значение true полю свойства FModified. В

конце вызывается метод KeyPress родительского класса.

Можете скомпилировать и протестировать полученный компонент.

Удобно воспользоваться для параллельной разработки компонента и его теста Менеджером Проектов, включив в группу проекта пакет My.bpi и

приложение Test. Форма содержит компонент EditLetNum, кнопку Button с

надписью Clear (при ее нажатии выполняется метод Clear), метку Labe1, в

которой отображается значейие свойства Modified компонента

EditLetNum1, и три индикатора типа TCheckBox. Два из них (назовите их

CBNum и CBLet) указывают допустимость ввода цифр и букв. О третьем речь пойдет в следующем разделе.

Обработчик события OnCreate формы может иметь вид: void __fastcall TForml::FormCreate(TObject *Sender)

{

if (EditLetNuml->Modified) Label1->Caption = "Modified = true";

else Label1->Caption = "Modified = false";

}

Этот обработчик заносит в метку Label1 сообщение о текущем значении свойства Modified.

В обработчике события OnKeyUp компонента EditLetNum напишите оператор:

FormCreate(Sender);

Этот оператор вызывает тот же приведенный выше обработчик события

OnCreate формы для отображения текущего значения Modified.

Обработчик события OnClick индикатора CBNum может иметь вид:

EditLetNum1->EnableNum = CBNum->Checked;

CBLet->Checked = EditLetNum1->EnableLet;

EditLetNum1->SetFocus();

Первый оператор этого обработчика устанавливает значение параметра

EnableNum в зависимости от состояния индикатора CBNum. Второй оператор устанавливает состояние индикатора CBLet равным значению параметра EnableLet. Это надо, поскольку если, например, сбрасывается в false значение параметра EnableNum, а значение параметра EnableLet в

этот момент тоже было равно false, то компонент EditLetNum1 установит значение EnableLet равным true. И надо, чтобы это новое значение отобразилось в индикаторе CBLet.

Аналогично может выглядеть обработчик события OnClick индикатора

CBLet:

EditLetNum1->EnableLet = CBLet->Checked;

CBNum->Checked = EditLetNum1->EnableNum;

EditLetNum1->SetFocus();

В обработчике события OnClick кнопки Clear напишите: EditLetNum1->Clear();

FormCreate(Sender);

Эти операторы проверят работу введенного вами метода Clear и

отобразят на экране изменение значения свойства Modified.

Оттранслируйте компонент. В вашем тестовом приложении установите свойства EnableNum и EnableLet в соответствии с тем, какие исходные установки индикаторов CBNum и CBLet вы задали.

Активизируйте в окне Менеджера Проектов вершину вашего тестового приложения, запустите его на выполнение m проверьте в работе при различных значениях свойств EnableNum и EnableLet.

Если в написанных файлах вашего нового компонента что-то не в порядке, вам может захотеться ввести в файл реализации компонента какие-то точки прерывания. В Windows 2000/ХР и NT проблем не

возникнет. При запуске теста на выполнение точки прерывания, введен-

ные в файл компонента, окажутся недоступными. А если вы активизируете в окне Менеджера Проектов вершину пакета и попробуете выполнить его, то получите сообщение: «Cannot debug project unless a host application is defined. Use Run | Parameters… dialog box». Это означает, что вы сначала с помощью команды Run | Parameters должны определить хост

— тестирующее приложение. Выполните эту команду, предварительно активизировав в окне Менеджера Проектов вершину пакета. Перед вами откроется диалог. В верхнем окошке вы должны указать файл тестирующего приложения. Для этого можете воспользоваться кнопкой:

поиска Browse. Кнопка Load позволяет вам загрузить тестирующее приложение и начать его выполнение по шагам. Затем можете запускать выполнение обычной командой Run | Run (F9). Впрочем, после того, как вы один раз задали хост для пакета в окне, вы можете в дальнейшем запускать пакет на выполнение обычным образом.

В нашем примере требуется ввести событие OnClear, происходящее в момент очистки окна методом Clear. В C++Builder событие — это просто специально свойство, являющееся указателем функции. Тип обобщенного указателя на функцию, которой передается один параметр типа

TComponent (обычно this), - TNotifyEvent. Подобный тип используется в

C++Builder для событий типа OnClick и многих других, которые передают в обработчик только один параметр — TObject *Sender. Подойдет этот тип и нам для события OnClear.

В этом случае объявления нашего события могут иметь вид: private:

iTNotifyEvent FOnClear;

__published:

__property TNotifyEvent OnClear = (read=FOnClear, write=FOnClear} ;

Эти объявления создают поле FOnClear типа TNotifyEvent,

соответствующее событию. А само событие объявляется точно так же, как любое свойство. Только в read и write записываются не функции чтения и записи, а само поле.

Остается только вызвать в нужный момент обработчик события пользователя, если пользователь его предусмотрел. Проверка, имеется ли обработчик пользователя, осуществляется проверкой соответствующего события как булевой величины, возвращающей true, если пользователь предусмотрел свой обработчик. В нашем случае эта проверка и вызов обработчика пользователя осуществляется добавлением в начало написанной ранее процедуры метода Clear оператора:

if (OnClear) OnClear(this);

который вызывает обработчик пользователя OnClear.

Внесите указанные изменения в файлы компонента, откомпилируйте их, выделите на форме тестового приложения компонент EditLetNum1 и

посмотрите страницу событий в Инспекторе Объектов. Вы увидите, что в списке его событий появилось событие OnClear. Дважды щелкните на его правом окне и вы увидите в тексте модуля заготовку для обработчика этого события:

void __fastcall TForm1::EditLetNum1Clear(TObject *Sender)

{

}

Внесите в этот обработчик какой-нибудь оператор, который бы отображал на экране факт свершения этого события, например:

ShowMessage("Событие OnClear");

Проверьте ваше тестовое приложение в работе.

Мы рассмотрели простейший вариант задания события типа

TNotifyEvent. Попробуем несколько усложнить это событие. Пусть мы будем передавать в это событие параметр CanClear, который по

умолчанию равен true, что будет означать разрешение очистить текст окна редактирования. Но предоставим пользователю возможность в своем обработчике события задать этому параметру значение false, что будет означать отказ от очистки.

Чтобы решить эту задачу, вам надо объявить новый тип события. Это объявление делается с помощью ключевого слова __closure. Пусть,

например, вы хотите дать вводимому новому типу событий имя TClear.

Тогда объявление указателя на этот тип может иметь вид: typedef void __fastcall (__closure *TClear)

(System::TObject *Sender, bool& CanClear);

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

Приведенное выше объявление типа должно помещаться перед описанием класса. Тогда в объявлениях свойства и его поля надо ссылаться на этот тип, следующим образом изменив соответствующие операторы:

private:

TClear FOnClear;

__property TClear OnClear = {read=FOnClear, write=FOnClear};

Осталось изменить метод Clear так, чтобы в нем учитывался новый параметр CanClear:

void fastcall TEditLetNum::Clear()

{

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]