Первое приложение
Создадим следующее простое приложение. На форме расположим кнопку и при ее нажатии на форме должен появиться красный кружок.
Рассмотрим из каких файлов состоит проект приложения.
Проект приложения состоит из четырёх основных типов файлов:
Файлы описания форм — двоичные файлы с расширением DFM, описывающие формы с компонентами. В этих файлах запоминаются начальные значения свойств, установленные вами в инспекторе объектов.
Файлы программных модулей — текстовые файлы с расширением Н и СРР, содержащие исходные коды форм на языке C++. Н-файлы содержат интерфейсные части форм, а СРР-файлы — части реализации. В СРР-файлах вы записываете код обработчиков событий.
Главный программный файл — текстовый файл с расширением СРР, содержащий список всех используемых программных модулей, а также стандартную для всех программ на языке C++ функцию WinMain(), обеспечивающую запуск приложения. Главный программный файл создается и контролируется средой C++Builder автоматически.
Файл проекта — текстовый файл с расширением .mak в Builder 1 и .bpr для Builder 3, содержащий всю необходимую информацию для компилятора, используемую при трансляции и сборке программных модулей. Он тоже создается автоматически.
Файлы описания форм
Первая составная часть проекта — это двоичный файл с расширением DFM, описывающий форму. В DFM-файле сохраняются все установки свойств формы и ее компонентов, сделанные вами во время проектирования приложения. Количество DFM-файлов равно количеству используемых в приложении форм. Например, в нашем примере используется только одна форма, поэтому и DFM-файл только один — Unit1.dfm.
Иногда в литературе DFM-файл называют графическим образом формы. Это всего лишь сравнение, профессиональный программист должен знать о форме чуточку больше. На самом деле образ формы в DFM-файле не является графическим, он скорее описательный. В DFM-файле попросту хранятся исходные значения для свойств формы и ее компонентов, заданные вами в инспекторе объектов. Хотя DFM-файл — двоичный, у него существует текстовое представление.
DFM-файл нужен только на этапе проектирования. При сборке приложения данные DFM-файла помещаются в область ресурсов выполняемого ЕХЕ-файла. Во время работы приложения они интерпретируются, в результате форма и ее компоненты получают нужные свойства и отображаются на экране так, как вы задали при проектировании.
Файл Uint1.dfm
object OneForm: TOneForm
Left = 283
Top = 192
Width = 544
Height = 375
Caption = 'OneForm'
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'MS Sans Serif'
Font.Style = []
PixelsPerInch = 96
TextHeight = 13
object Button1: TButton
Left = 208
Top = 160
Width = 75
Height = 25
Caption = 'Draw'
TabOrder = 0
OnClick = Button1Click
end
end
Файлы программных модулей
Каждой проектируемой в визуальной среде форме соответствует свой программный модуль, состоящий из двух файлов: заголовочного файла с расширением Н и программного файла с расширением СРР. Заголовочный файл содержит программный интерфейс формы на языке C++; он генерируется средой C++Builder автоматически. Программный файл содержит реализацию интерфейса формы на языке C++, в частности методы обработки событий; он создается программистом, т. е. вами.
Количество программных модулей может превышать количество форм. Почему? Потому, что в ряде случаев программные модули могут и не относиться к формам, а содержать вспомогательные подпрограммы и данные. Наша задача об идеальном весе очень простая, поэтому в ней имеется только один программный модуль, относящийся к форме. Он состоит из двух файлов: Unitl.h и Unitl.cpp.
Эти файлы необходимо внимательно изучить.
Обычно в редакторе кода виден лишь СРР-файл, поскольку именно в нем записывается алгоритм решения задачи. Н-файл остается скрыт до тех пор, пока вы не выберете в меню команду View | Source/Header File. В результате выполнения этой команды в редакторе кода появится страница следующего содержания:
Файл Uint.h
//---------------------------------------------------------------------------
#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
//---------------------------------------------------------------------------
class TOneForm : public TForm
{
__published: // IDE-managed Components
TButton *Button1;
void __fastcall Button1Click(TObject *Sender);
private: // User declarations
public: // User declarations
__fastcall TOneForm(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TOneForm *OneForm;
//---------------------------------------------------------------------------
#endif
Дадим необходимые комментарии к тексту заголовочного файла. Первые две строки и последняя строка файла содержат директивы условной компиляции:
#ifndef Unit1H
#define Unit1H
…
#endif
Здесь они нужны для того, чтобы исключить повторную компиляцию заголовочного файла при его повторном включении в один и тот же программный файл. Это случается в сложных проектах, когда программные модули используют друг друга.
Директива #ifndef (от английского if not defined — если не объявлен) проверяет, не была ли объявлена константа UnitlH. Если такое объявление уже было, компилятор пропускает текст до строки с директивой #endif. В противном случае объявляется константа Unit1H и компилируется весь заголовочный файл.
Перейдем к следующим строкам заголовочного файла Unit1.h. Они содержат директивы включения необходимых заголовочных файлов библиотеки VCL:
#include <vcl\Classes.hpp>
#include <vcl\Controls.hpp>
#include <vcl\StdCtrls.hpp>
#include <vcl\Forms.hpp>
Встретив директиву #include, компилятор просто подставляет содержимое заданного файла вместо нее и продолжает трансляцию модуля (т. е. фактически осуществляется компиляция всех записанных заголовочных файлов). Среда C++Builder формирует эти строки без вашего участия (в зависимости от используемых компонентов), но, в принципе, список подключенных файлов можно изменять и вручную.
Далее в заголовочном файле объявлен класс формы. По умолчанию он называется Tform1 и порожден от стандартного класса TForm. Помещенные на форму компоненты представлены полями формы. У нас на форме шесть компонентов, поэтому и полей в описании класса тоже шесть. Имена полей совпадают с именами компонентов, заданными в инспекторе объектов.
После полей идут заголовки методов обработки событий. Название каждого такого метода среда C++Builder формирует автоматически на основании имени компонента и имени генерируемого им события. Например, для кнопки Button1 метод обработки события OnClick называется ButtonlClick.
Обратите внимание, что поля, представляющие компоненты формы, и методы обработки событий получают атрибут видимости __published (он принимается по умолчанию для всех наследников TForm). Благодаря этому вы можете работать с ними на визуальном уровне, например видеть их имена в инспекторе объектов. Поскольку среда C++Builder умеет понимать содержимое секции __published на этапе проектирования, никогда не модифицируйте эту секцию вручную, пользуйтесь визуальными инструментами: палитрой компонентов и инспектором объектов. Запомните:
Когда вы помещаете на форму компоненты, среда C++Builder добавляет в описание класса соответствующие поля, а когда вы удаляете компоненты с формы, среда C++Builder удаляет их поля из описания класса.
Когда вы определяете в форме или компонентах обработчики событий, среда C++Builder определяет в классе соответствующие методы, а когда вы удаляете весь код из методов обработки событий, среда C++Builder удаляет и сами методы.
Для вашего удобства в классе формы заранее объявлены пустые секции private и public, в которых вы можете размещать любые вспомогательные поля, методы и свойства. Визуальная среда C++Builder их "не видит", поэтому с ними можно работать только на уровне исходного текста в редакторе кода.
Вы наверняка обратили внимание на ключевое слово __fastcall в заголовках методов. Оно указывает, что при вызове данного метода параметры передаются через внутренние регистры процессора, а не как обычно — через стековую память. Это увеличивает быстродействие программы, поэтому все стандартные функции из библиотеки VCL объявлены таким образом.
После описания класса объявлена ссылка на объект данного класса, это необходимо для того, чтобы использовать объект класса в других программных модулях.
extern PACKAGE TOneForm *OneForm;
Н-файл определяет интерфейс программного модуля. Реализация интерфейса выполняется в СРР-файле:
Файл Uint1.cpp
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TOneForm *OneForm;
//---------------------------------------------------------------------------
__fastcall TOneForm::TOneForm(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TOneForm::Button1Click(TObject *Sender)
{
OneForm->Canvas->Brush->Color=clRed;
OneForm->Canvas->Ellipse(100,100,30,30);
}
//---------------------------------------------------------------------------
Программный файл начинается с включения стандартного заголовочного файла библиотеки VCL, в котором объявлены часто вызываемые подпрограммы, а также классы помещенных на форму компонентов:
#include <vcl\vcl.h>
Затем следует директива #pragma hdrstop, появление которой связано с тем, что стандартные заголовочные файлы имеют достаточно большие размеры (десятки и сотни тысяч строк), поэтому компилятор затрачивает много времени на трансляцию этих файлов каждый раз, когда вы заново компилируете проект. Для решения этой проблемы компилятор транслирует заголовочные файлы в объектный код только один раз и помещает его в специальный файл с расширением CSM. Имя CSM-файла совпадает с именем проекта. При повторной компиляции проекта C++Builder не транслирует подключаемые файлы, а просто считывает их объектный код из CSM-файла, что существенно (в 3—4 раза) ускоряет процесс получения выполняемого ЕХЕ-файла. При появлении в модуле нового заголовочного файла компилятор транслирует его содержимое и добавляет в CSM-файл. Этот механизм эффективен только для стандартных библиотечных Н-файлов, поскольку их содержимое никогда не изменяется. В остальных случаях он не нужен и останавливается специальной директивой #pragma hdrstop.
Рассмотрим следующую строку программы #include "Unitl.h"
Эта директива подключает заголовочный файл Unitl.h к программному файлу.
Следующая директива компилятора #pragma resource "*.dfa"служит для подключения файла описания формы.
Этой директивой подключается только один DFM-файл, в котором описана форма данного программного модуля. Имя DFM-файла получается путем замены звездочки на имя файла, в котором записана директива.
Далее объявлена ссылка на объект формы: TOneForm *OneForm;
OneForm — это переменная, которая содержит указатель на объект класса TOneForm. Конструирование объекта OneForm выполняется в главном программном файле (рассмотрено ниже).
После всех объявлении в программном файле реализуются методы обработки событий. Среда C++Builder создает для них пустые заготовки, а вы начиняете их операторами:
void __fastcall TOneForm::Button1Click(TObject *Sender)
{
OneForm->Canvas->Brush->Color=clRed;
OneForm->Canvas->Ellipse(100,100,30,30);
}
Если вы хотите удалить метод обработки события и убрать ссылки на него, просто сделайте метод пустым, удалив весь написанный вами код, включая комментарий. При сохранении или компиляции проекта среда C++Builder выбросит из текста пустые методы.
При внимательном изучении исходного текста модуля остается невыясненным один вопрос: чем обеспечивается вызов метода ButtonlClick при нажатии на форме кнопки, ведь эти методы как бы "повисают в воздухе". Все очень просто. Загляните в DFM-файл, точнее, в его текстовое представление, приведенное выше. Кроме установки значений свойств вы найдете установку и обработчика события:
object Button1 : Tbutton
…
OnClick = ButtonlClick
еnd
Таким образом обеспечивается привязка методов формы к соответствующим событиям.
Главный программный файл и файл проекта
В самом деле, как компилятору узнать, какие конкретно файлы входят в проект? Должно же быть какое-то организующее начало? Да, и оно есть. Это два файла: главный программный файл с расширением СРР и соответствующий ему файл проекта с расширением *.mak в C++Builder1 и с расширением *.bpr в C++Builder3. Имена главного программного файла и файла проекта всегда совпадают.
В главном программном файле перечислены все программные модули и располагается стандартная для программ на языке C++ функция WinMain, обеспечивающая запуск приложения. В файле проекта содержатся указания по компиляции и компоновке всех файлов, составляющих проект.
Когда вы по команде File | New Application начинаете разработку нового приложения, среда C++Builder автоматически создает главный программный файл и файл проекта. По мере создания новых форм содержимое этих файлов видоизменяется автоматически.
Чтобы увидеть главный программный файл приложения, вычисляющего идеальный вес, выберите в меню C++Builder команду View | Project Source. В редакторе кода появится новая страница со следующим текстом: