3574
.pdfсумма – float
10.ЦЕХ имя – char*
начальник – char* количество работающих – int
11.ПЕРСОНА имя – char*
возраст – int
пол – int(bool)
12.АВТОМОБИЛЬ марка – char* мощность – int стоимось – float
13.СТРАНА
имя – char*
форма
правления – char* площать – float
14.ЖИВОТНОЕ имя – char*
класс – char* средний вес – int
15.КОРАБЛЬ имя – char*
водоизмещение – int
тип – char*
4. Контрольные вопросы.
1.Что такое конструктор? Для чего он используется? 2.Что такое деструктор? Какой формат его описания?
3.Как определяется указатель на компоненту-функцию?
4.Как определяется указатель на экземпляр класса?
Лабораторная работа №3. Структура класса компонента.
1.Цель лабораторной работы.
Целью лабораторной работы является освоение практических навыки создания иерархии классов и использования статических компонентов класса.
2.Теоретический материал для домашнего изучения.
Заготовка модуля компонента, созданная в результате указанных ранее действий, имеет следующий вид.
Файл EditLetNum.h: #ifndef EditLetNumH fdefine EditLetNumH
// ---------------------------------------------------------------
finclude <SysUtils.hpp> linclude<Controls.hpp> linclude <Classes.hpp> #include <Forms.hpp> finclude <StdCtrls. hpp>
// ---------------------------------------------------------------
class PACKAGE TEditLetNum : public TEdit
{
private:
protected:
public:
__fastcall TEditLetNum(TComponent* Owner); __published:
}; // ---------------------------------------------------------------
#endif
Файл EditLetNum.cpp: tinclude <vcl.h> #pragma hdrstop finclude "EditLetNum.h"
#pragma package(smart_init)
// -------------------------------------------------------------
//(Функция ValidCtrCheck используется для проверки того,
//что компонент не содержит чистых виртуальных функций)
static inline void ValidCtrCheck{TEditLetNum *)
{
new TEditLetNum(NULL);
}
// ---------------------------------------------------------------
__fastcall TEditLetNum::TEditLetNum(TCompanent* Owner) : TEdit(Owner)
{
}
// ---------------------------------------------------------------
namespace Editletnum {
void __fastcall PACKAGE Register()
{
TComponentClass classes[1] = {__classid(TEditLetNum)};
RegisterComponents("Мои компоненты", classes, 0);
}
}
Собственно говоря, в этой заготовке пока только каркас класса будущего модуля. Файл EditLetNum.cpp содержит три процедуры:
ValidCtrCheck, конструктор TEditLetNum и Register. Процедура
ValidCtrCheck носит вспомогательный характер и вводится, чтобы проверять, не содержит ли компонент чистых виртуальных функций. Тело
конструктора TEditLetNum пока пустое. Позднее оно будет заполнено. А
процедуру Register, регистрирующую компонент на заданной странице библиотеки.
Код регистрации компонента начинается с оператора namespace.
Ключевое слово namespace устанавливает локальность имен данной процедуры регистрации. После этого ключевого слова следует имя файла,
содержащего компоненты. Имя пишется символами в нижнем регистре,
кроме первой заглавной буквы.
В процедуре регистрации Register первый оператор создает массив регистрируемых компонентов classes тнпа TComponentClass и заносит в него регистрируемый компонент. Если бы вы создали два компонента
(пусть имя второго из них TEdit2), регистрируемых на одной странице библиотеки, вы моглн бы занести их в массив оператором:
TComponentClass classes [2] = {__classid(TEditLetNum), __classid(TEdit2)};
Следующий оператор процедуры регистрации регистрирует функцией
RegisterComponents компоненты, занесенные в classes (второй параметр функции) на странице Мои компоненты (первый параметр). Последний параметр является последним индексом массива регистрируемых компонентов.
Теперь рассмотрим коротко описание класса в заголовочном файле
EditLetNum.h. В нем есть разделы private, protected, public и __published.
Они определяют четыре варианта доступа к переменным, процедурам и функциям:
private |
Процедуры и функции, определенные таким |
(закрытые) |
образом, доступны только в пределах данного |
|
модуля. |
|
|
protected |
Процедуры и функции, определенные таким |
(защищенные) |
образом, доступны в классах потомков. |
|
|
public (открытые) |
Эти процедуры и функции доступны везде. |
|
|
__published |
Процедуры и функции доступны везде и |
(опубликованные) |
имеют связь со средой разработки C++Builder, |
|
обеспечивающую вывод на экран в |
|
Инспекторе Объектов страниц информации о |
|
свойствах и событиях. |
|
|
Определение объекта (компонент — это объект) выступает как совокупности свойств, методов и обработчиков событий. Причем свойства
— это совокупность полей данных и методов их чтения и записи.
Вспомним также принцип скрытия информации. Исходя из этого,
разработчик компонента должен продумать, в какие разделы вставить вводимые им поля данных, свойства и методы.
Задание свойств
Поля данных всегда должны быть защищены от несанкционированного доступа. Поэтому их целесообразно определять в private — закрытом разделе класса. В редких случаях их можно помещать в protected —
защищенном разделе класса, чтобы возможные потомки данного класса имели к ним доступ. Традиционно идентификаторы полей совпадают с именами соответствующих свойств, но с добавлением в качестве префикса символа ―F‖. Таким образом, в нашем примере вы можете занести в раздел private объявления трех необходимых нам полей данных:
class PACKAGE TEditLetNum : public TEdit
{
private:
// Закрытые элементы-данные класса bool FEnableNum;
bool FEnableLet; bool FModified; } ;
Теперь надо объявить свойства — методы чтения и записи этих полей.
Свойство объявляется оператором вида:
__property <тип> <имя> = {read=<имя поля или метода чтения> write=<имя поля или метода записи> <директивы запоминания и значения по умолчанию>;
Если в разделах read или write записано имя поля, значит,
предполагается прямое чтение или запись данных.
Если в разделе read записано имя метода чтения, то чтение будет осуществляться только функцией с этим именем. Функция чтения — это функция без параметра, возвращающая значение того типа, который объявлен для свойства. Имя функции чтения принято начинать с префикса
Get, после которого следует имя свойства.
Если в разделе write записано имя метода записи, то запись будет осуществляться только процедурой с этим именем. Процедура записи — это процедура с одним параметром того типа, который объявлен для свойства. Имя процедуры записи принято начинать с префикса Set, после которого следует имя свойства.
Если раздел write отсутствует в объявлении свойства, значит это свойство только для чтения и пользователь не может задавать его значение.
Директивы запоминания определяют, как надо сохранять значения свойств при сохранении пользователем файла формы *.dfm. Чаще всего используется директива
default = <значение по умолчанию>
Она не задает начальные условия. Это дело конструктора. Директива просто говорит, что если пользователь в процессе проектирования не изменил значение свойства по умолчанию, то сохранять значение свойства не надо.
Итак, для нашего примера объявления свойств могут иметь вид: public:
__fastcall TEditLetNum(TComponent* Owner); // Свойство только времени выполнения
__property bool Modified = {read=FModified, default=false}; __published:
// Свойства компонента, включаемые в Инспектор Объектов
__property bool EnableLet = {read=FEnableLet, write=SetEnableLet, default=true};
__property bool EnableNum = {read=FEnableNum, write=SetEnableNum, default=true};
Объявление свойства Modified помещается в раздел public, поскольку это свойство должно быть доступно только во время выполнения.
Свойства EnableNum и EnableLet помещаются в раздел published, так как они должны отображаться в Инспекторе Объектов во время проектирования.
Свойства EnableNum и EnableLet имеют прямой доступ к полям для чтения. Но для записи они имеют методы SetEnableNum и SetEnableLet
соответственно. Это связано с тем, что при записи свойств надо проверять,
не окажутся ли значения обоих этих свойств равными false. Подобное задание значений надо предотвращать, поскольку в этом случае в окно редактирования вообще ничего нельзя будет ввести.
Свойство Modified вообще не имеет метода записи, поскольку оно предназначено только для чтения.
Указанные в объявлениях методы записи могут быть реализованы обычными функциями, объявления которых помещаются в private —
закрытый раздел класса, а их реализация включается в тело модуля. Для того чтобы задать свойствам начальные значения, надо еще включить соответствующие операторы в тело конструктора. В итоге в данный момент модуль компонента может иметь следующий вид.
Файл EditLetNum.h:
class PACKAGE TEditLetNum : public TEdit
{
private:
// Закрытые элементы-данные класса
bool FEnableLet; bool FEnableNum; bool FModified; protected:
// Защищенные методы записи
void __fastcall SetEnableLet{bool AEnableLet); void __fastcall SetEnableNum(bool AEnableNum);
public:
// Объявление конструктора
__fastcall TEditLetNum(TComponent* Owner); // Свойство только времени выполнения
__property bool Modified = {read=FModified, default=false}; _ published:
// Свойства компонента, включаемые в Инспектор Объектов
__property bool EnableLet = {read=FEnableLet, write=SetEnableLet, default=true};
__property bool EnableNum = {read=FEnableNum, write=SetEnableNum, default=true};
} ;
Файл EditLetNum.cpp:
static inline void ValidCtrCheck(TEditLetNum *) {
new TEditLetNum(NULL);
}
//--------------------------------------------------------------------
__fastcall TEditLetNum::TEditLetNum(TComponent* Owner) : TEdit(Owner)
{
FEnableLet = true;
FEnableNum = true;
FModified = false;
}
//--------------------------------------------------------------------
namespace Editletnum {
void _ fastcall PACKAGE Register()
{
TComponentClass classes[l] = {__classid{TEditLetNum)); RegisterComponents{"MyComponents", classes, 0);
}
}
//--------------------------------------------------------------------
void __fastcall TEditLetNum::SetEnableNum(bool AEnableNum)
{
//Присваивание значения полю FEnableNum FEnableNum = AEnableNum;
//Если значения FEnableNum и FEnableLet = false,
//то полю FEnableLet присваивается true if(!AEnableNum)
if(!FEnableLet) FEnableLet = true;
}
//---------------------------------------------------------------------------------------
void __fastcall TEditLetNum:: SetEnableLet(bool AEnableLet)
{
//Присваивание значения полю FEnableLet FEnableLet = AEnableLet;
//Если значения FEnableNum и FEnableLet = false,
//то полю FEnableNum присваивается true if(!AEnableLet)
if(!FEnableNum) FEnableNum = true;
}
Процедуры, реализующие методы записи, присваивают полю переданное в них значение параметра и в случае, если передано значение false, проверяют значение другого поля. Если и другое поле имеет значение false, то оно исправляется на true.
Объявления процедур записи включены в раздел protected. Это означает, что они закрыты для внешнего пользователя, но доступны для возможных потомков вашего класса.
Конструктор объявлен в открытом разделе класса public и имеет имя
TEditLetNum, совпадающее с именем класса. В реализации конструктора задаются начальные значения новым свойствам, которые вы добавили в компонент.
Теперь давайте сохраним подготовленные файлы, откомпилируем их
(кнопка Compile в Диспетчера Пакетов) и построим тестовое приложение для отладки установленного компонента. Хотя он еще не до конца создан,
кое-что уже можно увидеть.
Откройте новый проект и внесите в него с соответствующей страницы библиотеки ваш новый компонент. Откройте (командой File | Open) файл вашего компонента, чтобы можно было вносить в него какие-то изменения. Сохраните проект под каким-нибудь именем (например,