[ Новые сообщения · Участники · Правила форума · Поиск · RSS ]
  • Страница 1 из 2
  • 1
  • 2
  • »
Micro-Life Форум » Программирование » C и C++ » Уроки изучения Borland C++ Builder 5
Уроки изучения Borland C++ Builder 5
iBlackДата: Среда, 14.10.2009, 21:22 | Сообщение # 1
Main Admin
Сообщений: 51
[ 3 ]
:-)
Все материалы взяты с открытых источников!!!

Шаг 1 - Знакомство с С++ Builder 5

Этот раздел рассказывает об объектно-ориентированном программировании в среде C++ Builder 5. Правда, я не рассказываю об общем синтаксисе C++, где нужно, просто даю пояснения о расширениях. Я рекомендую прочитать какую-нибудь из книг именно по C++, например Бьярна Страустрапа "Введение в C++".

Язык C++, используемый в этой системе, не сильно отличается от стандарта ANSI. Он дополнен некоторыми ключевыми словами, позволяющими использовать в полной мере преимущества Windows-программирования.

Прежде, чем приступить к описанию этой системы, я должен, как и любой автор, отметить, что C++ Builder и Delphi похожи, как близнецы-братья. Хотя это заезженная истина, но, тем не менее, несмотря на похожесть, методы программирования на них отличаются. Впрочем, переход от одной системы к другой не будет особенно затруднительным. Вот такой дисклеймер :).

Стандартной библиотекой C++ Builder является VCL, а не MFC или OWL, как в других системах программирования на C++ под Windows.

VCL - библиотека визуальных компонентов. Их вид можно менять на стадии проектирования, а не только на стадии выполнения (run-time).

Тем не менее, C++ Builder позволяет использовать и эти две библиотеки. Также возможно прямое обращение к функциям Windows API и вызов из DLL. В общем, куда хочешь залезь и что хочешь запусти.

С++ Builder поставляется в трех вариантах - Standard, Professional и Enterprise. Для нас большой разницы в цене нет благодаря отечественным морякам CD дисков, так что я буду рассматривать все примеры на базе Enterprise.

Вариант Enterprise позволяет работать с COM и CORBA, базами данных (SQL, Paradox, dBase, MS OLE DB, Access 97, FoxPro, InterBase, Oracle, Sybase, Informix, DB2), Интернетом(TCP/IP, HTTP, FTP, NNTP, SMTP, POP3, CGI, WinCGI, ISAPI, NSAPI). Кроме того, содержит встроенные средства интегрированной отладки, о которых я расскажу в следующих шагах.

Инсталляция обычно проходит без проблем, главное, не забудьте посмотреть код диска. Кто не знает, его обычно пишут в .diz файле или Lisense.txt.

Я думаю, сейчас я рассказал достаточно, чтобы приступить к этакому quick-tour'у по C++ Builder.

Для начала ознакомимся со средой программирования:

На экране 4 окна - главное(верхнее), окно Инспектора объектов слева, Редактора кода справа и Дизайнер форм под окном Редактора.

В главном окне видна Палитра компонентов. Палитра - один из основных методов ускоренного программирования. На палитре размещены компоненты VCL на нескольких вкладках. Название каждой из вкладок довольно хорошо характеризует ее содержимое.

Палитра используется вместе с Дизайнером форм. Чтобы создать на форме компонент, выберите его на Палитре и растяните по необходимым размерам на форме. Дизайнер отображает форму практически в том виде, в котором она будет при запуске, если вы, конечно, не создадите какие-то компоненты "at run-time".

Для модификации внешнего вида и параметров компонента, а также задания обработчиков(handlers) событий, используйте Инспектор объектов. Для этого выберите какой-то компонент на форме или из выпадающего списка (сама форма - тоже компонент), и на первой вкладке вы увидите свойства данного объекта, а на второй вкладке - обработчиков событий. Для создания нового обработчика или перехода к уже имеющемуся используйте двойной щелчок по соответствующей строке Инспектора. Это приведет вас к тексту в окне Редактора (а куда же еще :)).

Также существует некоторое количество других окон, некоторые из которых могут быть "причалены" в окне Редактора, о них я расскажу в последующих шагах.

Вроде для ознакомления с системой достаточно. В следующем шаге будет минимальный проект.

iBlackДата: Среда, 14.10.2009, 21:24 | Сообщение # 2
Main Admin
Сообщений: 51
[ 3 ]
:-)
Шаг 2 - Первый проект [TLabel, TEdit, TButton]

Новый проект создается автоматически при запуске C++ Builder. Также можно выбрать команду File->New Application или открыть т.н. "Хранилище новых объектов" командой File->New.

Итак, каким-то из способов мы сделали начальную программу. Теперь сделаем что-то, преследующее банальную, но подходящую цель - решение квадратного уравнения.

Перетащите на форму компоненты TLabel , TEdit и TButton и разместите, как показано на рисунке (чтобы разместить сразу несколько компонентов, нажмите на его кнопку при нажатой клавише Shift). Чтобы модифицировать размеры компонентов, нужно ввести соответствующие значения в свойства Top, Left, Width, Height Инспектора или мышью навести на угол компонента и перетащить его. Короче, визуально.

Для изменения имени выбранного компонента используйте свойство Name.

Чтобы модифицировать надписи этих компонентов, нужно изменить свойство Caption в Инспекторе объектов. Для определения обработчика события OnClick(), которое возникает при нажатии на объект кнопкой мыши, можно пойти двумя путями: ввести имя или выбрать его из списка доступных в правой вкладке Инспектора, или просто два раза щелкнуть по кнопке. После этого в Редакторе откроется место для ввода кода:

Это, собственно, и есть процедура обработки события. Сюда нужно ввести следующие строчки:

Code
#include <math.h>

void __fastcall TForm1::Button1Click(TObject *Sender)
{
   double a,b,c;
   a=Edit1->Text.ToDouble();
   b=Edit2->Text.ToDouble();
   c=Edit3->Text.ToDouble();
   int d=b*b+4*a*c;
   if (d<0){
    Label6->Caption="Нет корней!!!";
    return;
   };
   double x1=(-b+sqrt(d))/(2*a);
   double x2=(-b-sqrt(d))/(2*a);
   Label6-<Caption=String(x1)+"   ;\r\n   "+String(x2);
}

Здесь, по-моему, все понятно - Edit1, Edit2 и Edit3 - соответствующие области ввода, а Label6 - надпись, здесь невидимая, поскольку с нулевым текстом, а в программе выводящая результат. Свойство Text объектов класса TEdit содержит введенную строку. Программа выводит полученные корни или говорит, что их нет.

Честно говоря, пример далек от совершенства - программа не контролирует, вводятся числовые или символьные данные и при ошибке просто "вылетает". Но для общего ознакомления подходит (наверное).

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

Компонент TLabel

* Caption - Определяет надпись, выводимую объектом
* Align - Выравнивание метки
* AutoSize - Автоматическое масштабирование метки по длине введенного текста
* Enabled - Разрешена ли метка
* Color - Цвет метки
* Font - Стандартное свойство типа TFont. Содержит вложенные свойства, которые означают именно то, как они называются. Можно выбрать тип шрифта из открывающегося по двойному щелчку диалога.
* ParentFont - Определяет, нужно ли использовать шрифт родителя по умолчанию.
* Transparent - Прозрачность фона метки.
* ShowAccelChar - Можно ли использовать "быструю клавишу", которая определяется символом после символа "&" и переводит фокус ввода на компонент, определенный в свойстве FocusControl.
* WordWrap - Автоматический перенос слов, если они не умещаются на строке.

Компонент TEdit

* Anchors - Свойство типа множество, которое определяет, как будет изменяться длина компонента при изменении размеров формы.
* BorderStyle - Окантовка области ввода.
* Color - Цвет фона.
* ReadOnly - Запрещает редактирование введенного текста.
* PasswordChar - Отображение одного и того же символа (например, звездочки) вместо вводимого текста. Ну, вы сами знаете, зачем это нужно.
* Text - Содержит введенную строку типа AnsiString. Вообще, этот тип является стандартным для VCL и будет подробно рассмотрен в дальнейшем.

Компонент TButton

* OnClick - Событие, возникающее при нажатии на кнопку клавишей мыши.
* Cancel - Говорит о том, что при нажатии клавиши ESC или закрытии дилогового окна используется обработчик события OnClick() данной кнопки.
* Default - Кнопка выбирается по умолчанию и обводится рамкой.
* Caption - Подпись на кнопке.
* ModalResult - В модальных диалоговых окнах значение этого свойства, не равное mrNone, при нажатии на кнопку закроет окно и запишет свое значение в свойство ModalResult формы.
* PopupMenu - Контекстное меню, выходящее при нажатии на компонент правой кнопкой мыши.

Вот. На одном дыхании целый шаг написал. Ну, пока хватит, а то клавиатура устала smile

iBlackДата: Среда, 14.10.2009, 21:31 | Сообщение # 3
Main Admin
Сообщений: 51
[ 3 ]
:-)
Шаг 3 - Маленький текстовый редактор [TMainMenu, TMemo, TOpenDialog, TSaveDialog]

Теперь мы для примера попробуем сделать простой текстовой редактор, с меню Файл и ... без ничего! Минимализм.

Сначала пустую форму, как обычно. Затем перетащите компоненты TMainMenu и TMemo на форму. Также с вкладки Dialogs возьмите компоненты TOpenDialog и TSaveDialog .

Отредактируем меню. Для этого нужно два раза щелкнуть по TMainMenu. Откроется Дизайнер меню. Введите названия пунктов в свойство Caption. Жмите Enter для создания нового пункта. Чтобы создать горячую клавишу, в Caption перед ней поставьте символ "&". Чтобы создать подменю, жмите Ctrl+"->".

Отредактируйте меню по образцу:

Для задания обработчиков событий нужно, как обычно, два раза щелкнуть по пункту меню.

Лучше задать понятные имена - Open, Save и Exit. Для TMemo установите свойство Align в alClient, а ScrollBars в ssBoth.

Код должен быть такой:

Code
void __fastcall TForm1::ExitClick(TObject *Sender)
{
  Close();
}
//-----------------------------------------------------------------

void __fastcall TForm1::OpenClick(TObject *Sender)
{
  if (OpenDialog1->Execute())
   Memo1->Lines->LoadFromFile(OpenDialog1->FileName);
}
//-----------------------------------------------------------------

void __fastcall TForm1::SaveClick(TObject *Sender)
{
  if (SaveDialog1->Execute())
   Memo1->Lines->SaveToFile(SaveDialog1->FileName);
}

Тут тоже вроде все понятно - методы LoadFromFile() и SaveToFile() делают, что надо. Кстати, более подробную информацию о классах можно получить в Help'е.

iBlackДата: Среда, 14.10.2009, 21:34 | Сообщение # 4
Main Admin
Сообщений: 51
[ 3 ]
:-)
Шаг 4 - Создание дочерней формы [TForm]

Во многих приложениях существует многооконный интерфейс. Все формы, визуально разрабатываемые в среде, являются потомками класса TForm. Команда для создания новой формы - File->New Form.

В приложении существует главная форма, автоматически создаваемая при запуске и закрывающая приложение при своем закрытии, и дочерние формы, которые могут статически существовать в приложении или динамически создаваться во время работы программы. Эти установки задаются по команде Project->Options на вкладке Forms.

В качестве примера я рассмотрю проект, выдающий по щелчку на кнопке дочернее окно с двумя полями ввода и добавляющий данные в объект TListBox. Формы надо спроектировать так:

Для кнопок Ok и Cancel значение ModalResult нужно установить в mrOk и mrCancel соответственно.

Вот код:

Code
#include "unit2.h"
void __fastcall TForm1::EnterClick(TObject *Sender)
{
  Form2=new TForm2(this);
  if (Form2->ShowModal()==mrCancel) return;
  ListBox1->Items->Add(Form2->Edit1->Text+" - "+Form2->Edit2->Text);
}
//----------------------------------------------------------

void __fastcall TForm1::DeleteClick(TObject *Sender)
{
  ListBox1->Items->Delete(ListBox1->ItemIndex);
}

Вот и все! Этого кусочка текста хватает, чтобы отобразить две формы записной книжки. Честно говоря, в OWL пришлось бы написать побольше. Главное, не забывать включать заголовочный файл другой формы, и все ее компоненты будут как на ладони.

iBlackДата: Среда, 14.10.2009, 21:58 | Сообщение # 5
Main Admin
Сообщений: 51
[ 3 ]
:-)
Шаг 5 - Хранилище объектов [MDIEdit]

В C++ Builder есть такая замечательная вещь, как "Хранилище объектов". Там полно всякой всячины - заготовки форм, диалогов, типы файлов, даже целые проекты попадаются :). Хранилище вызывается по команде File->New и выглядит так:

На нескольких вкладках расположены довольно полезные заготовки. Мы разберем заготовку MDI Application с вкладки Projects.

С помощью нее можно создать приложение в стиле MDI - многооконном интерфейсе. Несмотря на сложное название, это всего лишь интерфейс всевозможных редакторов. Самый распространенный пример - MS Word. Одно окно главное, другие - окна документа.

Запустите мастер двойным щелчком. Он лаконично спросит про местоположение проекта и вам останется только нажать кнопку Finish.

Итак, довольно приличная заготовка - меню, панель статуса, панель кнопок. Но текстовые редакторы что-то мне уже надоели. Лучше сделаем мультимедиа-проигрыватель. Для этого добавьте компонент TToolBar с вкладки Win32, а поверх него TMediaPlayer с вкладки System. Еще через View->Forms нужно открыть MDIChild и стереть с него компонент Memo1, совершенно нам не нужный.

Выглядеть все это должно так:

В код нужно внести такие изменения, чтобы MediaPlayer открывался:

Code
void __fastcall TMainForm::CreateMDIChild(String Name)
{
  TMDIChild *Child;

  //--- create a new MDI child window ----
  if (FileExists (Name)) {
   MediaPlayer1->FileName=Name;
   MediaPlayer1->Open();
   MediaPlayer1->Play();
  };
}

В принципе тут все зависит от CreateMDIChild, правда, форму Child вообще можно было выкинуть, потому что она не используется, но в следующем шаге я код добавлю, чтобы использовалось.

А так - чистый интерфейс MDI, все в ажуре :). Вы этот проект не выкидывайте, этот проект еще будет жить и развиваться.

iBlackДата: Среда, 14.10.2009, 22:10 | Сообщение # 6
Main Admin
Сообщений: 51
[ 3 ]
:-)
Шаг 6 - Апгрейдим MDI проект [TImage]

Честно говоря, предыдущий проект немного халтура, поскольку весь код, касающийся родного MDI, я выкинул. Но ничего. Сейчас мы добавим такую полезную функцию, как просмотр стандартных файлов изображений и текстовых файлов.

Добавьте к форме MDIChild компонент TImage с вкладки Additional и компонент TMemo с Standard, так беспощадно выкинутый в предыдущем шаге. Для обоих параметр Align задаем как alClient, а для TImage еще и свойство Stretch, как true. Логику сделаем такую: если формат *.avi, *.mid, *.wav, то открыть в MediaPlayer'е, если *.bmp, *.ico, *.wmf, то в изображении, а если ни то ни се - то в TMemo.

Естественно, для этого надо модифицировать код:

Code
void __fastcall TMainForm::CreateMDIChild(String Name,bool img)
{
  TMDIChild *Child;

  //--- create a new MDI child window ----
  Child = new TMDIChild(Application);
  Child->Caption = Name;
  if (FileExists (Name)) {
   if (img){
    Child->Image1->Picture->LoadFromFile(Name);
    Child->Image1->Visible=true;
    Child->Memo1->Visible=false;
   } else {
    Child->Memo1->Lines->LoadFromFile(Name);
    Child->Memo1->Visible=true;
    Child->Image1->Visible=false;
   };
  };
}

void __fastcall TMainForm::FileOpen1Execute(TObject *Sender)
{
  if (OpenDialog->Execute())
   switch (OpenDialog->FilterIndex)
   {
    case 2: CreateMDIChild(OpenDialog->FileName,true); break;
    case 3:
     MediaPlayer1->FileName=OpenDialog->FileName;
     MediaPlayer1->Open();
     MediaPlayer1->Play();
     break;
    default: CreateMDIChild(OpenDialog->FileName,false);
   }
}

Это в main.cpp, а еще в main.h нужно изменить объявление CreateMDIChild на:
Code
void __fastcall TMainForm::CreateMDIChild(String Name,bool img);

...и поменять реализацию функции:
Code
void __fastcall TMainForm::FileNew1Execute(TObject *Sender)
{
  CreateMDIChild("NONAME" + IntToStr(MDIChildCount + 1),false);
}

Теперь разъяснения и немного теории. MDI окно - чаще всего окно документа в редакторе или вьювере. Кто обратил внимание, такое окно нельзя вытащить за пределы MDI формы и при распахивании оно как бы включает в себя главное меню. В принципе, это основное, что нужно знать. Еще есть некоторые функции WinAPI, которые позволяют всякие фокусы с окном выделывать, но мы еще до них не дошли. Как эпилог теории "MDI - это удобно!".

Для создания такого окна мастер вставил функцию CreateMDIChild(), которую мы и терзаем уже второй раз. В OpenDialog'е по выбранному фильтру (здесь слабый момент) определяется тип файла и, в качестве второго параметра для CreateMDIChild(), передается img=true или false. Внутри это соответственно учитывается, и в зависимости от ситуации, свойством Visible скрывается один компонент и показывается другой.

Вот вроде и все. В качестве приза привожу скриншот программы с открытыми окнами:

iBlackДата: Среда, 14.10.2009, 22:15 | Сообщение # 7
Main Admin
Сообщений: 51
[ 3 ]
:-)
Шаг 7 - Прием файлов Drag'n'Drop [DragAcceptFiles, DragQueryFile]

Хорошо известный и уже довольно поднадоевший прием открытия файлов - через перетаскивание в целевое окно. Для приложения неплохо бы его иметь. В общем, здесь главное делают функции WinAPI под названием DragAcceptFiles() и DragQueryFile(). Функция DragAcceptFiles() сообщает Windows, что окно готово к приему файлов. Его (обращение к функции) обычно засовывают в самый старт программы - в FormCreate(). Впрочем, всяк ищет место по-разному. А DragQueryFile() выдает информацию о поднятых (то есть кинутых) файлах.

Вообще-то здесь есть один узкий момент - чтобы можно было отреагировать на это событие Windows придется сделать таблицу откликов MESSAGE_MAP в стиле Visual C++ или OWL. Но выхода другого нет. Опять немного теории: таблица откликов включается в описание класса. При возникновении какого-либо события, Windows передает управление процедуре-обработчику события. В принципе, для тех, кто программировал под DOS, это должно напоминать таблицу векторов прерываний для BIOS. В таблице указывается имя процедуры, тип события, которое она обрабатывает и тип сообщения, передаваемое обработчику. Общий синтаксис таблицы будет подробно рассмотрен в следующих шагах, а именно для этого примера нужно сделать вот что.

В секцию private класса TMainForm нужно вставить описание функции-обработчика, функции-диспетчера и таблицу откликов:

Code
private:
  void __fastcall CreateMDIChild(const String Name,bool img);
  void __fastcall WmDropFiles(TWMDropFiles& Message);
  void __fastcall ReadFile(AnsiString FileName);
public:
  virtual __fastcall TMainForm(TComponent *Owner);
  BEGIN_MESSAGE_MAP
   MESSAGE_HANDLER(WM_DROPFILES,TWMDropFiles,WmDropFiles)
  END_MESSAGE_MAP(TForm);

Диспетчер ReadFile() нужен, чтобы два раза код не писать. Вообще-то вид у него громоздкий, зато от фильтра диалога не зависит. Вот изменения в главном файле:
Code
#include <dir.h>

__fastcall TMainForm::TMainForm(TComponent *Owner)
  : TForm(Owner)
{
  DragAcceptFiles(Handle,true);
}

void __fastcall TMainForm::WmDropFiles(TWMDropFiles& Message)
{
  HDROP drop_handle=(HDROP)Message.Drop;
  char fName[MAXPATH];
  int filenum=DragQueryFile(drop_handle,-1,NULL,NULL);
  for (int i=0;i<filenum;i++)
  {
   DragQueryFile(drop_handle,i,fName,MAXPATH);
   ReadFile(fName);
  };
  DragFinish(drop_handle);
};

void __fastcall TMainForm::ReadFile(AnsiString FileName)
{
  String str=ExtractFileExt(FileName);
  if((str==".avi")||(str==".wav")||(str==".mid")||(str==".mov"))
  {
   MediaPlayer1->FileName=FileName;
   MediaPlayer1->Open();
   MediaPlayer1->Play();
  }
  else
  if((str==".bmp")||(str==".ico")||(str==".wmf"))
   CreateMDIChild(FileName,true);
  else
   CreateMDIChild(FileName,false);
};

void __fastcall TMainForm::FileOpen1Execute(TObject *Sender)
{
  if (OpenDialog->Execute())
   ReadFile(OpenDialog->FileName);
}

Тут, по-моему, легко разобраться. Обратите внимание, как сократился код FileOpen1Execute() - вот что создатель языка Алгол называл структурным программированием! Функцию DragAcceptFiles() я поместил в объектный конструктор, что допустимо, хотя все-таки лучше использовать FormCreate() (если забыли, для автосоздания этого метода достаточно два раза щелкнуть кнопкой по форме). Параметр Handle - свойство, отражающее "window handle", как же это по-русски будет, что-то вроде объектного указателя на windowed control. Второй параметр - разрешить или запретить бросание файлов. Еще о DragQueryFile(). Message.Drop - это handle для параметров, переданных Windows. Он указывается в качестве первого аргумента. Если второй параметр unsigned int равен 0xFFFFFFFF или -1 (что проще записать), то функция возвращает число файлов, иначе имя соответствующее номеру файла. В первом случае два вторых параметра NULL, во втором строка как char str[n] и размер массива (unsigned int). MAXPATH - наибольший размер имени файла, определен в файле . DragFinish() - освобождает ресурсы.

Функция ExtractFileExt() возвращает расширение файла из пути к файлу (легче сделать, чем сказать). Например, для "C:\Мои документы\super.jpg" результат будет ".jpg".

Вот вроде бы и все. Как-нибудь надо будет еще рассмотреть String [AnsiString], но я Шаг с ним случайно стер, так что в следующий раз.

iBlackДата: Среда, 14.10.2009, 22:24 | Сообщение # 8
Main Admin
Сообщений: 51
[ 3 ]
:-)
Шаг 8 - Консоль [спецификаторы printf, функции getc, getchar]

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

Консоль в C++Builder сильно напоминает юниксовую, тот же stdin, stdout и все в том же духе. При запуске программа как консоль выводит обыкновенное окно "Сеанс МС-ДОС", с интутитивным интерфейсом :). Чтобы создать консольное приложение, нужно выбрать команду File->New, и в списке создаваемых объектов выбрать Console Wizard. После этого выйдет такое диалоговое окно:

Все флажки обозначают то, что написано. Можно выбрать язык исходных кодов, разрешить или запретить использование VCL. При снятом флажке VCL можно выбрать или снять флажок multi-threaded, то есть многопоточный. В моем примере я обойдусь одним потоком. Флажок Console Application дает возможность выбрать, исполняется прграмма как консольное окно или как Win32. Все различие в том, что будет прописана функция WinMain или просто Main.

Жмите OK и смотрите жиденький код в Редакторе. После Win32 Application как-то не впечатляет. Но тем не менее. В примере будет решена задача отображения в ответ на ввод символа его ASCII кода. При нажатии клавиши ESC, если не ошибаюсь, код #27, выход из программы. Обычный бесконечный цикл, стандарт для ДОС-программ. Текст должен быть такой:

Code
#include <stdio.h>
#pragma hdrstop

#pragma argsused
int main(int argc, char* argv[])
{
  char c=getc(stdin);
  while(c!=27)
  {
   if (c!=10)
    printf("\r\n%i",c);
   c=getc(stdin);
  };
  getchar();
  return 0;
}

В первой строке подключается заголовочный файл, аналогичный . Он отвечает за ввод-вывод. Одна из него - функция getc() возвращает символ из стандартного ввода stdin. Потом идет цикл. Коды 27 и 10 - это ESC и ENTER. Функция printf() форматирует строку, подставляя вместо спецификаторов преобразованные значения. Спецификаторы для printf, печатаемые после "%":

* Числа:
* d - целое без знака
* i - целое без знака
* u - целое со знаком
* o - восьмеричное со знаком
* x - шестнадцатеричное в ниэнем регистре
* X - шестнадцатеричное в верхнем регистре
* f - плавающее как [-]dddd.dddd
* e - плавающее с экспонентой
* g - два предыдущих в зависимости от значения
* E,G - два предыдущих с экспонентой в верхнем регистре
* Символы:
* c - одиночный символ
* s - NULL-terminated строка
* Указатели:
* n - что-то вроде указателя на количество выдаваемых символов
* p - печатает указатель в формате операционной системы сегмент:смещение.

Я привожу спецификаторы так подробно, потому что они встретятся далее в строках типа AnsiString и прочих.

Последняя функция getchar() вводит один символ с клавиатуры, просто чтобы окно сразу не закрылось.

Напоследок. Помните, что на этой древности многие программисты писали хорошие и очень хорошие программы! smile

iBlackДата: Среда, 14.10.2009, 22:30 | Сообщение # 9
Main Admin
Сообщений: 51
[ 3 ]
:-)
Шаг 9 - Строковые классы [AnsiString, char*, WideString]

Я думаю, что в любой программе подобные штуки нужны. Вообще на строки C++ Builder богат - тут и собственные массивы, и свистнутый у Delphi AnsiString, и UNICODE WideString, и даже из STL есть класс. Так что есть из чего выбрать. Но я перечислю по порядку.

AnsiString

Также называемый просто String (для удобства записи определен через typedef). Основной строковый класс VCL. Используется почти везде, где нужно представить текст. На мой взгляд, самый удобный класс - много хороших методов, хорошая совместимость, объектный конструктор переваривает типы от char* до double. Как я сказал, методов много, даже чересчур. Перечислю главные:

* с_str() - возвращает указатель на массив NULL-terminated string, содержащий ту же информацию, что и исходная строка. Используется для WinAPI обращений.
* data() - то же самое, что и предыдущий, только при пустой строке возвращает не "", а NULL. Иногда это бывает удобно.
* CurrToStr(), CurrToStrF() - перевод типа TCurrency в строку.
* FloatToStrF() - форматированный перевод из плавающего числа в строку.
* IntToHex() - перевод шестнадцатеричного числа. Второй параметр - минимальное число цифр.
* ToInt(), ToDouble(), ToIntDef() - возвращает соответственно int, double, default int.
* Delete(), SubString() - соответственно удаление символов и копирование подстроки.
* Pos() - позиция строки-аргумента, начиная с 1.
* Length() - длина строки.
* printf(), sprintf() - форматирование строки по таким же спецификаторам, как в предыдущем шаге. Первая заменяет значение строки форматированным, вторая его присоединяет.
* operator[] - один из операторных методов. Возвращает соответствующий char, как в массиве, начиная с 1.

Еще эти строки сравниваются по стандарту ANSI через операторы сравнения.

char[]

Старые добрые символьные массивы, состоящие из символов и ограниченные последним значением, равным NULL. Поэтому их еще называют NULL-terminated string. Писать долго, зато очень звучно :). Используются в основном при обращении к WinAPI функциям. При желании более подробную информацию можно посмотреть в Help'е, а я приведу функции. Первый аргумент здесь везде основная строка:

* strlen() - длина строки.
* sprintf() - аналогичное printf() форматирование.
* strcpy() - копирование одной строки в другую.
* strstr() - аналогично Pos().
* strcmp(), strcmpi() - сравнение двух строк. Во втором случае без учета регистра.

WideString

Этот класс также взят из Delphi. Основным его отличием от AnsiString является хранение массива расширенных символов, называемых wide characters, тип wchar_t*. Поэтому он в основном используется в COM приложениях или при обращении к OLE интегрированным объектам. Приводить его методы я не буду, поскольку они очень сильно похожи на соответствующие в AnsiString. Наиболее оригинальным является метод c_bstr(), возвращающий, по аналогии с c_str(), указатель на массив из wchar_t*. Он используется без аргументов.

Надо отметить, что существуют еще некоторые строчные классы C++, которые я не рассмотрел. Например: string, basic_string, SmallString. Я все же скажу насчет первых двух. Класс string я считаю несколько устаревшим на фоне AnsiString. Класс basic_string является классом STL.

На сегодня все, по-моему, много разобрали.

iBlackДата: Среда, 14.10.2009, 22:35 | Сообщение # 10
Main Admin
Сообщений: 51
[ 3 ]
:-)
Шаг 10 - Списочные классы [TList, оператор dynamic_cast]

Списочные классы C++ Builder хранят указатели типа void*, которые могут адресовать любые объекты. Для того, чтобы воспользоваться таким указателем, необходимо выполнить преобразование типа. Преобразования можно выполнить двумя способами - статическим в стиле обычного C++ и динамическим (расширенным ANSI). Последний предпочтительнее по ряду причин. Почему станет понятно из примеров:

Code
void __fastcall TForm1::Edit1Change(TObject *Sender)
{
  TEdit* tmpedit=(TEdit*)Sender;
  Label1->Caption=tmpedit->Text;
}

Данный код выполняет статическое преобразование из TObject к TEdit. Этот обработчик можно назначить сразу нескольким компонентам TEdit, но только TEdit! При попытке преобразования другого класса программа выдаст исключение на преобразование типов. На практике бывает часто необходимо выполнить преобразование из неизвестного класса. Конечно, можно поставить ловушки - инструкции try...catch, но проще воспользоваться динамическим преобразованием - оператором dynamic_cast. Вот пример его использования, аналогичный предыдущему:
Code
void __fastcall TForm1::Edit1Change(TObject *Sender)
{
  TEdit* tmpedit=dynamic_cast<TEdit*>(Sender);
  if (tmpedit==NULL) return;
  Label1->Caption=tmpedit->Text;
}

В угловых скобках указывается целевой класс, а в круглых - аргумент преобразования. При невозможности выполнить преобразование возвращается NULL. Исключение при этом не возникает.

После этого теоретического введения можно приступить к рассмотрению одного из представителей списочных классов VCL - класса TList. Вот его свойства и методы:

* Capacity() - определяет емкость контейнера. Предпочтительнее заранее задавать емкость, чтобы каждый раз при вызове метода Add() не перераспределять память.
* Count() - количество элементов.
* Items[] - индексированный массив указателей на объекты списка. Нумерация с 0.
* Add() - добавляет указатель-аргумент к списку.
* Delete() - удаляет элемент по номеру.
* Pack() - удаляет все указатели, равные NULL.
* Move() - перемещение указателя.
* Exchange() - обмен местами двух указателей.

Опять-таки приведу пример:

Code
TList* list;
void __fastcall Button1Click(TObject* Sender)
{
  const count=10;
  list=new TList;
  list->Capacity=count;
  for (int i=0;i<count;i++)
  {
   TImage* img=new TImage(this);
   img->Picture->LoadFromFile(IntToStr(i)+".bmp");
   list->Add(img);
  }
}

void __fastcall Button2Click(TObject* Sender)  
{
  Image1=dynamic_cast<TImage*>(list->Items[Edit1->Text->ToIntDef(0)]);
}

Этот пример создает список изображений и загружает их из файлов "0.bmp"..."9.bmp". Здесь показаны основные манипуляции.

В следующем шаге мы рассмотрим список строк TStringList.

iBlackДата: Четверг, 15.10.2009, 16:43 | Сообщение # 11
Main Admin
Сообщений: 51
[ 3 ]
:-)
Шаг 11 - Класс списка строк TStringList и его связь с INI-файлами [TStringList, TIniFile]

Класс TStringList является потомком класса TStrings, используемого в некоторых компонентах VCL, оперирующих текстом. Однако для использования вне объектов класс TStrings не подходит, поскольку не имеет собственного конструктора. Для этих целей используется TStringList, расширяющий функциональность предка.

Вот пример кода, оперирующего с TStringList:

Code
void OperateStrings()
{
TStringList* slist=new TStringList;
slist->LoadFromFile("mystrings.txt") ;   //загружает строки из файла (разделение по CR LF)
slist->Sort();  //сортирует строки
    if(slist->IndexOf("Gonna return from here")==-1)
     return;  //метод, возвращающий индекс искомой строки, начиная с 0
slist->Add("Walked through first");  //добавляет строку в конец списка
slist->AddObject(Edit1->Text,Edit1);   //добавляет строку и указатель в список
((TEdit*)slist->Objects[0])->Text=slist->Strings[0];  //соответственно по индексу указатели и строки
slist->SaveToFile("mynewstrings.txt");  //сохраняет список в файл
slist->Clear();  //очищает список
delete slist;
}

Остальные методы довольно типичны. Однако для этого класса есть свойства, позволяющие эффективно использовать этот класс для манипулирования ini-файлами - файлами программных настроек. Это Values и Names. Они используются, если список состоит из пар вида Name=Value. Первое возвращает значение-строку по параметру строке, второй - Name по индексу (естественно, от 0). В предкомпиляционном файле inifiles.hpp (включаемом через #include) описан класс TIniFile. Один из его методов - ReadSectionValues, переписывает строки из выбранной секции в заданный объект TStringList. В принципе, можно напрямую пользоваться TIniFile, но так короче и эффективней с точки зрения скорости.

Вот код, считывающий записанные в файле prog.ini настройки размера окна и приводящий их в исполнение:

Code
void __fastcall TForm1::LoadClick(TObject *Sender)
{
TIniFile* file=new TIniFile("prog.ini");
TStringList* lst=new TStringList;
file->ReadSectionValues("Size",lst);
Top=StrToInt(lst->Values["Top"]);
Left=StrToInt(lst->Values["Left"]);
Width=StrToInt(lst->Values["Width"]);
Height=StrToInt(lst->Values["Height"]);
delete lst,file;
}
//---------------------------------------------------------------------------

void __fastcall TForm1::SaveClick(TObject *Sender)
{
TIniFile* file=new TIniFile("prog.ini");
file->WriteString("Size","Top",Top);
file->WriteString("Size","Left",Left);
file->WriteString("Size","Height",Height);
file->WriteString("Size","Width",Width);
delete file;
}

В принципе это все, что умеет TIniFile. Еще несколько методов:

* TIniFile - конструктор с параметром - именем ини-файла. Если файл не существует, то создается. По умолчанию смотрится Windows-директория
* ReadSection - считывает имена всех параметров заданной секции
* ReadSections - считывает имена секций данного ини-файла
* EraseSection - удаляет секцию. Логично wink
* SectionExists, ValueExists - соответственно существует ли секция или параметр
* ReadString, ReadBool, ReadDate, ReadDateTime, ReadFloat, ReadInteger, ReadTime - считывание соответственных значений из указанной секции
* WriteString, WriteBool, WriteDate, WriteDateTime, WriteFloat, WriteInteger, WriteTime - то же самое наоборот

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

Классы TStack и TObjectList

Эти классы не являются чем-то особенным, но на них стоит обратить внимание. Класс TObjectList отличается от TList тем, что хранит не void указатели, а TObject* указатели. Впрочем, преобразование типа в большинстве практических случаев производить все равно придется. Напомню, что TObject является прародителем всех объектных классов VCL. Еще одной интересной особенностью TObjectList является свойство OwnsObject, которое при true значении дает возможность объекту TObjectList контролировать выделение памяти для индексированных объектов - при удалении объекта из списка или при удалении самого списка автоматически освобождается память, отведенная под указанные объекты. Большинство остальных методов унаследованы от TList, я их рассматривать не буду.

TStack - заготовка для стека. Я говорю заготовка, потому что этот класс содержит минимум полезных методов. Свойств у него вообще нет. Методы Peek, Pop, Push соответственно считывают элемент, не удаляя, считывают, удаляя, и записывают в вверх. Классика.

В следующем шаге будет рассмотрен реестр и способы взаимодействия с ним.

iBlackДата: Четверг, 15.10.2009, 18:18 | Сообщение # 12
Main Admin
Сообщений: 51
[ 3 ]
:-)
Шаг 12 - Реестр Windows (Обзор)

Всем знаком реестр Windows. Этакая общая база для операционки и ее приложений. В принципе большинство программистов копались с реестром, у некоторых нормально обошлось, у других не очень. Но это неважно. Главное, всегда делать резервную копию. Вот о них мы и поговорим.

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

В левой части иерархическая структура реестра - разделы, подразделы. Как видно, он имеет иерархическую структуру. В принципе это похоже на окно проводника. В левой части окна видны параметры. У каждого раздела есть параметр по имени "По умолчанию" или в английской версии "Default". Я, когда в первый раз это услышал, удивился - как же программы смогут считывать его значение, если в разноязычных версиях у него разные имена? Однако выяснилось, что это регедит как бы облегчает пользователям жизнь - настоящее имя параметра "". На самом деле интернационально ;).

В реестре есть шесть главных разделов - HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, HKEY_LOACAL_MACHINE, HKEY_USERS, HKEY_CURRENT_CONFIG, HKEY_DYN_DATA. Я их юуду называть сокращенно, например HKLM означает HKEY_LOCAL_MACHINE. HKCR содержит информацию в основном о файлах, документах OLE и прочем. В нем хранятся такие полезные вещи, как информация о файле по расширению - иконка по умолчанию, контекстное меню, команда выполнения по двойному щелчку.

HKCU хранит информацию о текущем пользователе. Если не ошибаюсь, этот раздел строится динамически по загрузке Windows. В нем, как и в HKLM и HKU, есть раздел Software/CLASSES, содержащий практически ту же информацию, что и HKCR. Раздел HKLM, как и положено по названию, хранит информацию об установленных программах и его настройках.

HKU - информация, относящаяся ко всем пользователям. Несмотря на, казалось бы, ответственную функцию, в его раздел .Default обычно кидают программы всякую дрянь, которую в другие места бросить стыдно.

HKCC содержит, как понятно, информацию и текущей конфигурации. Правда, у обычных пользователей эта конфигурация обычно одна, но неважно. Вообще в этом разделе ничего интересного практически нет, поскольку если с другими разделами копаться более-менее безопасно, то здесь я делать это не рекомендую.

HKDD - раздел, который строится динамически по мере загрузки Windows. В NT 2000 через него можно было что-то поменять, а в 9x в нем хранятся в основном PnP настройки и все в таком роде. Этот раздел частично создается из других.

В общем-то это и есть иерархия знаменитого реестра. В C++Builder довольно унифицированная система управления реестром, через специальный класс TRegistry. Работа с реестром в основном заключается в считывании определенных параметров и их записи и создания и удаления разделов. Этот класс все это позволяет. Нужно сказать, что значения параметров в реестре могут быть строкового, двоичного и типа double word. Последний эквивалентен int.

Класс TRegistry будет подробно рассмотрен в следующем Шаге.

iBlackДата: Четверг, 15.10.2009, 18:20 | Сообщение # 13
Main Admin
Сообщений: 51
[ 3 ]
:-)
Шаг 13 - TRegistry

Вот мы и добрались до этого класса. Если читали предыдущие Шаги, то помните класс TIniFile. В общем, класс TRegistry на него похож, довольно сильно.

Чтобы легче было разбираться с Help'om, скажу, что понятие Key означает не параметр, как можно было подумать, а раздел. Каждый раздел является подразделом главного раздела, которые мы рассмотрели в предыдущем Шаге. Чтобы понятней было, скажу, что иерархия напоминает файловую систему, где HKEY_* - диски, а разделы - директории (или папки, кому как удобней). Как и в файловой системе, возможна относительная адресация и существует корневая - "\".

А вот свойства и методы:

* Access - доступ к реестру. По умолчанию KEY_ALL_ACCESS, некоторые другие значения говорят сами за себя - KEY_READ, KEY_WRITE, KEY_CREATE_SUB_KEY. Некоторые же другие, по-моему, излишни. Кстати, доступ как параметр можно указывать при вызове конструктора.
* RootKey - Корневой или метараздел. Ну это все HKEY_*.
* CurrentKey - только для чтения. Естественно, что текущий раздел.
* CreateKey - создать раздел. Учтите, что только раздел, без параметров.
* KeyExists - существует ли раздел
* OpenKey - открыть раздел. Второй параметр bool разрешает или запрещает создавать раздел.
* SaveKey, LoadKey - соответственно сохранить или загрузить раздел с параметрами из .reg файла.

Методы чтения и записи абсолютно идентичны таковым из класса TIniFile, так что я думаю, Вы с ними разберетесь.

В следующем шаге будет пример использования TRegistry.

iBlackДата: Четверг, 15.10.2009, 18:29 | Сообщение # 14
Main Admin
Сообщений: 51
[ 3 ]
:-)
Шаг 14 - Извлечение иконки по расширению файла и ее загрузка из DLL или EXE

Наверняка многие задумывались, как это сделать, на собственном опыте знаю. Это на самом деле не так сложно. В разделе Software/CLASSES раздела HKLM хранится информация по расширениям для всех обще используемых файлов. Информация записывается как раздел с именем "."+расширение файла. Если не верите, сами можете в regedit'е посмотреть. Далее, параметр "" содержит строку, пересылающую нас на конкретный тип файла, тоже являющимся разделом там же. Именно этот раздел хранит информацию о типе файла.

"По умолчанию" в разделе прописан тип строкой, например "WinZip file". В подразделе DefaultIcon указан в том же параметре путь к иконке с файлом. Тут является еще одна проблема - иконки чаще всего хранятся в EXE или DLL файлах, а стандартными способами C++ Builder ее оттуда не выудишь. Но с этим мы справимся. Еще есть раздел shell, тоже очень интересный, но пока он нам не нужен. Займемся иконками.

Нужно создать новое приложение и положить на него компоненты TEdit , TImage , TButton и TLabel . Приблизительно форма должна выглядеть так:

Свойство Center объекта Image1 лучше всего установить в true, тогда изображения автоматически будет центрироваться. Кодовый листинг будет такой:

Code
#include <registry.hpp>
void __fastcall TForm1::Button1Click(TObject *Sender)
{
String s=Edit1->Text;
    if(s[1]!='.')s="."+s;
TRegistry* reg=new TRegistry;
reg->RootKey=HKEY_LOCAL_MACHINE;
reg->OpenKey("Software\\CLASSES",false);
    if(!reg->OpenKey(s,false))return;
bool open=false;
s=reg->ReadString("");
open=reg->OpenKey("\\Software\\CLASSES\\"+s+"\\DefaultIcon",false);
s=reg->ReadString("");
    if(s=="%1")return;
int pos=s.LastDelimiter(",");
int index=s.SubString(pos+1,s.Length()-pos).ToInt();
s.Delete(pos,s.Length()-pos+1);
Image1->Picture->Icon->Handle=ExtractIcon(HInstance,s.c_str(),index);
}

По-моему, по количеству появлений здесь главную роль играет строка s ;) . Листинг вроде понятный. При неудаче открытия раздела OpenKey возвращает false. Единственное - для файлов ICO и EXE иконка задается внутри самого файла по индексу, то есть по умолчанию может не быть. Тогда параметр DefaultIcon содержит значение "%1". Естественно, что имени конкретного файла мы не знаем, для этого я возврат и вставил. Далее вычисляется путь к DLL или EXE и индекс иконки. Они идут через запятую, например "C\Мои документы\prog.exe, 1". Обратите внимание, что бэкслэш "\" пишется как "\\", потому что это ESC-комбинации.

В последней строчке вызывается функция WinAPI ExtractIcon(). Первым параметром у нее идет HInstance, переменная приложения, затем NULL-term строка и индекс иконки. Она возвращает Handle иконки. Свойство Picture класса TImage имеет вложенное свойство TIcon, содержащее ICO файл. Его-то Handle мы и нашли этой функцией.

Вот и все на этот раз. Это заготовка для создания файлового менеджера, как мне кажется, правда, очень уж базовая ;) .

iBlackДата: Четверг, 15.10.2009, 18:33 | Сообщение # 15
Main Admin
Сообщений: 51
[ 3 ]
:-)
Шаг 15 - Обзор WinAPI

Раз уж мы в прошлом шаге воспользовались функцией WinAPI, то нужно немного о WinAPI узнать. WinAPI - это прикладной интерфейс взаимодействия с программой. Он включает в себя стандартизованные классы, возможности связи (messaging) между приложениями, функции OLE и многое другое. Практически, все классы VCL используют WinAPI, например, кнопка TButton произведена от простого класса WinAPI BUTTON. Однако использовать только WinAPI нерационально. Поэтому были созданы библиотеки стандартных компонентов, в частности VCL, MFC, OWL. Наиболее, так скажем, "машинно-независимой" из них является, безусловно, VCL Однако все же иногда возникает нужда обратится к этим функциям, поскольку все библиотеки не полностью инкапсулируют функции Windows.

Как известно, Windows - среда, управляемая событиями. События бывают разных типов - пользовательские (нажатие на клавиатуру, движение мыши), аппаратные (поступление данных от модема, сбой в работе принтера), программные (завершение обработки операции) и прочие. Четкого разделения не существует. Наиболее близкий аналог событий Windows в технике - прерывания процессора. Когда возникают события, операционка перенаправляет их программе, которая может обработать или не обрабатывать их.

С этим связан еще и такой важный момент, как разделение процессорных ресурсов. В отличие от DOS-программ, которые обычно замыкали бесконечный цикл в ожидании нажатий на клавиатуру или чего-то еще, Windows-программы лишь обрабатываеют события, а затем переходят в режим ожидания (Idle), при котором ресурсы не расходуются.

Также нужно объяснить понятие Handle. Я уже довольно много раз им пользовался, применяя различные переводы. Правильнее всего будет сказать, что это оконный дескриптор объекта. Дело в том, что для WinAPI аргументом является не указатель на объект, а его оконный дескриптор. называемый также HWND. В Windows окнами считаются не только окна-формы, но и объекты такие, как кнопка, панель, меню. В иерархии VCL эти классы - производные TWinControl. Кстати, увидеть иерархию можно, нажав в Help'е кнопку Hierarchy сверху.

Надо отдать должное, функции WinAPI очень разнообразны. Кто обращал внимание, справка C++ Builder, появляющаяся по F1, не дает по ним сведений. Для того, чтобы открыть справку, нужно воспользоваться help-файлами MS SDK. В меню Пуск, там же, где и Builder, есть подраздел Help, а в нем MS SDK help files представлены по категориям. Но удобнее, естественнее, вести поиск сразу во всей базе. Для этого неплохо бы создать ярлык на файл "Program Files\Common Files\Borland Shared\MSHelp\win32sdk.hlp", который содержит все разделы справки MS SDK.

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

Основные инструменты WinAPI - это функции и сообщения (messages). Если с первыми более менее понятно, поскольку их не так много, то вторых очень большое количество, причем с первого взгляда они выполняют одинаковые функции. Но тут есть один момент. В названии сообщения первые буквы перед "_" определяют, к какому типу объектов оно может посылаться. Например, WM означает Window Message, то есть сообщение, отправляемое окну-форме. EM - Edit control Message, отправляемые объектам типа TMemo, SBM - Scroll Bar Message, отправляется полосам прокрутки. В принципе в справке это указывается.

Для отправления сообщений используются функции SendMessage и PostMessage. Для приема чаще всего используются таблицы MESSAGE_MAP. И то, и другое будет рассмотрено в следующем Шаге.

Micro-Life Форум » Программирование » C и C++ » Уроки изучения Borland C++ Builder 5
  • Страница 1 из 2
  • 1
  • 2
  • »
Поиск: