20.04.2014 Aufrufe

Функции - eDrive

Функции - eDrive

Функции - eDrive

MEHR ANZEIGEN
WENIGER ANZEIGEN

Sie wollen auch ein ePaper? Erhöhen Sie die Reichweite Ihrer Titel.

YUMPU macht aus Druck-PDFs automatisch weboptimierte ePaper, die Google liebt.

Министерство образования и науки РФ<br />

_________________________________<br />

Санкт-Петербургский государственный<br />

электротехнический университет «ЛЭТИ»<br />

В. М. Водовозов А. К. Пожидаев<br />

Разработка<br />

программ<br />

под Windows<br />

Учебное пособие<br />

Санкт-Петербург<br />

Издательство СПбГЭТУ «ЛЭТИ»<br />

2005


ББК 32.973.26-018<br />

В62<br />

УДК 681.3.016 (018)<br />

Водовозов В. М., Пожидаев А. К. Разработка программ под Windows:<br />

Учеб. пособие. СПб.: Изд-во СПбГЭТУ «ЛЭТИ», 2005. 104 с.<br />

Дано описание языка С++. Представлены основные этапы процедурного,<br />

структурного, модульного и объектно-ориентированного программирования<br />

под Windows. Показаны особенности работы с библиотеками, процедурами,<br />

функциями, файлами, базами данных и справочными системами.<br />

Предназначено для студентов направления 654500 – Электротехника,<br />

электромеханика и электротехнологии, обучающихся по специальностям<br />

180400 – Электропривод и автоматизация промышленных установок и технологических<br />

комплексов, 180500 – Электротехнологические установки и<br />

системы.<br />

Рецензенты: кафедра информационных технологий СПбИДПО;<br />

д-р техн. наук, проф. И. М. Семенов (СПбГПУ).<br />

ISBN 5-7629-0567-5 © В. М. Водовозов, А. К. Пожидаев, 2005


Оглавление<br />

Введение в процедурное программирование.................................................... 5<br />

Редактор кода.........................................................................................................................5<br />

<strong>Функции</strong>..................................................................................................................................5<br />

Выражения .............................................................................................................................8<br />

Классы форм ........................................................................................................................11<br />

Обработка событий..............................................................................................................13<br />

Программирование графики...............................................................................................16<br />

Исключения..........................................................................................................................17<br />

Простые типы данных и операторы...................................................................................19<br />

Тип AnsiString ......................................................................................................................22<br />

Составные операторы........................................................................................ 27<br />

Операторы перехода............................................................................................................27<br />

Условный оператор .............................................................................................................27<br />

Оператор выбора..................................................................................................................29<br />

Оператор цикла....................................................................................................................30<br />

Операторы повторения........................................................................................................31<br />

Программирование диалогов..............................................................................................32<br />

Введение в структурное программирование................................................... 34<br />

Массивы................................................................................................................................34<br />

Перестановки и сортировки................................................................................................36<br />

Списки TStringList...............................................................................................................40<br />

Структуры и объединения ..................................................................................................43<br />

Динамические структуры данных......................................................................................45<br />

Введение в модульное программирование ..................................................... 53<br />

Работа с текстовыми и графическими файлами ...............................................................53<br />

Работа с двоичными файлами ............................................................................................59<br />

Модули DLL и пакеты.........................................................................................................60<br />

Ресурсы.................................................................................................................................63<br />

Приложения баз данных.................................................................................... 63<br />

Технологии создания приложений баз данных ................................................................63<br />

Архитектуры приложений баз данных..............................................................................65<br />

Подключение к базам данных ............................................................................................66<br />

Доступ к записям .................................................................................................................68<br />

Объектно-ориентированное программирование ............................................ 72<br />

Инкапсуляция.......................................................................................................................72<br />

Конструкторы и деструкторы.............................................................................................75<br />

Дружественные функции и классы....................................................................................80<br />

Полиморфизм.......................................................................................................................82<br />

Шаблоны...............................................................................................................................85<br />

Стандартная библиотека шаблонов STL...........................................................................89<br />

Наследование .......................................................................................................................94<br />

Виртуальные функции и абстрактные классы..................................................................98<br />

Список литературы..........................................................................................103


5<br />

Введение в процедурное программирование<br />

Редактор кода<br />

«Работа высшего, что есть в человеке – разума<br />

– сводится к непрерывному ограничению<br />

бесконечности, к разделению бесконечного на<br />

удобные, легко перевариваемые части»<br />

Е.Замятин<br />

Файлы программ обрабатываются на вкладках окна редактора кода, запускаемого<br />

через меню View .Units (), View .Toggle Form/Unit<br />

(). Вкладки с текстами открываются в редакторе кода через меню File<br />

.Open. Переход между ними удобно выполнять с помощью навигационных<br />

стрелок в правом верхнем углу окна. В левом поле выделения окна щелчком<br />

можно расставлять и снимать точки прерывания выполнения программы. Переключение<br />

между кодами и файлами заголовков выполняется через контекстные<br />

меню Open Source/Header File () и Open File at Cursor (). Щелчки по именам в текстах программ при нажатой клавише <br />

превращают их в гиперссылки (hotspot), с помощью которых обычно перемещаются<br />

между связанными файлами. Другие возможности системы редактирования<br />

Code Insight приведены в табл. 1:<br />

Таблица 1.<br />

Подсказки в системе редактирования Code Insight<br />

Технология Клавиши Назначение<br />

Code Completion Список членов класса после стрелки -> и точки<br />

Code Parameters Список параметров функции после скобки (<br />

Code Templates Список шаблонов программирования<br />

Tooltip Expression<br />

Evaluation<br />

Подсказка значения переменной под курсором<br />

в окне отладчика<br />

Tooltip Symbol Insight<br />

Подсказка объявления объекта в редакторе<br />

кода<br />

<strong>Функции</strong><br />

Исходный текст программы на языке C++ составляют выражения,<br />

сгруппированные в функции.<br />

<strong>Функции</strong> – это единственный вид процедур, используемый в C++. Будучи<br />

разновидностью процедуры, функция (function) представляет относительно<br />

автономную именованную часть программы, описывающую поведе-


6<br />

ние программных объектов через действия над данными. Программирование<br />

с использованием функций сокращает размеры исходного текста и упрощает<br />

его организацию, хотя «экономия пространства не экономит времени»<br />

[Кнут].<br />

В отличие от простой процедуры, функция обычно имеет возвращаемое<br />

значение (return value), то есть результат своей деятельности в вызывающем<br />

ее выражении.<br />

Каждая функция может многократно вызываться из других функций. В<br />

момент вызова функция получает исходные данные и начинает выполнять<br />

предписанные действия над ними.<br />

Различают встроенные библиотечные и авторские функции. Первые<br />

хранятся в библиотеках C++. Для доступа к библиотечным функциям в начале<br />

текста программы указываются имена соответствующих файлов заголовков:<br />

#include < имяФайлаЗаголовка ><br />

или<br />

#include «имяФайлаЗаголовка»<br />

Файлы заголовков – «это хранилища информации, содержащие описания<br />

констант и объявления функций» [Сван]. Каждый такой файл сообщает компилятору<br />

имена и характеристики функций из других файлов и библиотек,<br />

которые используются в программе. Файлы заголовков главных библиотек<br />

фирмы Borland приведены в табл. 2.<br />

Таблица 2.<br />

Файлы заголовков библиотек фирмы Borland<br />

Файл заголовка<br />

ALLOC.H<br />

BIOS.H<br />

CONIO.H<br />

CTYPE.H<br />

DIR.H<br />

FSTREAM.H<br />

IOMANIP.H<br />

IOSTREAM.H<br />

MATH.H<br />

PROCESS.H<br />

STDIO.H<br />

STRING.H, STDLIB.H<br />

TIME.H<br />

VCL.H<br />

WINDOW.H<br />

Назначение<br />

<strong>Функции</strong> распределения памяти<br />

<strong>Функции</strong> интерфейса с BIOS<br />

<strong>Функции</strong> консольного ввода и вывода<br />

<strong>Функции</strong> классификации символов<br />

<strong>Функции</strong> доступа к файлам<br />

<strong>Функции</strong> ввода и вывода файловых потоков<br />

<strong>Функции</strong>-манипуляторы потоков<br />

<strong>Функции</strong> потокового ввода и вывода<br />

Математические функции<br />

<strong>Функции</strong> управления процессами<br />

<strong>Функции</strong> стандартного ввода и вывода<br />

<strong>Функции</strong> преобразования строк<br />

<strong>Функции</strong> времени и даты<br />

<strong>Функции</strong> библиотек VCL<br />

<strong>Функции</strong> для работы в среде Windows


7<br />

Выражение, начинающееся символом #, называют директивой препроцессора<br />

(preprocessor directive). Если имя файла заголовка в директиве<br />

препроцессора заключено в угловые скобки, компилятор отыскивает его в<br />

специальной папке \INCLUDE. Если же это имя заключено в кавычки, поиск<br />

будет начат из текущей папки.<br />

В текстах программ C++Builder много директив препроцессора, служащих<br />

инструкциями для компилятора. Все они располагаются в начале исходного<br />

текста (табл. 3).<br />

Таблица 3.<br />

Директивы препроцессора<br />

Директива препроцессора<br />

#include <br />

#include «файлЗаголовка»<br />

#define имяМакроса (список<br />

параметров) текстМакроса<br />

#pragma argsused<br />

#pragma hdrstop<br />

#pragma package (smart_init)<br />

#pragma resource "*.dfm"<br />

#ifdef (#if defined)… #endif<br />

#ifndef … #define… #endif<br />

Назначение<br />

Заменить строку содержимым файла заголовка<br />

Создать макрос<br />

Запрет вывода сообщений о неиспользуемых параметрах<br />

Ограничение списка ранее компилированных заголовков<br />

Инициализировать пакеты в порядке их объявления<br />

В качестве файла ресурсов использовать файлы .DFM<br />

Если имя ранее определено, выполнить все до #endif<br />

Если имя еще не определено, выполнить все до #endif<br />

С применением авторских функций связаны три понятия: объявление<br />

функции, определение функции и вызов функции. Каждая функция только<br />

один раз определяется (declare) (описывается) в тексте. При этом ей присваивается<br />

уникальное имя и сообщается набор параметров (parameter), то<br />

есть данных, которые должны поступить к ней прежде, чем подпрограмма<br />

приступит к их обработке. В ряде случаев определению функции предшествует<br />

ее объявление (announcement). Объявления и определения функций могут<br />

выноситься в авторские библиотеки. Каждый программист помещает<br />

нужные ему функции в файлы заголовков и при необходимости обращается к<br />

этим файлам из файлов кодов, получая доступ к созданным ранее функциям.<br />

Такое обращение называется вызовом (call). При вызове функции передаются<br />

аргументы (argument), то есть значения параметров. В процессе компиляции<br />

автоматически создается список всех функций, необходимых обрабатываемому<br />

файлу, а в ходе последующей компоновки устанавливаются необходимые<br />

связи.<br />

Имена функциям, равно как и переменным, константам, определяемым<br />

в программах, даются программистами. Имена чувствительны к регистру,<br />

начинаются с буквы или символа подчеркивания, не содержат пробелов и<br />

иных символов, кроме подчеркивания, цифр и латинских букв, и отличаются<br />

от лексем, служащих именами встроенных функций, операторов, констант и<br />

других элементов языка. Имена функций расширяют набор лексем языка


8<br />

программирования, используемый в данной программе; они включаются в<br />

выражения наряду с лексемами языка.<br />

Обязательной частью программы является единственная главная функция<br />

(main function), обычно WinMain, расположенная в файле кода проекта.<br />

Раздел объявлений и другие функции необязательны. Когда компьютер приступает<br />

к выполнению программы, он начинает ее с первой строки главной<br />

функции, независимо от взаимного расположения функций в программе.<br />

Выражения<br />

Любое объявление является выражением, содержащим сведения об области<br />

определения и области значений данных. Объявления функций называют<br />

прототипами (prototype), т.к. они представляют собой своеобразный<br />

шаблон, образец использования функции:<br />

[Модификатор] [тип<strong>Функции</strong>] имя<strong>Функции</strong><br />

( типПараметра [ имяПараметра [ = Значение ]]...);<br />

Здесь и далее используется метаязык описания языка программирования, в<br />

котором в прямоугольные скобки принято заключать необязательные части<br />

выражений, а многоточием отмечается возможность повторения элементов<br />

синтаксической формулы.<br />

Все выражения в C++, кроме директив препроцессора и комментариев,<br />

завершаются точкой с запятой. Длинные выражения можно разбивать символом<br />

\ – признаком продолжения на следующей строке. В круглых скобках<br />

помещаются сведения о параметрах функции. При этом части из них, расположенной<br />

в конце списка параметров, можно сразу присваивать значения –<br />

это называется инициализацией по умолчанию (default initialization). Тип<br />

функции (function type) характеризует возвращаемое значение, а тип параметра<br />

(parameter type) – множество входных данных, обрабатываемых этой<br />

функцией. Кроме объявления встроенных функций из стандартных библиотек,<br />

находящихся в файлах заголовков, авторские функции, создаваемые программистом,<br />

также могут включаться в файлы заголовков. Но часто их объявляют<br />

и затем определяют прямо в тексте программы. В тех случаях, когда<br />

функция определяется до ее вызова, объявлять функции необязательно. C++<br />

не допускает объявления и определения функций внутри других функций:<br />

Формат определения авторской функции:<br />

Заголовок {<br />

[ объявлениеДанных...]<br />

Выражение…<br />

[ return возвращаемоеЗначение;] }


9<br />

Заголовок (caption) определяемой функции является своего рода копией<br />

ее объявления, но обязательно содержит имена параметров и не завершается<br />

точкой с запятой. Внутри фигурных скобок в определении функции заключается<br />

ее тело (body). Текст программы содержит выражения, состоящие из<br />

переменных, констант, операторов, вызовов функций и заканчивающиеся<br />

точкой с запятой. Фигурными скобками текст может разбиваться на отдельные<br />

блоки.<br />

Обычно каждый блок обрабатывает свои переменные и константы, «не<br />

заботясь» о других данных программы. В этом «правиле ограниченной видимости»<br />

заключается автономность, независимость блоков и функций. Данные,<br />

обрабатываемые внутри блока, называют локальными (local variable),<br />

личными или автоматическими. Память под них выделяется на время работы,<br />

а по завершении работы функции их значения так же автоматически стираются,<br />

освобождая место в памяти переменным других частей программы.<br />

В C++ разрешено совмещать определение коротких функций с их объявлением<br />

с помощью лексемы inline. Благодаря этому механизму, код функции<br />

автоматически встраивается в то место, откуда происходит ее вызов, и<br />

вызов происходит значительно быстрее.<br />

Если требуется сохранять значения локальных переменных между вызовами<br />

функций, их объявления снабжают модификатором static. В момент<br />

объявления статическую переменную можно явно инициализировать. Если<br />

этого не сделать, то при первом вызове ей автоматически присваивается нулевое<br />

значение. При повторных вызовах выражение инициализации игнорируется.<br />

Модификатор register направляет локальную переменную на сохранение<br />

в регистры процессора. А модификатор volatile явно указывает, что локальная<br />

переменная «изменчива», то есть может в любой момент поменять<br />

свое значение.<br />

Кроме локальных данных, в программах существуют глобальные переменные<br />

(global variable) и глобальные константы (global constant), доступные<br />

из разных частей программы. Они размещаются в разделе объявлений файла<br />

и помогают функциям обмениваться информацией, минуя параметры, но порой<br />

снижают надежность программной системы и увеличивают размер памяти,<br />

занимаемой программой в ходе выполнения.<br />

Внешний модификатор extern информирует компилятор о существовании<br />

внешних данных, находящихся в другом файле кода программы. За<br />

внешними и глобальными данными память закрепляется постоянно на все<br />

время работы программы, а при первом вызове они инициализируются нулем.<br />

Каждый вызов функции представляет собой запрос на совершение определенных<br />

действий. Он осуществляется в соответствии с объявленным<br />

прототипом и семантической организацией программы:


10<br />

имя<strong>Функции</strong> ([ списокАргументов ]);<br />

В ответ на вызов копия кода функции передается в вызывающую функцию<br />

для обработки, после чего она удаляется из памяти. Исключением являются<br />

функции, объявленные с модификатором inline, коды которых обрабатываются<br />

непосредственно. При вызове функций параметры, инициализированные<br />

по умолчанию, могут быть опущены в списке передаваемых аргументов.<br />

В любой части программы можно разместить комментарии – тексты,<br />

обрамленные символами /* */, или фрагменты строк, начинающиеся символами<br />

//, не влияющие на ход выполнения программы.<br />

Ниже приведен пример текста файла проекта на языке C++Builder:<br />

#include <br />

#pragma hdrstop<br />

// Заголовочный файл описания классов VCL<br />

// Ограничитель списка прекомпилированных<br />

// заголовочных файлов<br />

// Файлы и имена форм проекта<br />

USEFORM ("bMainForm.cpp", MainForm);<br />

USEFORM ("bDataBase.cpp", DataBase);<br />

USEFORM ("bDiagram.cpp", Diagram);<br />

USEFORM ("bAbout.cpp", About);<br />

WINAPI WinMain (HINSTANCE, HINSTANCE, LPSTR, int)<br />

{ try {<br />

Application->Initialize ();<br />

// Главная<br />

// функция<br />

// Инициализация OLE<br />

// Создание формы<br />

Application->CreateForm (__classid (TMainForm), &MainForm);<br />

Application->Run ();<br />

// Запуск приложения<br />

}<br />

catch (Exception &exception)<br />

{ Application->ShowException (&exception); }<br />

catch (...) {<br />

try { throw Exception (""); }<br />

catch (Exception &exception)<br />

{ Application->ShowException(&exception); }<br />

}<br />

return 0;<br />

}<br />

// Обработка исключений<br />

Здесь директива препроцессора #include подключает к тексту<br />

файл заголовка, ссылающийся на описание классов VCL, а директива<br />

#pragma hdrstop ограничивает список файлов заголовка, доступных для<br />

предварительной компиляции. Директивы USEFORM сообщают о модулях,<br />

используемых в проекте. Выражение Application->Initialize() инициализирует


11<br />

OLE-серверы автоматизации. Каждое выражение Application->CreateForm()<br />

создает отдельную форму, а выражение Application->Run() запускает приложение,<br />

переводя его в состояние ожидания наступления событий. Конструкция<br />

try…catch используется для корректного завершения приложения в случае<br />

возникновения ошибки.<br />

Классы форм<br />

Меню View .Class Explorer открывает окно обозревателя классов. В нем<br />

представлена иерархия классов с указанием типов, член-данных, членфункций<br />

и глобальных переменных, входящих во все файловые модули.<br />

Каждой форме соответствует файл заголовка с определением ее класса<br />

в формате<br />

например:<br />

class имяКласса: public TForm<br />

{ [[ Метка ] обявлениеЧленов...]...};<br />

class TForm1 : public TForm {<br />

__published:<br />

TEdit *Edit1;<br />

TEdit *Edit2;<br />

TButton *Button1;<br />

TLabel *Label1;<br />

void __fastcall Button1Click (TObject *Sender);<br />

private:<br />

public:<br />

__fastcall TForm1 (TComponent* Owner);<br />

};<br />

Закрытые член-данные и член-функции класса должны размещаться под меткой<br />

private: или в начале описания класса без метки, защищенные члены<br />

снабжаются меткой protected:, а открытые члены перечисляются под меткой<br />

public:. Данные, свойства которых представлены в окне Object Inspector, находятся<br />

под меткой __published:. Объекты, порождаемые от закрытых членов<br />

класса, доступны лишь объектам своего класса. Объекты, порождаемые защищенными<br />

членами, открыты объектам своего класса и производных от него<br />

классов. Объекты открытых членов могут использоваться во всех операциях<br />

и взаимодействовать с переменными программы.<br />

Данные в определении класса объявляются со своими типами и именами,<br />

а у член-функций указываются, кроме того, типы данных параметров. В<br />

рассматриваемом примере присутствуют указатели на данные Edit1, Edit2,<br />

Button1, Label1 классов TEdit, TButton, TLabel. Модификатор быстрого вызо-


12<br />

ва __fastcall организует передачу аргументов методам VCL не через стек, а<br />

через процессорные регистры, если это не вещественные числа, не структуры<br />

данных и не функции. Лексема void является указателем на то, что функция<br />

Button1Click не возвращает никаких значений по окончании работы.<br />

Бестиповая функция TForm1, имя которой совпадает с именем класса,<br />

называется конструктором (constructor). Конструкторы (а их может быть несколько)<br />

предназначены для автоматической инициализации данных в ходе<br />

порождения объектов.<br />

Класс формы является производным классом, и в его заголовке через<br />

двоеточие указано имя базового класса TForm с открытым типом доступа<br />

public. Производный класс наследует все члены базового класса, за исключением<br />

конструкторов и деструктора. В производном классе унаследованные<br />

члены могут иногда изменяться, а также добавляются новые члены.<br />

Одновременно с объявлением класса в файле заголовка задается одноименная<br />

с формой глобальная переменная, позволяющая обращаться к ней из<br />

любого файла проекта, например:<br />

extern PACKAGE TForm1 *Form1;<br />

Лексема PACKAGE является именем макроса, в котором содержится код экспорта<br />

из библиотеки пакетов BPL – Borland Package Library.<br />

Члены классов, объявленные в файле заголовка, определяются в файле<br />

кода формы в форматах:<br />

[Модификатор] [Тип] имяКласса :: имяЧлен-данного [= Значение];<br />

[Модификатор][Тип] имяКласса :: имяЧлен-функции (списокПараметров)<br />

телоЧлен-функции<br />

В частности, каждой форме в ее файле кода автоматически прописывается<br />

пустой конструктор, получающий при вызове единственный параметр Owner,<br />

указывающий объекта-владельца, создающего форму. Например:<br />

TForm1 *Form1;<br />

__fastcall TForm1::TForm1(TComponent* Owner): TForm(Owner) {}<br />

При удалении из формы элементов управления их объявления автоматически<br />

удаляются из описания класса формы, хотя объявления членфункций<br />

сохраняются, и при необходимости программисту следует удалить<br />

их вручную. При переименовании объектов переименовываются и все ссылки<br />

на них в файле заголовка и в определениях.


Обработка событий<br />

13<br />

Доступ к функциям форм, обрабатывающим события элементов управления,<br />

может осуществляться через вкладку Events инспектора объектов. Эти<br />

же обработчики можно и непосредственно открывать в окне редактора кода.<br />

По умолчанию C++Builder присваивает обработчику имя, образованное соединением<br />

имени компонента с именем события. Например,<br />

void __fastcall TForm1::Button1Click (TObject *Sender) {}<br />

– это обработчик щелчка – события OnClick кнопки Button1 в форме Form1,<br />

являющийся функцией класса TForm1.<br />

Текст обработчика программист наполняет выражениями, описывающими<br />

поведение формы или элементов управления, изменяющими их характеристики<br />

или посылающими сообщения. Элементы управления, обрабатываемые<br />

этими выражениями, характеризуются свойствами и методами. В обработчике<br />

свойства и методы связываются стрелками (->) с указателями на<br />

элементы управления. Ниже приведены примеры обработчиков трех пунктов<br />

меню и кнопки:<br />

void __fastcall TForm1::menuNewClick (TObject *Sender) {<br />

Edit1->Text = "2003";<br />

Label1->Caption = "Санкт-Петербург";<br />

}<br />

void __fastcall TForm1::menuDatabaseClick (TObject *Sender) {<br />

Form2->Show();<br />

}<br />

void __fastcall TForm1::menuAboutClick (TObject *Sender) {<br />

FormAbout->ShowModal();<br />

}<br />

void __fastcall TForm1::Button1Click (TObject *Sender) {<br />

Application->Terminate();<br />

}<br />

Здесь при выборе пункта меню menuNew свойству Текст поля Edit1 присваивается<br />

значение «2003», а надписи Label1 передается «Санкт-Петербург».<br />

При выборе пункта menuDatabase открывается форма Form2, а при выборе<br />

пункта menuAbout открывается модально форма FormAbout. При нажатии<br />

кнопки Button1 приложение завершает свою работу.


14<br />

В следующем примере показаны обработчики меню открытия и сохранения<br />

файла, обслуживающие элемент RichEdit1 и строку состояния Status-<br />

Bar1:<br />

void __fastcall TForm1::menuOpenClick (TObject *Sender) {<br />

OpenDialog1->Execute()<br />

RichEdit1->Lines->LoadFromFile (OpenDialog1->FileName);<br />

StatusBar1->Panels->Items[0]->Text = OpenDialog1->FileName;<br />

}<br />

void __fastcall TForm1::menuSaveAsClick (TObject *Sender) {<br />

SaveDialog1->Execute()<br />

RichEdit1->Lines->SaveToFile (SaveDialog1->FileName);<br />

AnsiString fn = SaveDialog1->FileName;<br />

StatusBar1->Panels->Items[0]->Text = SaveDialog1->FileName;<br />

}<br />

Передача данных между не модальными формами осуществляется либо<br />

через открытые члены их классов, либо через свойства форм. Например, чтобы<br />

внести в надпись Label1 активной формы-владельца, на которую ссылается<br />

указатель this, значение заголовка формы ModelessForm, можно воспользоваться<br />

следующим выражением:<br />

this->Label1->Caption = ModelessForm->Caption;<br />

Важным достоинством обработчиков является их способность создавать<br />

новые элементы управления в ходе выполнения программы (runtime<br />

mode), а не на стадии проектирования (design time mode). Новые объекты порождаются<br />

оператором new, который выделяет необходимое количество свободной<br />

памяти и вызывает конструктор объекта. Объекты, созданные оператором<br />

new, следует удалять из памяти оператором delete, освобождающим<br />

память.<br />

В частности, формам не обязательно выделять память при запуске приложения.<br />

Это можно делать динамически, по мере необходимости. На стадии<br />

проектирования имена динамических форм надо удалить из списка Autocreate<br />

forms вкладки Forms (меню Project .Options) или просто вычеркнуть<br />

соответствующие строки из WinMain, а в нужном месте программы создавать<br />

их и открывать не модально методом Show или модально методом ShowModal.<br />

В следующем примере в форме MainForm по щелчку кнопки Button1<br />

создаются три формы: не модальная форма ModelessForm, модальная форма<br />

ModalForm, объявленная глобальной переменной в файле заголовка, и модальная<br />

форма LocalForm, не связанная с глобальной переменной:


15<br />

#include <br />

#include "Unit1.h"<br />

#include "Unit2.h"<br />

#include "Unit3.h"<br />

TForm1 *Form1;<br />

__fastcall TForm1::TForm1 (TComponent* Owner): TForm (Owner)<br />

{<br />

ModelessForm->Show();<br />

ModalForm = new TModalForm (this);<br />

ModalForm->ShowModal();<br />

delete ModalForm;<br />

// при наличии<br />

// глобального<br />

// определения<br />

TLocalForm *LocalForm = new TLocalForm (0); // вместо<br />

LocalForm->ShowModal();<br />

// глобального<br />

delete LocalForm;<br />

// определения<br />

ModelessForm->Close();<br />

}<br />

В программе использован аргумент this, указывающий на владельца<br />

TMainForm, и аргумент 0, не связывающий новую форму с конкретным владельцем.<br />

Еще один фрагмент программы посвящен созданию кнопки:<br />

TButton *b = new TButton (this);<br />

b->Parent = this;<br />

b->Name = “NewButton”;<br />

b->Width = 100;<br />

b->Height = 30;<br />

b->Left = 10;<br />

b->Top =10;<br />

b->Caption = “Новая кнопка”;<br />

b->Visible = true;<br />

b->OnClick = MyClick;<br />

void __fastcall TForm1::MyClick (TObject *Sender) {<br />

ShowMessage (“Привет”);<br />

}


Программирование графики<br />

16<br />

Табл. 4 посвящена характерным методам формирования графических<br />

образов по канве.<br />

Таблица 4.<br />

Методы обработки канвы<br />

Метод<br />

Назначение<br />

Arc<br />

Строит дугу внутри заданной области<br />

Chord Строит сектор эллипса по хорде<br />

CopyRect Копирует фрагмент изображения с другой канвы<br />

Draw Создает графический объект с заданными свойствами и координатами<br />

Ellipse Строит эллипс в заданных границах<br />

FillRect Строит прямоугольную область с заданным свойством Brush<br />

FloodFill Заполняет пространство канвы заданным свойством Brush<br />

FrameRect Строит прямоугольник с границами, заданными свойством Brush<br />

LineTo Ведет линию из позиции PenPos в точку с координатами X-1 и Y-1<br />

MoveTo Переносит позицию рисования в точку с координатами X-1 и Y-1<br />

Pie<br />

Строит сектор эллипса внутри заданного прямоугольника<br />

Polygon Строит замкнутый контур из отрезков, проходящих через заданные точки<br />

PolyLine Строит ломаную из отрезков, проходящих через заданные точки<br />

Rectangle Строит прямоугольник через левый верхний и правый нижний углы<br />

RoundRect Строит прямоугольник с закругленными углами<br />

StretchDraw Вписывает изображение в прямоугольник, автоматически масштабируя<br />

TextHeight Возвращает высоту текстовой строки на рисунке<br />

TextOut Формирует строку текста из точки с X и Y, перенося в конец ее курсор<br />

TextWidth Возвращает ширину текстовой строки на рисунке<br />

TextRect Размещает фрагмент строки в заданной области, обрезая излишки<br />

В следующем примере показан обработчик события формы FormPaint,<br />

рисующий Андреевский флаг:<br />

void __fastcall TAboutBox::FormPaint (TObject *Sender) {<br />

Canvas->Brush->Color = clBlue;<br />

Canvas->FillRect (Rect (0,0,ClientWidth, ClientHeight));<br />

Canvas->Pen->Color = clWhite; Canvas->Pen->Width = 20;<br />

Canvas->MoveTo (0,0); Canvas->LineTo (ClientWidth, ClientHeight);<br />

Canvas->MoveTo (0,ClientHeight); Canvas->LineTo (ClientWidth, 0);<br />

}<br />

Аналогичную задачу можно решить в элементе управления PaintBox по событию<br />

PainBoxPaint.<br />

Во втором примере обработчик события, связанного с нажатием кнопки,<br />

формирует диалог для вставки объекта OLE:


void __fastcall TModalForm::Button1Click (TObject *Sender) {<br />

OleContainer1->InsertObjectDialog();<br />

}<br />

17<br />

Третий пример демонстрирует использование компонента Clipboard<br />

для копирования содержимого одного объекта Image в другой:<br />

#include <br />

void __fastcall TForm1::BitBtn1Click (TObject *Sender) {<br />

Image1->Picture->LoadFromFile ("d:/val/scan.bmp");<br />

Clipboard()->Assign (Image1->Picture);<br />

Image2->Picture->Bitmap->Assign (Clipboard ());<br />

}<br />

Компонент Image способен самостоятельно обновляться. Он является<br />

удобным средством ускоренного отображения графики в форме: достаточно<br />

создать скрытый вариант Image и копировать его в канву формы по событию<br />

OnPaint:<br />

void __fastcall TForm1::FormPaint (TObject *Sender) {<br />

this->Canvas->CopyRect (ClientRect, Image1->Canvas, Image1->ClientRect);<br />

}<br />

Исключения<br />

C++Builder поддерживает стандартный механизм обработки исключений<br />

(exception), то есть ошибок, которые могут возникнуть при выполнении<br />

программы (например, при делении на нуль или нарушении обслуживания<br />

потоков). При встрече с ненормальной ситуацией он может передать управление<br />

другой части программы, продолжить ее выполнение либо завершить<br />

работу. Обычно определяют обработчик исключения (exception handler), выполняющий<br />

необходимые действия перед завершением программы. Блоки<br />

кода, генерирующие исключения, начинаются словом try и заключаются в<br />

фигурные скобки. В случае обнаружения ошибки происходит программное<br />

прерывание. При этом управление передается блоку обработчика исключения<br />

catch. В скобках указывают спецификацию класса конкретного исключения,<br />

а для перехвата всех ошибок – многоточие (…). Не менее одного такого<br />

блока следует за блоком try. Для каждой исключительной ситуации определен<br />

свой класс. Некоторые классы исключений приведены в табл. 5.


18<br />

Некоторые классы исключений<br />

Таблица 5.<br />

Класс<br />

EAccessViolation<br />

EConvertError<br />

EDatabaseError<br />

EDivByZero<br />

EInOutError<br />

EPrinterError<br />

EStackOverflow<br />

EZeroDivide<br />

EOverflow<br />

Описание<br />

Ошибка доступа в память<br />

Ошибка преобразования данных<br />

Ошибка доступа к базе данных<br />

Ошибка деления на нуль целого числа<br />

Ошибка файлового обмена<br />

Ошибка печати<br />

Переполнение стека<br />

Ошибка деления на нуль вещественного числа<br />

Переполнение регистров вещественных чисел<br />

В приведенном ранее тексте файла проекта в блок try функции WinMain<br />

помещены выражения инициализации объектов OLE Automation, создания<br />

главной формы и запуска приложения. В блоке catch в случае исключительной<br />

ситуации выводится сообщение с указанием ее причины.<br />

Простой пример обработки исключений может иметь вид:<br />

void __fastcall TForm1::Button2Click (TObject *Sender) {<br />

try { Label1->Caption = Edit1->Text ; }<br />

catch (...) { ShowMessage ("Ошибка"); }<br />

}<br />

Другой пример обработки исключительной ситуации:<br />

try { Roubles->Text =<br />

FloatToStrF (floor (StrToFloat (EditDollars->Text) *<br />

StrToFloat (EditRate->Text) + 0.5), ffFixed, 10, 2);<br />

}<br />

catch (const EConvertError &) {<br />

LabelError->Caption = “Неверный формат числа”;<br />

}<br />

catch (const EOverflow &) {<br />

LabelError->Caption = “Неверная величина числа”;<br />

}<br />

В блоке try можно использовать лексему throw для вызова исключительной<br />

ситуации или для указания типа исключения, которое вызывает<br />

функция. Вслед за блоком try вместо catch часто располагают блок __finally,<br />

выполняющийся вне зависимости от наличия исключительной ситуации.


19<br />

Простые типы данных и операторы<br />

Типы данных C++Builder ориентированы на 32-разрядную платформу<br />

(табл. 6).<br />

Таблица 6.<br />

Типы данных C++Builder<br />

Тип данных Байт Значения Название<br />

char 1 ±128 Байт<br />

BYTE 1 0…255 Байт без знака<br />

short 2 ±32767 Короткий целый<br />

unsigned short, WCHAR 2 0…65534 Короткий целый без знака<br />

int, long 4 ±2147483648 Целый<br />

unsigned long 4 0…4294967295 Целый без знака<br />

__int64 8 9,2E±18 Длинный целый<br />

bool 1 true, false Логический<br />

float 4 1,2E±38 Вещественный<br />

double 8 2,2E±308 Двойной вещественный<br />

long double 10 3,3E±4932 Длинный двойной вещественный<br />

void * 8 2,2E±308 Указатель<br />

char *<br />

Строковый с нулем в конце<br />

void<br />

Пустой<br />

TDateTime 8 Дата и время<br />

Currency 8 Валюта, 4 цифры после запятой<br />

Variant 16 Универсальный (int, float, char, bool)<br />

AnsiString<br />

Динамический строковый<br />

WideString<br />

Строковый, 2 байта на символ<br />

Первая октава образует категорию порядковых типов данных (ordered<br />

data type). Пять последних типов являются типизированными классами (typed<br />

class) C++Builder. Из большого многообразия типов данных рекомендуется в<br />

первую очередь использовать int и __int64, double, bool, AnsiString и void. Некоторые<br />

(фундаментальные) типы введены для совместимости с языком C:<br />

char, short, long, float.<br />

Типы указываются во всех объявлениях переменных и констант. Объявления<br />

переменных могут совмещаться с их инициализацией:<br />

[Модификатор] [Тип] имяПеременной [=Значение];<br />

а во множестве различных объявлений констант инициализация обязательна:<br />

const Тип имяКонстанты = Значение;<br />

enum [Тип] { списокЗначений };<br />

enum { списокЗначений } имяТипа;<br />

#define имяКонстанты Значение


20<br />

Последний вариант объявления констант введен для совместимости с C.<br />

Типы переменных, входящих в параметры функций, указываются в<br />

круглых скобках вслед за именем функции. Если параметр отсутствует, скобки<br />

остаются пустыми. Тип возвращаемого функцией значения указывается<br />

при ее объявлении и определении перед именем. Функция, не возвращающая<br />

значений, объявляется как функция типа void. Если у функции или данных не<br />

объявлен тип, они получают тип int.<br />

В соответствии с типами, различают символьные, строковые, целые и<br />

вещественные константы. Первые заключаются в апострофы (например, ‘e’<br />

или ‘1’), вторые – в кавычки («привет»). Целые бывают десятичными (например<br />

56), восьмеричными (056) и шестнадцатеричными (0хАС), а вещественные<br />

– десятичными (6.23) и экспоненциальными (5Е-12). Специальную<br />

группу символьных констант образуют управляющие последовательности<br />

(табл. 7).<br />

Таблица 7.<br />

Управляющие последовательности<br />

'\0' Ноль '\t' Горизонтальная табуляция<br />

'\a' Сигнал '\v' Вертикальная табуляция<br />

'\b' Возврат на символ '\"' Кавычки<br />

'\f' Новая страница '\'' Апостроф<br />

'\n' Новая строка '\\' Обратная косая черта<br />

'\r' Начало строки<br />

Данные, которыми манипулирует программа, хранятся в ячейках памяти<br />

компьютера, и именованные ячейки памяти мы назвали переменными и константами.<br />

Hо память предназначена не только для хранения значений, обрабатываемых<br />

в ходе выполнения программы. Часть ячеек памяти выделяется<br />

для указания на другие области, и эту часть называют указателями и ссылками.<br />

«Введение связей с другими элементами данных через указатели – чрезвычайно<br />

важная идея в программировании; это ключ к представлению сложных<br />

структур» [Кнут]. Указатель (pointer) – это специальный тип данных,<br />

описывающий не значение, а размещение переменной, константы или возвращаемого<br />

значения функции, «одно из хитроумных понятий C» [Сван]. Его<br />

объявляют как Тип *имяУказателя. Оператор разыменования * (dereference<br />

operator) служит средством доступа к данным, а имяУказателя может быть<br />

другим указателем или адресом конкретной области памяти. В последнем<br />

случае указатель называют ссылкой (reference) и объявляют как Тип<br />

&имяСсылки. & – это оператор взятия адреса, а имяСсылки – значение хранящихся<br />

по этому адресу данных. Между операторами * и & установлено следующее<br />

соотношение: если имяУказателя – адpес данных, то *имяУказателя<br />

– их значение (т.е. указатель); если имяСсылки – значение данных, то<br />

&имяСсылки – их адpес (т.е. ссылка).


21<br />

Однажды инициализировав ссылку, ей нельзя присвоить другое значение.<br />

И, в отличие от указателей, которые могут быть объявлены без инициализации<br />

или установлены в нуль, ссылки всегда указывают на конкретные<br />

данные.<br />

Использование указателей и ссылок играет важную роль в работе с<br />

функциями. Обычно аргументы, передаваемые в функцию при ее вызове, обрабатываются<br />

функцией как копии данных, и потому сами данные не меняются<br />

в ходе выполнения функции. В вызывающем модуле функция может<br />

изменить только одну переменную – ту, которую она возвращает как результат<br />

своей работы. Если же требуется, чтобы функция изменяла более одной<br />

переменной, ей в качестве аргументов следует передавать не значения, а<br />

ссылки на данные, и в первую очередь – адpеса переменных. Аналогичное<br />

правило действует и при передаче функции в другую функцию в качестве аргумента.<br />

Для выполнения операций над типизированными данными в C++ используется<br />

более 50 простых операторов. В табл. 8 они расположены по убыванию<br />

приоритета.<br />

Таблица 8.<br />

Простые операторы C++<br />

Приоритет<br />

Категория Доступ к данным<br />

1 Первичные () [] {} /* */ // ‘’<br />

“” :: ; # -> .<br />

2 Унарные & * - + (тип) new<br />

delete sizeof<br />

Арифметические<br />

Логические<br />

++ -- ! ~<br />

Двоичные<br />

3 Мультипликативные * / % >> =<br />

== !=<br />

6 И && &<br />

7 Исключающее ИЛИ ^<br />

8 ИЛИ || |<br />

9 Условие ? :<br />

10 Присваивание = *= /= %=<br />

+= -=<br />

=<br />

&= |= ^=<br />

Круглые скобки отделяют список аргументов, квадратные – индексы<br />

массивов, фигурные – блоки данных, косые – комментарии. В кавычки заключаются<br />

константы. Двойное двоеточие (::) образует оператор области видимости<br />

данных. Точка с запятой (;) завершает выражения. Знак номера (#)<br />

начинает директивы препроцессора. Стрелка ( -> ) и точка (.) отделяют свойства<br />

и методы классов.<br />

Амперсанд (&) и звездочка (*) помечают адреса, ссылки и указатели.<br />

Операторы сложения и вычитания (+, -) меняют знаки операндов. В скобки


22<br />

заключают временно заменяемый тип данных. Оператором new создаются, а<br />

оператором delete удаляются объекты. Оператор sizeof определяет размер<br />

данных. Оригинальные операторы автоинкремента (++) и автодекремента (-<br />

-) могут действовать до операции или после ее. Так, действие j = i++ аналогично<br />

выражениям j = i; i = i+1. А j = --i можно представить как i = i-1; j = i. То<br />

же можно сказать и о таких операторах, как *=, /= и им подобным. Восклицательный<br />

знак (!) служит для отрицания, а тильда (~) – для инверсии.<br />

Звездочка (*) является знаком умножения, косая черта (/) – деления, а<br />

процент (%) – остатка от деления нацело. Двойными стрелками (>>,


23<br />

Продолжение таблицы 10.<br />

Метод<br />

FormatFloat (AnsiString& format,<br />

long double& val)<br />

Insert (AnsiString& str, int index)<br />

IsDelimiter (AnsiString& delimiters,<br />

int index)<br />

IsEmpty()<br />

LastDelimiter (AnsiString& delimiters)<br />

Length()<br />

LoadStr (int ident)<br />

operator != (AnsiString& rhs)<br />

operator < (AnsiString& rhs)<br />

operator (AnsiString& rhs)<br />

operator >= (AnsiString& rhs)<br />

operator == (AnsiString& rhs)<br />

operator [] (int idx)<br />

operator = (AnsiString& rhs)<br />

operator + (AnsiString& rhs)<br />

operator += (AnsiString& rhs)<br />

Pos (AnsiString& subStr)<br />

printf (char* format, ...)<br />

sprintf (char* format, ...)<br />

SetLength (int newLength)<br />

StringOfChar (char ch, int count)<br />

SubString (int index, int count)<br />

ToDouble (), ToInt ()<br />

ToIntDef (int defaultVal)<br />

Trim (),TrimLeft (),TrimRight ()<br />

UpperCase ()<br />

Назначение<br />

Задает формат представления (0, #, ., ;, E+, ‘ ‘, “ “)<br />

положительных и отрицательных вещественных чисел<br />

и нулей, например # ##0,00 “руб.”<br />

Вставляет подстроку str в позицию index<br />

Подтверждает наличие определенного символа<br />

(delimiter) в заданной позиции (index)<br />

Подтверждает, что строка пуста<br />

Последняя позиция в строке определенного символа<br />

(delimiter)<br />

Длина строки в байтах<br />

Загружает строку из файла<br />

Сравнение с другой строкой rhs<br />

Символ под указанным индексом idx<br />

Инициализация строки другой строкой rhs<br />

Соединение с другой строкой rhs<br />

Позиция начала подстроки subStr в строке<br />

Задают формат строки в стандарте языка С, возвращая<br />

длину или ссылку на строку<br />

Задает длину (newLength) строки<br />

Строка заданного числа (count) символов (ch)<br />

Подстрока длиной count в позиции index<br />

Преобразуют строку в число, в т.ч. – в число по<br />

умолчанию в случае ошибки преобразования<br />

Удаляют пробелы<br />

Преобразует в прописные буквы<br />

Отдельные символы строк класса AnsiString индексируются, начиная с 1.<br />

В следующем примере приведен текст конструктора формы Form1, в<br />

котором вычисляется округленное значение гипотенузы и результат направляется<br />

в надпись Label1. Затем вычисляется средняя длина двух окружностей,<br />

и округленный результат направляется в надпись Label2. Тексты надписей<br />

имеют тип данных AnsiString, поэтому используются функции преобразования<br />

типов FloatToStr и FloatToStrF.<br />

#include <br />

#include <br />

#pragma hdrstop<br />

#pragma resource "*.dfm"


TForm1 *Form1;<br />

24<br />

__fastcall TForm1::TForm1 (TComponent* Owner) : TForm(Owner) {<br />

double a = 5, b = 7;<br />

Form1->Label1->Caption = FloatToStr (floor (hypot (a, b) + 0.5));<br />

double R1 = 5, R2 = 7;<br />

Form1->Label2->Caption = FloatToStrF (M_PI * (R1 + R2), ffFixed, 10, 2);<br />

}<br />

В примере задействована математическая библиотека Borland C , подключаемая<br />

файлом заголовка . <strong>Функции</strong> этой библиотеки описаны в<br />

табл. 10:<br />

Таблица 11.<br />

Математические функции Borland C<br />

<strong>Функции</strong><br />

abs, fabs<br />

acos, asin, atan<br />

cos, sin, tan<br />

exp<br />

floor<br />

hypot<br />

pow, pow10<br />

seal<br />

sqrt<br />

Назначение<br />

Абсолютное значение<br />

Обратные тригонометрические функции<br />

Тригонометрические функции<br />

Экспонента<br />

Ближайшее меньшее целое<br />

Гипотенуза<br />

Степень<br />

Ближайшее большее целое<br />

Квадратный корень<br />

Второй пример посвящен использованию типа AnsiString, а также типов<br />

TDateTime, Currency и Variant.<br />

void __fastcall TForm1::vAnsiStringClick (TObject *Sender) {<br />

AnsiString as = Edit0->Text, as1 = as;<br />

Label0->Caption = as.StringOfChar ('>', as.Length ())<br />

+ as.ToIntDef (0) + as.StringOfChar ('


void __fastcall TForm1::vCurrencyClick (TObject *Sender) {<br />

Currency c = Edit2->Text;<br />

Label2->Caption = (c < 100) ? AnsiString (--c) + " меньше 100 рублей":<br />

AnsiString (c++) + " больше 100 рублей";<br />

}<br />

25<br />

void __fastcall TForm1::vVariantClick (TObject *Sender) {<br />

Variant v = Edit3->Text;<br />

Label3->Caption = (v.Type () == varString) ? v : BoolToStr (v.operator bool ());<br />

}<br />

В третьем примере дана форма с полями ввода Edit1 и Edit2, кнопкой<br />

Button1 и надписью Label1:<br />

class TForm1 : public TForm {<br />

__published:<br />

TEdit *Edit1;<br />

TEdit *Edit2;<br />

TLabel *Label1;<br />

TButton *Button1;<br />

void __fastcall Button1Click (TObject *Sender);<br />

public:<br />

AnsiString CalcF (double, double);<br />

AnsiString CalcSF (AnsiString, double);<br />

AnsiString CalcS (AnsiString, AnsiString);<br />

};<br />

При нажатии кнопки данные из полей формы преобразуются к нужному<br />

формату, обрабатываются в соответствии с условием конкретной задачи, и<br />

результат возвращается в надпись:<br />

void __fastcall TForm1::Button1Click (TObject *Sender) {<br />

AnsiString c = "";<br />

try {<br />

c = CalcF (StrToFloat (Form1->Edit1->Text),<br />

StrToFloat (Form1->Edit2->Text));<br />

} catch (Exception &EConvertError) {<br />

try {<br />

c = CalcSF (Form1->Edit1->Text, StrToFloat(Form1->Edit2->Text));<br />

} catch (Exception &EConvertError) {<br />

try {<br />

c = CalcS (Form1->Edit1->Text, Form1->Edit2->Text);<br />

} catch (...) { c = "Ошибка данных"; }<br />

}<br />

}


}<br />

Form1->Label1->Caption = c;<br />

26<br />

Далее следуют обработчики кнопки Button1, которые выводят в надпись Label1<br />

решение трех задач:<br />

AnsiString TForm1::CalcF (double a, double b) { return a / b; }<br />

AnsiString TForm1::CalcSF (AnsiString a, double b) {<br />

return a + FloatToStr (++b);<br />

}<br />

AnsiString TForm1::CalcS (AnsiString a, AnsiString b) {<br />

return “Имя: “ + a + “\nФамилия: “ + b;<br />

}<br />

Еще один пример посвящен передачи данных из модальной формы в<br />

обычную. Необходимые данные должны быть получены до того, как модальная<br />

форма будет уничтожена. Это удобно делать, в частности, через дополнительный<br />

параметр конструктора. В рассматриваемом примере таким параметром<br />

является указатель *arg, который инициализируется в конструкторе.<br />

При нажатии кнопки Button1 запомненный по указанному адресу заголовок<br />

формы переносится в надпись Label1 не модальной формы:<br />

class TLocalForm : public TForm {<br />

__published: TLabel *Label1;<br />

public:<br />

__fastcall TLocalForm (TComponent* Owner);<br />

__fastcall TLocalForm (String *arg, TComponent* Owner);<br />

};<br />

__fastcall TLocalForm::TLocalForm (String *arg, TComponent*Owner):<br />

TForm (Owner) { *arg = this->Caption; }<br />

void __fastcall TForm1::Button1Click (TObject *Sender) {<br />

String s;<br />

TLocalForm *LocalForm = new TLocalForm (&s, 0);<br />

LocalForm->ShowModal(); delete LocalForm;<br />

this->Label1->Caption = s;<br />

}


27<br />

Составные операторы<br />

«Длинна та дорога, где нет поворота»<br />

Английская пословица<br />

Операторы перехода<br />

Ветвящиеся и циклические алгоритмы управления информационными<br />

процессами реализуются с помощью составных операторов. Такие операторы<br />

часто входят друг в друга, образуя управляющих структуры.<br />

Традиционным средством организации нелинейных процессов являются<br />

операторы перехода, служащие для изменения хода вычислительных процессов.<br />

К таким операторам относятся: break, continue, return, goto Метка.<br />

Первый из операторов перехода прекращает выполнение самого внутреннего<br />

из составных операторов, передавая управление следующему за прерываемым<br />

оператором. Второй прерывает выполнение составного оператора,<br />

передавая управление в его конец. Третий завершает выполнение функции. А<br />

четвертый передает управление на метку внутри текущей функции, состоящую<br />

из имени и двоеточия. Применение его не поощряется в современном<br />

программировании, так как нарушает принцип процедурного подхода. «Goto<br />

- неизбежное зло, ибо программы с безусловными переходами трудно понять»<br />

[Бабэ]; «любая программа может обойтись без оператора goto» [Трой].<br />

Условный оператор<br />

С помощью условного оператора ход вычислительного процесса организуется<br />

в зависимости от значения логического выражения:<br />

if (истинноеЛогическоеВыражение) Выражение… [else Выражение… ]<br />

Логическое выражение строится на базе логических операторов и переменных.<br />

Пример:<br />

void Ifs1 () {<br />

double x, y, r;<br />

x=StrToFloat (MyForm->Edit1->Text);<br />

y=StrToFloat (MyForm->Edit2->Text);<br />

r=StrToFloat (MyForm->Edit3->Text);<br />

if (x * x + y * y < r * r) MyForm->Label1->Caption = "Точка внутри круга";<br />

else MyForm->Label1->Caption = "Точка вне круга";<br />

}<br />

Другой пример посвящен сравнению двух слов:


28<br />

void Ifs2 () {<br />

AnsiString a, b;<br />

a= Edit1->Text; b= Edit2->Text;<br />

if (a > b) Label1->Caption = "a”; else Label1->Caption = "b”<br />

}<br />

Следующий пример демонстрирует, как можно с помощью условного<br />

оператора организовать построение произвольных линий, подобно тому, как<br />

это делается в редакторе Paint. Рисование начинается в момент нажатия<br />

кнопки мыши и заканчивается в момент ее отпускания. Флаг нажатия drawing<br />

должен быть предварительно внесен в список открытых членов класса формы.<br />

void __fastcall TForm1::FormMouseDown (TObject *Sender,<br />

TMouseButton Button, TShiftState Shift, int X, int Y) {<br />

drawing=true;<br />

Canvas->TextOut (X, Y, "Старт!"); Canvas->MoveTo (X, Y);<br />

}<br />

void __fastcall TForm1::FormMouseUp (TObject *Sender,<br />

TMouseButton Button, TShiftState Shift, int X, int Y) {<br />

drawing = false; Canvas->TextOut (X, Y, "Стоп!");<br />

}<br />

void __fastcall TForm1::FormMouseMove (TObject *Sender,<br />

TShiftState Shift, int X, int Y) {<br />

if (drawing) Canvas->LineTo (X, Y);<br />

}<br />

Еще одна функция показывает работу методов Delete (удаление букв) и<br />

Length (длина строки) класса AnsiString в операции сцепления первой буквы<br />

слова a, точки и слова b:<br />

void Ifs3 () {<br />

AnsiString a = MyForm->Edit1->Text;<br />

AnsiString b = MyForm->Edit2->Text;<br />

AnsiString c = a.Delete (2, a.Length () - 1);<br />

if (a > b) MyForm->Label1->Caption = c + '.' + b + '\n' + a;<br />

else MyForm->Label1->Caption = a + '\n' + c + '.' + b;<br />

}


29<br />

Составные операторы могут иметь любую глубину вложенности. Но<br />

«вложенный набор операторов if...else иногда напоминает водопровод старого<br />

дома: система работает, но трудно понять, какая труба куда ведет» [Сван].<br />

Оператор выбора<br />

Оператор выбора служит для организации хода вычислительного процесса<br />

в зависимости от значения выражения или переменной:<br />

switch (порядковаяПеременная)<br />

{ case Константа : Выражение…<br />

...<br />

[default : Выражение…] }<br />

После выполнения каждого выражения действие передается следующему<br />

выражению. Для прерывания этой последовательности обработку каждой<br />

константы в операторе выбора завершают оператором break. Выражения, открываемые<br />

лексемой default, рассчитаны на обработку незапланированных<br />

значений логического выражения. Пример:<br />

void Cases () {<br />

AnsiString day;<br />

}<br />

int n = StrToInt (MyForm->Edit1->Text);<br />

switch (n) {<br />

case 1: day = "Понедельник"; break;<br />

case 2: day = "Вторник"; break;<br />

case 3: day = "Среда"; break;<br />

case 4: day = "Четверг"; break;<br />

case 5: day = "Пятница"; break;<br />

case 6: day = "Суббота"; break;<br />

case 7: day = "Воскресенье"; break;<br />

default: day = "Ошибка ввода";<br />

} MyForm->Label1->Caption = day;<br />

Следующий пример посвящен созданию новой формы в ходе выполнения<br />

программы. Для передачи форме значения параметра newArg класс формы<br />

дополнен вторым конструктором:<br />

class TLocalForm : public TForm {<br />

__published: TLabel *Label1;<br />

public:<br />

__fastcall TLocalForm (TComponent* Owner);<br />

__fastcall TLocalForm (int newArg, TComponent* Owner);<br />

};


30<br />

__fastcall TLocalForm::TLocalForm (int newArg, TComponent* Owner):<br />

TForm (Owner) {<br />

switch (newArg) {<br />

case 1: Label1->Caption = "Обычная форма"; break;<br />

case 2: Label1->Caption = "Форма с параметром"; break;<br />

}<br />

}<br />

Оператор цикла<br />

Оператор цикла организует повторяющийся вычислительный процесс в<br />

заданном диапазоне изменения переменной-счетчика цикла:<br />

for ([ выражениеИнициализации ];<br />

[ выражениеУсловия ];<br />

[ выражениеИтерации ]) Выражение…<br />

Выражения цикла, условия и итерации строятся с использованием переменной-счетчика<br />

цикла, который относится к одному из порядковых типов.<br />

Выражение итерации может быть как увеличивающимся, так и уменьшающимся,<br />

например, a++, b-=2, c--. По завершении цикла значение переменной-счетчика<br />

остается неопределенным.<br />

void Fors1 () {<br />

char ch;<br />

AnsiString alphabet;<br />

// ch – переменная-счетчик цикла<br />

}<br />

for (ch='A'; chLabel1->Caption = alphabet;<br />

Во втором примере для извлечения символов в операциях сравнения<br />

строк и формирования строки символов алфавита служит метод operator []<br />

класса AnsiString. Этот метод возвращает символ строки, соответствующий<br />

указанному в качестве аргумента порядковому номеру.<br />

void Fors2 () {<br />

AnsiString chars="", aa, bb;<br />

aa = MyForm->Edit2->Text; bb = MyForm->Edit3->Text;<br />

char a=aa.operator [](1); char b=bb.operator [](1);<br />

for (char k=a; kLabel1->Caption = chars;<br />

}


31<br />

Для досрочного завершения цикла в его тело включают операторы перехода.<br />

При отсутствии выражения условия или итерации образуются «вечные<br />

циклы», например for (;;).<br />

Операторы повторения<br />

Операторы повторения предназначены для организации вычислительных<br />

процессов, повторяющихся заранее неизвестное число раз. Различают<br />

операторы повторения с предусловием и с постусловием:<br />

while (истинноеЛогическоеВыражение ) Выражение…<br />

do Выражение… while (истинноеЛогическоеВыражение );<br />

Значение логического выражения обычно меняется в теле оператора в ходе<br />

выполнения выражений. В противном случае образуются «вечные циклы»<br />

(endless repetition) типа while (1) или do while (!0). До начала работы оператора<br />

с предусловием должно быть сформировано истинное логическое выражение.<br />

Для досрочного завершения выполнения операторов повторения в<br />

выражениях размещаются операторы перехода. Примеры:<br />

void Whiles1 () {<br />

int temp = 10, count = 1;<br />

AnsiString result = MyForm->Label1->Caption;<br />

while (2 * temp > 3 * count++) result = result + IntToStr (temp--) + ' '; // 10 9 8 7<br />

MyForm->Label1->Caption = result;<br />

}<br />

Оператор повторения с постусловием относится к числу наименее надежных<br />

управляющих структур, так как условие его завершения проверяется только<br />

после начала работы.<br />

void Whiles2 () {<br />

int x = 9;<br />

}<br />

do<br />

MyForm->Label1->Caption = MyForm->Label1->Caption +<br />

IntToStr (x--) + " в квадрате равно " + IntToStr (x * x) + '\n';<br />

while (x);<br />

А вот пример «вечного цикла», выход из которого возможен только с<br />

помощью оператора перехода:


void Whiles3 () {<br />

int leapYear, year;<br />

TLabel *lb = MyForm->Label1;<br />

try { year = StrToInt (MyForm->Edit1->Text); } catch (...) {<br />

lb->Caption = "Вводите годы целыми числами"; return;<br />

}<br />

while (1) {<br />

if (year > 0) {<br />

if ((year % 100) == 0) leapYear = ((year % 400) == 0);<br />

else leapYear = ((year % 4) == 0);<br />

if (leapYear) lb ->Caption = " - високосный";<br />

else lb ->Caption = " - не високосный";<br />

break;<br />

}<br />

else { lb ->Caption = " - неверный"; break; }<br />

} lb ->Caption = IntToStr (year) + lb ->Caption + " год";<br />

}<br />

32<br />

Программирование диалогов<br />

Составные операторы помогают организовать диалог с пользователем с<br />

помощью специально предназначенных для этого функций (табл. 11), что<br />

особенно удобно на этапе подготовки и отладки приложений.<br />

Таблица 12.<br />

<strong>Функции</strong> сообщений<br />

Функция<br />

int MessageDlg (AnsiString text, TMsgDlgType type,<br />

TMsgDlgButtons buttons, int helpContext)<br />

int MessageDlgPos (AnsiString text, TMsgDlgType type,<br />

TMsgDlgButtons buttons, int helpContext, int X, int Y)<br />

void ShowMessage (AnsiString text)<br />

bool SelectDirectory (AnsiString &dir, TSelectDirOpts options,<br />

int helpContext)<br />

Назначение<br />

Сообщение в центре экрана с<br />

возвратом результата<br />

Сообщение в позиции X,Y с<br />

возвратом результата<br />

Сообщение с кнопкой ОК<br />

Окно диалога для выбора<br />

папки<br />

Большинство функций диалога возвращает определенное значение, которое<br />

можно обрабатывать в составных операторах.<br />

Ниже дан пример обработчика, в котором нажатие кнопки открывает<br />

возможность выбора или создания новой папки, после чего ее адрес отображается<br />

в надписи.<br />

#include <br />

#include <br />

#include <br />

#pragma hdrstop


#pragma resource "*.dfm"<br />

TForm1 *Form1;<br />

33<br />

void __fastcall TForm1::Button1Click (TObject *Sender) {<br />

if (MessageDlg ("Создать/выбрать папку?", mtConfirmation,<br />

TmsgDlgButtons () Controls[i]->Name ;<br />

ShowMessage ("Дочерние объекты формы " + info);<br />

info.Delete (1, info.Length ());<br />

for (i=0; iToolBar1->ControlCount; ++i)<br />

info = info + '\n' + MyForm->ToolBar1->Controls[i]->Name;<br />

ShowMessage ("Дочерние объекты панели инструментов " + info);


34<br />

Введение в структурное программирование<br />

«Подобное к подобному влечется»<br />

Из Платона<br />

Массивы<br />

Для группового представления и обработки однотипных данных применяются<br />

составные типы – массивы (array). В объявлениях массива используются<br />

прямоугольные скобки:<br />

Тип имяМассива [ [Размер] ]…[={списокЗначений} ];<br />

Массивы инициализируются одним из двух способов:<br />

имяМассива [ Индекс ]…=Значение;<br />

имяМассива [ [Размер] ]…= { списокЗначений };<br />

Размер и индекс относятся к целому типу. Они указываются в квадратных<br />

скобках вслед за именем массива. При наличии списка значений один из<br />

размеров можно опускать: он рассчитывается автоматически. Массивы индексируются<br />

с нуля. Число размеров определяет размерность массива, а число<br />

членов массива равно произведению всех размеров. Пример:<br />

void Arrays1 () {<br />

AnsiString students[] = {"Иванов", "Петров", "Сидоров"};<br />

MyForm->Edit1->Text = students[0];<br />

MyForm->Edit2->Text = students[1];<br />

MyForm->Edit3->Text = students[2];<br />

}<br />

Число байтов, выделяемое массиву при инициализации, определяется<br />

как произведение размера типа и числа членов. Для резервирования и освобождения<br />

этой памяти целесообразно использовать выражения<br />

Пример:<br />

имяМассива = new Тип [ Размер ]…;<br />

delete [ ] имяМассива;<br />

void Arrays2 () {<br />

AnsiString *whos;


35<br />

whos = new AnsiString[4];<br />

whos[0] = MyForm->Edit1->Text; whos[1] = "Mr. " + whos[0];<br />

whos[2] = "Dr. " + whos[0]; whos[3] = *(whos + 1);<br />

MyForm->RichEdit1->Text =<br />

whos[0] + '\n' + whos[1] +'\n' + whos[2] + '\n' + whos[3];<br />

delete [] whos;<br />

}<br />

Имя каждого члена массива состоит из имени массива и индексов в<br />

квадратных скобках. Программа обрабатывает многомерные массивы, начиная<br />

с членов, отмеченных крайними справа индексами. Имя массива указывает<br />

на адрес его нулевого члена. Так как начальный адрес массива устанавливается<br />

автоматически в момент инициализации, он не может быть переопределен<br />

пользователем и, следовательно, имена массивов нельзя присваивать<br />

друг другу. Копирование массивов выполняется почленно, в том числе с помощью<br />

специальных функций.<br />

Массив передается в функцию только по ссылке на свой адрес. При передаче<br />

массива в функцию в качестве аргумента указывается имя массива, а<br />

при определении функции с многомерным массивом параметром задается<br />

имя массива и все размеры, кроме первого:<br />

void F1 () { AnsiString x[3]; F2 (x); ShowMessage (x[0] + ‘ ‘ + x[1]); }<br />

void F2 (AnsiString y[3]) { y[0] = “Привет”; *(y+1) = “Салют”; }<br />

void F3 () {<br />

int x[2][2]; F4 (x); ShowMessage (IntToStr (x[0][0]) + ‘ ‘ + IntToStr (x[0][1]));<br />

}<br />

void F4 (int y[][2]) { y[0][0] = 1; y[0][1] = 2; }<br />

Примером массива является свойство Cells компонента StringGrid, которым<br />

задается содержание ячейки, адресуемой индексами. Массивами описывают<br />

также отдельные панели строки состояния и строки списков.<br />

void Arrays4 () {<br />

MyForm->StringGrid1->Cols[1]->Add (“0-ая ячейка 1-го столбца”);<br />

MyForm->StringGrid1->Cols[1]->Add (“1-ая ячейка 1-го столбца”);<br />

MyForm->StringGrid1->Cols[1]->Append (“2-ая ячейка 1-го столбца”);<br />

MyForm->StringGrid1->Cells[2][3] = “Ячейка 2-го столбца 3-ей строки”;<br />

MyForm->StatusBar1->Panels->Items[0]->Text = "Левая панель";


}<br />

MyForm->ListBox1->Items->Strings[0] = "Нулевая строка списка";<br />

36<br />

Массивы эффективно используются в графике, например, для рисования<br />

геометрических фигур по канве формы:<br />

void __fastcall TForm1::FormPaint (TObject *Sender) {<br />

TPoint v[5];<br />

int cw = ClientWidth, ch = ClientHeight;<br />

v[0] = Point (0, cw/10); v[1] = Point (cw/4, cw/10); v[2] = Point (cw/2, cw/4);<br />

v[3] = Point (cw/4, cw/4); v[4] = Point (0, cw/10); Canvas->Polyline (v, 4);<br />

v[0] = Point (cw/2, ch/2); v[1] = Point (cw/2, ch);<br />

v[2] = Point (cw,ch); Canvas->Polygon (v,2);<br />

Canvas->Rectangle (0, ch/2, cw/2, ch);<br />

Canvas->RoundRect (0, ch/2, cw/2, ch, cw/10, ch/10);<br />

Canvas->Ellipse (0, ch/2, cw/2, ch);<br />

}<br />

Свойство Pixels типа TColor у компонентов Canvas можно использовать<br />

как двухмерный массив цвета точки, координаты которой отсчитываются<br />

от левого верхнего угла изображения.<br />

void __fastcall TForm1::FormMouseDown (TObject *Sender,<br />

TMouseButton Button, TShiftState Shift, int X, int Y) {<br />

Label1->Caption = X; Label2->Caption = Y;<br />

Label3->Caption = Canvas->Pixels[X][Y];<br />

Canvas->Pixels[X][Y] = clWhite;<br />

}<br />

Перестановки и сортировки<br />

Простым алгоритмом описывается широко распространенная в программировании<br />

операция перестановки (swap) данных местами. Пример перестановки<br />

пары переменных выглядит так:<br />

void Swap (int &a, int &b) { int x = a; a = b; b = x; }<br />

void SwapVar () {<br />

int x = 2, y = 1;<br />

Swap (x, y);<br />

ShowMessage (x >y ? “x > y” : “x < y”);<br />

}


37<br />

Следующий пример иллюстрирует перестановку членов массива:<br />

void Swap (AnsiString a[], int n) {<br />

int i, j;<br />

for (i=0, j=n-1; i


38<br />

ing), в отличие от «пузырька», переставляются не соседние, а далеко отстоящие<br />

члены, что иногда повышает скорость в 1,5 раза.<br />

Примерно втрое большую скорость, чем сортировка методом «пузырька»,<br />

обеспечивает «быстрая сортировка» (quick sorting). При сортировке<br />

этим методом выбирается произвольный член, а остальной список сортируемых<br />

членов делится пополам, и каждый член одной половины сравнивается с<br />

членами другой. Затем список вновь делится пополам и выполняется перестановка<br />

и т.д. Этот алгоритм положен в основу ряда библиотечных функций.<br />

void Swap (int &x, int &y) { int temp = x; x = y; y = temp; }<br />

void BubbleSort (int array[], int N) {<br />

for (int i=0; ii; --j)<br />

if (array[j] < array[j-1]) Swap (array[j], array[j-1]);<br />

}<br />

void ExchangeSort (int array[], int N) {<br />

for (int i=0; i array[j]) Swap (array[j], array[j-1]);<br />

}<br />

void ShellSort (int array[], int N) {<br />

int delta = 1, sw, offset = (N-1+delta) / 2;<br />

while (offset > 0) {<br />

int limit = N-1+delta-offset;<br />

do {<br />

sw = 0;<br />

for (int i=1; iarray[i+offset-delta]) {<br />

Swap (array[i-delta], array[i+offset-delta]); sw = i;<br />

}<br />

limit = sw - offset;<br />

} while (sw);<br />

offset /= 2;<br />

}<br />

}


39<br />

void QuickSort (int array[], int left, int right) {<br />

int i = left, j = right, temp, test = array [(left+right) / 2];<br />

do {<br />

while (array[i] < test) ++i;<br />

while (test < array[j]) --j;<br />

if (i RadioGroup1->ItemIndex) {<br />

case 0: BubbleSort (array, N); break;<br />

case 1: ExchangeSort (array, N); break;<br />

case 2: InsertionSort (array, N); break;<br />

case 3: ShellSort (array, N); break;<br />

case 4: QuickSort (array, 0, N-1); break;<br />

}<br />

for (i=0; i


= 0, если elem1 == elem2,<br />

> 0, если elem1 > elem2:<br />

40<br />

int Sort_function (const void *a, const void *b);<br />

void Qsorting () {<br />

char list[5] = {'c', 'r', 'a', 'p', 'n'};<br />

int i;<br />

AnsiString result;<br />

qsort ((void*) &list, 5, sizeof (list[0]), Sort_function);<br />

for (i = 0; i < 5; i++) result += AnsiString (list[i]) + ' ';<br />

ShowMessage (result);<br />

}<br />

int Sort_function (const void *a, const void *b) {<br />

return strcmp ((char*) a, (char*) b);<br />

}<br />

Списки TStringList<br />

Разновидностью массивов являются последовательности символьных<br />

строк – списки. VCL поддерживает общий интерфейс со списками через<br />

класс TString и производный от него класс TStringList. Это один из самых популярных<br />

классов, представляющий удобный контейнер для хранения строк<br />

и связанных с ними объектов. Методы класса позволяют загружать и сохранять,<br />

создавать списки, манипулировать строками, сортировать, вставлять,<br />

добавлять и удалять их.<br />

Временные списки и списки длительного пользования проектируют поразному.<br />

Первые обычно создаются и разрушаются в пределах одной функции:<br />

void __fastcall TForm1::Button1Click (TObject *Sender) {<br />

TStringList *TempList = new TStringList;<br />

try { Label1->Caption = “Временный список используется”; }<br />

__finally { delete TempList;}<br />

}<br />

Вторые сначала объявляются в файле заголовка формы как указатели<br />

на объекты класса TString. Объявленные объекты создаются в конструкторе<br />

формы и освобождают память при ее уничтожении. В приведенном ниже<br />

примере в файле заголовка формы был объявлен открытый указатель<br />

TStringList *ConstList:


__fastcall TForm1::TForm1 (TComponent* Owner): TForm (Owner) {<br />

ConstList = new TStringList;<br />

}<br />

void __fastcall TForm1::FormClose (TObject *Sender, TCloseAction &Action) {<br />

ConstList->SaveToFile (ChangeFileExt (Application->ExeName, ".LOG"));<br />

delete ConstList;<br />

}<br />

void __fastcall TForm1::FormMouseDown (TObject *Sender,<br />

TMouseButton Button, TShiftState Shift, int X, int Y) {<br />

TVarRec v[] = {X,Y};<br />

ConstList->Add (Format ("Курсор в (%d,%d)", v, ARRAYSIZE (v) - 1));<br />

}<br />

41<br />

Новая строка в конец списка добавляется методом Add. Количество<br />

строк в списке вычисляется методом Count, а свойство Strings содержит весь<br />

массив строк, доступных по их индексам, начиная с 0. Метод IndexOf определяет<br />

индекс строки, содержащей указанный текст, например:<br />

void StringLists2 () {<br />

MyForm->ConstList->Add ("Конец");<br />

MyForm->ConstList->Strings [ConstList->Count-1] =<br />

MyForm->ConstList->IndexOf ("Конец");<br />

}<br />

Чтобы добавить i-ю строку в список, используется метод Insert. Для<br />

добавления строк из другого списка служит методы Assign и AddStrings.<br />

Перемещение строк внутри списка выполняется методом Move. Удалением<br />

строки занимается метод Delete, а Clear очищает список:<br />

void StringLists3 () {<br />

TStringList *tsl = new TStringList;<br />

tsl->Add (“Раз”);<br />

tsl->Add (“Два”);<br />

ShowMessage (tsl->Count);<br />

ShowStr (tsl);<br />

// Раз Два<br />

tsl->Insert (1, “Полтора”); ShowStr (tsl); // Раз Полтора Два<br />

tsl->Move (0, 2); ShowStr (tsl);<br />

// Полтора Два Раз<br />

tsl->Exchange (0, 1); ShowStr (tsl); // Два Полтора Раз<br />

tsl->Delete (1); ShowStr (tsl);<br />

// Два Раз<br />

tsl->Sort (); ShowStr (tsl);<br />

// Два Раз<br />

int index; if (tsl->Find (“Раз”, index)) ShowMessage (index); // 0<br />

tsl->Clear (); ShowStr (tsl);


42<br />

TStringList *temp = new TStringList;<br />

Temp->Assign (tsl); ShowStr (temp);<br />

delete temp;<br />

// Два Раз<br />

Memo1->Lines->Assign (tsl);<br />

RichEdit1->Lines->Assign (tsl);<br />

tsl->Assign (Memo1->Lines);<br />

TStrings *ml = Memo1->Lines;<br />

ml->Add (“Три”);<br />

ShowMessage (ml->Count);<br />

ml->Move (0, 1);<br />

ml->Insert (1, “Четыре”);<br />

ml->Delete (1);<br />

ml->Clear ();<br />

ComboBox1->Items->AddStrings (tsl);<br />

TStrings *lb = ListBox1->Items;<br />

lb->AddStrings (tsl);<br />

lb->Add (“Три”);<br />

lb->Append (“Четыре”);<br />

ShowMessage (lb->Count);<br />

lb->Insert (2, “Пять”);<br />

lb->Move (0, 1);<br />

lb->Exchange (0, 1);<br />

lb->Delete (1);<br />

lb->SaveToFile (“1.txt”);<br />

lb->Clear ();<br />

lb->LoadFromFile (“1.txt”);<br />

ShowMessage (lb->Text);<br />

ShowMessage (lb->Strings[0]);<br />

ShowMessage (ListBox1->ItemIndex); // номер выделенной строки<br />

if (ListBox1->Selected[1] == true)<br />

ShowMessage (lb->Strings[ListBox1->ItemIndex]);<br />

ListBox1->Sorted = true;<br />

ListBox1->ItemIndex = 1;<br />

}<br />

void ShowStr (TStringList *z) {<br />

AnsiString s;<br />

for (int i=0; iCount; ++i) s+= z->Strings[i] + ‘\n’;<br />

ShowMessage (s);<br />

}


43<br />

Структуры и объединения<br />

Упростить представление и групповую обработку разнотипных данных<br />

помогают структуры (structure). Их объявления:<br />

struct имяТипаСтруктуры { объявлениеПолей };<br />

struct имяСтруктуры { объявлениеПолей } [=списокЗначений ];<br />

struct имяТипаСтруктуры имяСтруктуры [={ списокЗначений }];<br />

где объявление полей имеет формат<br />

Тип имяПоля;…<br />

Как и любой тип, структура представляет абстрактную категорию, характеризующую<br />

размер памяти и способ обработки данных. Для работы с<br />

данными создаются переменные:<br />

имяТипаСтруктуры списокИменСтруктур;<br />

Структуры инициализируют, подобно классам:<br />

имяСтруктуры.имяПоля = Значение;<br />

указательНаСтруктуру->имяПоля = Значение;<br />

Имена полей уникальны в пределах структуры. Поля поддерживают<br />

все операции, характерные для их типов. Структуры и объединения могут<br />

вкладываться друг в друга. Примеры:<br />

void Structures1 () {<br />

struct Student {<br />

AnsiString firstName;<br />

int birthYear;<br />

double scholarship;<br />

};<br />

Student example;<br />

example.firstName = MyForm->Edit1->Text ;<br />

example.birthYear = StrToInt (MyForm->Edit2->Text);<br />

example.scholarship = StrToFloat (MyForm->Edit3->Text) ;<br />

MyForm->StringGrid1->Cells[1][1] = example.firstName;<br />

MyForm->StringGrid1->Cells[2][1] = example.birthYear;<br />

MyForm->StringGrid1->Cells[3][1] = example.scholarship;<br />

}


void Structures2 () {<br />

TStringGrid *sg = StringGrid1;<br />

int rows = sg->RowCount;<br />

struct s {int n; AnsiString nm;};<br />

s group;<br />

try {<br />

for (int i=0; iCells[0][i]);<br />

group.nm = sg->Cells[1][i];<br />

ShowMessage (IntToStr (group.n) + ' ' + group.nm);<br />

}<br />

} catch (...) { ShowMessage ("Заполнить ячейки"); }<br />

}<br />

44<br />

Следующий пример посвящен пузырьковой сортировке строк String-<br />

Grid с использованием структуры:<br />

void Structures3 () {<br />

TStringGrid *sg = StringGrid1;<br />

int rows = sg->RowCount;<br />

struct s { AnsiString name; int year; };<br />

s temp;<br />

for (int i=1; ii; --j)<br />

if (sg->Cells[0][j] < sg->Cells[0][j-1]) {<br />

temp.name = sg->Cells[0][j];<br />

sg->Cells[0][j] = sg->Cells[0][j-1];<br />

sg->Cells[0][j-1] = temp.name;<br />

temp.year = StrToInt (sg->Cells[1][j]);<br />

sg->Cells[1][j] = sg->Cells[1][j-1];<br />

sg->Cells[1][j-1] = temp.year;<br />

}<br />

}<br />

При передаче в функцию в качестве аргументов могут использоваться<br />

как имена полей, так и указатели на адреса. Применение указателя в качестве<br />

значения аргумента избавляет от необходимости копирования в функцию<br />

всех данных, но в этом случае они остаются беззащитными перед изменениями<br />

в теле функции. Для устранения этой проблемы перед указателем<br />

можно помещать лексему const. Пример передачи данных структуры:<br />

struct x {int size, price;};<br />

void Change1 (x y) { y.size = y.price; }<br />

void Change2 (x *y) { y->size = y->price;}


45<br />

void Structures4 () {<br />

x y = {1, 2};<br />

Change1 (y);<br />

MyForm->Edit1->Text = y.size - y.price;<br />

Change2 (&y);<br />

MyForm->Edit2->Text = y.size - y.price;<br />

}<br />

Близкие к структурам по смыслу объединения (union) позволяют хранить<br />

в одной и той же области памяти значения переменных разных типов,<br />

поочередно заменяя одно другим. Размер памяти, занимаемой данными объединения,<br />

назначается в соответствии с тем из типов, который требует больше<br />

памяти. Их объявление и пример:<br />

union имяТипаОбъединения {объявлениеПолей} имяОбъединения<br />

[={списокЗначений}];<br />

void Unions () {<br />

union intDbl {int i; double x;} id = {70};<br />

MyForm->Edit1->Text = "Масса в граммах" + IntToStr (id.i);<br />

id.x = 0.07;<br />

MyForm->Edit2->Text = "Масса в килограммах" + FloatToStr (id.x);<br />

}<br />

Динамические структуры данных<br />

В отличие от составных типов, динамические структуры данных (dynamic<br />

data structure), или связные списки (linked list), изменяются в размере по<br />

мере заполнения и освобождения их от данных и обычно не требуют последовательного<br />

размещения элементов в памяти компьютера. Динамические<br />

структуры не входят в состав типов данных традиционных языков программирования<br />

и присутствуют в них как классы. Они, как правило, разрабатываются<br />

программистами или берутся из библиотек – контейнеров. Каждая<br />

структура характеризуется порядком заполнения (allocation) и порядком обхода<br />

(traversal).<br />

Некоторые характеристики классов динамических структур C++Builder<br />

приведены в табл. 12:


46<br />

Свойства и методы контейнерных классов C++Builder<br />

Таблица 13.<br />

Имя Член Классы Назначение<br />

Push Метод TStack, TQueue Добавляет член в вершину<br />

Pop Метод TStack, TQueue Удаляет члены из вершины<br />

Peek Метод TStack, TQueue Указатель на вершину<br />

Count Метод TStack, TQueue Возвращает число членов (размер)<br />

AtLeast Метод TStack, TQueue Проверяет размер<br />

Capacity Свойство TList Максимальный размер<br />

Count Свойство TList Фактический размер<br />

Items Свойство TList Указатель на член<br />

List Свойство TList Указатель на список членов<br />

First Метод TList Указатель на первый член<br />

Last Метод TList Указатель на последний член<br />

Add Метод TList Добавляет член в вершину<br />

Insert Метод TList Добавляет член в заданное место<br />

Delete Метод TList Удаляет член с указанным индексом<br />

Remove Метод TList Удаляет указанный член<br />

Clear Метод TList Удаляет все члены<br />

Pack Метод TList Удаляет пустоты<br />

Exchange Метод TList Меняет местами два члена<br />

Move Метод TList Перемещает указанный член<br />

Expand Метод TList Увеличивает максимальный размер<br />

Sort Метод TList Сортирует члены<br />

Наиболее распространенной динамической структурой является стек<br />

(stack). Это последовательность данных, в которой помещение и выборка<br />

информации выполняется с одного конца, именуемого вершиной стека (top).<br />

Иными словами, стек обрабатывает данные по принципу «последним вошел<br />

– первым вышел» (LIFO – Last Input, First Output). Примеры:<br />

void __fastcall TForm1::STACKClick (TObject *Sender) {<br />

AnsiString a[] = { "Иванов", "Петров", "Сидоров" }, b = "0\n";<br />

TStack *s = new TStack;<br />

for (int i=0; iPush (a[i].c_str ());<br />

b += AnsiString (s->Count ()) + ' ' + (char*) s->Peek () + '\n';<br />

}<br />

while (s->AtLeast (1))<br />

b += AnsiString (s->Count ()) + ' ' + (char*) s->Pop () + '\n';<br />

ShowMessage (b + '0');<br />

// 0 1 Иванов 2 Петров 3 Сидоров<br />

} // 3 Сидоров 2 Петров 1 Иванов 0


47<br />

void SimpleStack() {<br />

TStack *vStack = new TStack;<br />

vStack->Push ("Пеpвое ");<br />

vStack->Push ("Втоpое "); vStack->Push ("Тpетье ");<br />

ShowMessage ("Добавили " + AnsiString (vStack->Count ()));<br />

vStack->Pop (); ShowMessage ("Осталось " + AnsiString (vStack->Count ()));<br />

}<br />

Далее приведен пример разработки символьного стека:<br />

const int ARRAYSIZE = 1000;<br />

struct vStack { char s[ARRAYSIZE]; int top; };<br />

void Reset (vStack *stk) { stk->top = -1; }<br />

int Empty (const vStack *stk) { return stk->top == -1; }<br />

int Full (const vStack *stk) { return stk->top == ARRAYSIZE - 1; }<br />

void Push (char *str, vStack *stk) {<br />

int i = 0; while (str[i]) if (!Full (stk)) stk->s[++stk->top] = str[i++];<br />

}<br />

void Pop (vStack *stk) {<br />

AnsiString result;<br />

while (!Empty (stk)) result += AnsiString (stk->s[stk->top--]) + '\n';<br />

ShowMessage (result);<br />

}<br />

void MyStack () {<br />

vStack s;<br />

Reset (&s);<br />

Push ("Пеpвое ", &s); Push ("Втоpое ", &s); Push ("Тpетье ", &s); Pop (&s);<br />

}<br />

Очередью (queue) называют динамическую структуру, в которой помещение<br />

данных выполняется с одного конца – входа очереди (input), а выборка<br />

– с другого конца – выхода очереди (output) по принципу «первым вошел<br />

– первым вышел» (FIFO – First Input, First Output):<br />

void __fastcall TForm1::QUEUEClick (TObject *Sender) {<br />

AnsiString a[] = { "Иванов", "Петров", "Сидоров" }, b = "0\n";<br />

TQueue *q = new TQueue;<br />

for (int i=0; iPush (a[i].c_str());


48<br />

b += AnsiString (q->Count ()) + ' ' + (char*) q->Peek () + '\n';<br />

}<br />

while (q->AtLeast (1))<br />

b += AnsiString (q->Count ()) + ' ' + (char*) q->Pop () + '\n';<br />

ShowMessage (b + '0');<br />

// 0 1 Иванов 2 Иванов 3 Иванов<br />

} // 3 Иванов 2 Петров 1 Сидоров 0<br />

void SimpleQueue () {<br />

TQueue *vQueue = new TQueue;<br />

vQueue->Push ("Пеpвое ");<br />

vQueue->Push ("Втоpое "); vQueue->Push ("Тpетье ");<br />

ShowMessage ("Добавили " + AnsiString (vQueue->Count ()));<br />

vQueue->Pop ();<br />

ShowMessage ("Осталось " + AnsiString (vQueue->Count ()));<br />

}<br />

Списком называется динамическая структура, в которой поддерживаются<br />

помещение и выборка данных в произвольных позициях. Каждый член<br />

списка, именуемый узлом (node), содержит поле данных и поле–указатель<br />

следующего узла.<br />

Список называют линейным (linear list), если обход его выполняется<br />

только в одном направлении – от предшествующего узла к последующему,<br />

начиная с первого узла и кончая последним. Список называют циклическим<br />

(cyclic list), если последний узел его содержит указатель на первый узел; в<br />

нем обход выполняется также в одном направлении, но из любого узла можно<br />

достичь любой другой узла. Пример:<br />

void MyList () {<br />

TList *vList = new TList ();<br />

AnsiString item = "что-то есть";<br />

try {<br />

vList->Add (item.c_str ());<br />

ShowMessage ("В списке " + IntToStr (vList->Count));<br />

vList->Remove (item.c_str ());<br />

ShowMessage ("В списке " + IntToStr (vList->Count));<br />

}<br />

catch (...) { delete vList; throw; }<br />

delete vList;<br />

}<br />

void Info (TList *vl) {<br />

AnsiString b;<br />

for (int i=0; iCount; ++i) { b += AnsiString (i) + ' ' + (char*) vl->Items[i] + '\n';<br />

}


ShowMessage ("В списке: " + IntToStr (vl->Count) + '\n' + b);<br />

}<br />

void __fastcall TForm1::LISTClick (TObject *Sender){<br />

AnsiString a[] = {"Иванов", "Петров", "Сидоров", "Смирнов"};<br />

TList *vList = new TList;<br />

try {<br />

vList->Capacity = 10;<br />

for (int i=0; iAdd (a[i].c_str ());<br />

Info (vList); // В списке 4: 0 Иванов 1 Петров 2 Сидоров 3 Смирнов<br />

49<br />

vList->Remove (vList->Items[1]); vList->Delete (1);<br />

Info (vList); // В списке 2: 0 Иванов 1 Смирнов<br />

vList->Insert (1, a[0].c_str ());<br />

Info (vList); // В списке 3: 0 Иванов 1 Иванов 2 Смирнов<br />

ShowMessage ("Свободно " + IntToStr (vList->Capacity-vList->Count)); // 7<br />

vList->Pack ();<br />

ShowMessage ("Свободно " + IntToStr (vList->Capacity-vList->Count)); // 7<br />

} catch (...) { delete vList; throw; }<br />

delete vList;<br />

}<br />

Пример разработки списка:<br />

struct vList { int value; vList *p; };<br />

vList *current, *last, *first;<br />

int Get () {<br />

static value;<br />

int val = MessageDlg ("Продолжать?", mtConfirmation,<br />

TmsgDlgButtons () p = 0;<br />

if (last) last->p = current; else first = current;<br />

last = current;<br />

}<br />

}


50<br />

void Info () {<br />

AnsiString result;<br />

if (first) {<br />

result = "Список: ";<br />

current = first;<br />

do { result += AnsiString (current->value) + ' '; current = current->p; }<br />

while (current);<br />

result += " Конец";<br />

}<br />

ShowMessage (result);<br />

}<br />

void MyList () { Add (); Info (); Add (); Info (); }<br />

Другой пример посвящен двухмерному списку. Он демонстрирует технику<br />

выделения и освобождения памяти динамической структуре данных:<br />

struct vMatr { double **base; int row, column; };<br />

void Allocate (int r, int s, vMatr &m) {<br />

m.base = new double *[s];<br />

for (int i=0; i


int i, j, k[] = {2, 3, 4, 2}; // заданы случайные размеры массовов<br />

Allocate (k[0], k[1], book1); Allocate (k[2], k[3], book2);<br />

for (i=0; i


52<br />

прямом порядке обхода (depth-first traversal) («просмотре в глубину») движение<br />

начинается от корня и выполняется от родителей сначала к левым потомкам,<br />

а затем к правым. При обратном порядке (breadth-first traversal) обход<br />

начинается с листьев и выполняется от потомков к родителям слева направо,<br />

завершаясь в корне. Наиболее распространенный обход в симметричном порядке<br />

(simmetrical traversal) направлен от левого потомка через родителя к<br />

правому потомку от листьев к корню.<br />

Пример двоичного дерева:<br />

struct vTree { char *data; vTree *left; vTree *right; };<br />

vTree *root;<br />

void Search (vTree **tree, const char *s);<br />

AnsiString Process (vTree *node);<br />

AnsiString InOrder (vTree *node);<br />

void MyTree () {<br />

char s[128];<br />

// можно сортировать любые массивы<br />

randomize();<br />

for (int i=1; idata = strdup (s); p->left = 0; p->right = 0; *tree = p;<br />

}<br />

else {<br />

p = *tree;<br />

cmpResult = strcmp (s, p->data); // сравнение строк<br />

if (cmpResultleft, s);<br />

else if (cmpResult>0) Search (&p->right, s);<br />

else ShowMessage ("Такой член уже есть");<br />

}<br />

}<br />

AnsiString Process (vTree *node) {<br />

static AnsiString result;<br />

result += AnsiString (node->data) + ' ';


53<br />

return result;<br />

}<br />

AnsiString InOrder (vTree *node) {<br />

AnsiString result;<br />

If (node != 0) {<br />

InOrder(node->left);<br />

result = Process (node);<br />

InOrder (node->right);<br />

}<br />

return result;<br />

}<br />

Введение в модульное программирование<br />

«Требуется гораздо больше ума, чтобы передать<br />

свои мысли, чем чтобы их иметь»<br />

Из Гельвеция<br />

Работа с текстовыми и графическими файлами<br />

Файл (file) представляет именованную область накопителя, предназначенную<br />

для хранения, передачи и приема структурированной информации.<br />

Число одновременно открытых файлов и их размер определяются возможностями<br />

операционной системы и ресурсами компьютера.<br />

Ряд визуальных компонентов поддерживает файловый обмен своими<br />

методами SaveToFile и LoadFromFile. К ним относятся классы TStrings, TPicture,<br />

TOLEGraphic, TGraphic и производные от них классы. Рассмотрим несколько<br />

примеров:<br />

Для загрузки и сохранения строк в текстовом файле используются методы<br />

SaveToFile и LoadFromFile класса TStringList, организующие обмен таким<br />

образом, что каждая строка файла соответствует строке списка. Это<br />

применяется в заполнении из файлов элементов управления класса Memo или<br />

в сохранении содержимого элементов управления классов List и ComboBox,<br />

например:<br />

void StringLists1 () {<br />

AnsiString file = "test.INI";<br />

Form1->Memo1->Lines->LoadFromFile (file);<br />

Form1->Memo1->Lines->SaveToFile (ChangeFileExt (file,".BAK"));<br />

}


Списки в файл и из файла<br />

void __fastcall TForm1::StringListFilesClick (TObject *Sender) {<br />

AnsiString fName = "D:\\examples\\tempFile.txt";<br />

TStringList *sl = new TStringList;<br />

sl->Add ("раз"); sl->Add ("два"); sl->Add ("три"); sl->SaveToFile (fName);<br />

sl->Clear (); sl->LoadFromFile (fName);<br />

ListBox1->Items->LoadFromFile (fName); ComboBox1->Items->Assign (sl);<br />

delete sl;<br />

}<br />

// Строки в файл и из файла<br />

void __fastcall TForm1::ListFilesClick (TObject *Sender) {<br />

AnsiString fName ="tempFile.txt";<br />

StringListFilesClick (Sender);<br />

struct s { AnsiString as; int i; };<br />

s myS[3];<br />

for (int j=0; jItems->Count; ++j) {<br />

myS[j].as = ListBox1->Items->Strings[j]; myS[j].i = j;<br />

}<br />

for (int j=0; jCells[0][j] = myS[j].as; StringGrid1->Cells[1][j] = myS[j].i;<br />

}<br />

StringGrid1->Cols[0]->SaveToFile (fName);<br />

Memo1->Lines->LoadFromFile (fName);<br />

RichEdit1->Lines->LoadFromFile (fName);<br />

}<br />

// Картинки из файлов<br />

void __fastcall TForm1::GraphFilesClick (TObject *Sender){<br />

AnsiString fName = "D:\\examples\\scan.bmp";<br />

Image1->Picture->LoadFromFile (fName);<br />

Form1->Icon->LoadFromFile ("scan.ico");<br />

Graphics::TBitmap *gb = new Graphics::TBitmap;<br />

gb->LoadFromFile (fName); PaintBox1->Canvas->Draw (0, 0, gb);<br />

this->Canvas->CopyMode = cmSrcPaint;<br />

this->Canvas->CopyRect<br />

(ClientRect, PaintBox1->Canvas, PaintBox1->ClientRect);<br />

delete gb;<br />

}<br />

// Кнопки с картинками из файла<br />

void __fastcall TForm1::BtnFilesClick (TObject *Sender) {<br />

AnsiString fName = "D:\\examples\\scan.bmp";<br />

BitBtn1->Glyph->LoadFromFile (fName);<br />

54


55<br />

SpeedButton1->Glyph->LoadFromFile (fName);<br />

}<br />

Для проведения файлового обмена без рассмотренных компонентов в<br />

C++Builder используются функции, приведенные в табл. 13. Работа с файлом<br />

ведется с помощью файловых переменных (file variable), связываемых с конкретными<br />

файлами. Файл открывается для чтения, записи или других операций.<br />

После работы файл следует закрыть.<br />

Таблица 14.<br />

<strong>Функции</strong> файлового обмена<br />

Функция<br />

Int FileCreate (AnsiString name)<br />

Int FileOpen (AnsiString name, int<br />

mode)<br />

Int FileWrite (int handle, void<br />

*buffer, int count)<br />

Int FileRead (int handle, void<br />

*buffer, int count)<br />

void FileClose(int handle)<br />

int FileSeek (int handle, int offset,<br />

int origin)<br />

bool DeleteFile (AnsiString name)<br />

bool FileExists (AnsiString name)<br />

bool DirectoryExists (AnsiString<br />

name)<br />

bool ForceDirectories (AnsiString<br />

dir)<br />

AnsiString FileSearch (AnsiString<br />

name, AnsiString dir)<br />

Назначение<br />

Создает новый или переписывает существующий<br />

файл с заданным именем, возвращая файловую переменную.<br />

Открывает файл в режимах (mode) fmOpenRead<br />

(чтение), fmOpenWrite (запись), fmOpenReadWrite<br />

(чтение-запись), возвращая файловую переменную.<br />

Пишет count байт в файл handle из *buffer, возвращая<br />

число записанных байтов<br />

Читает count байт из файла handle в *buffer, возвращая<br />

число прочитанных байтов.<br />

Закрывает файл.<br />

Позиционирует указатель в файле со смещением<br />

(offset) относительно базы (origin), равной 0 (начало),<br />

1 (текущая позиция указателя) или 2 (конец<br />

файла), возвращая позицию указателя в файле.<br />

Удаляет файл.<br />

Проверяет наличие файла.<br />

Проверяет наличие папки.<br />

Создает папки.<br />

Выполняет поиск файла name в папках dir.<br />

Ниже даны примеры организации файлового обмена.<br />

Первый простой пример представляем вывод в файл содержания полей<br />

Edit1 и Edit2:<br />

void __fastcall TForm1::FieldsToFileClick (TObject *Sender) {<br />

AnsiString a = Edit1->Text,<br />

b = Edit2->Text;<br />

int f = FileCreate ("tempFile.txt");<br />

if (f != -1) {<br />

FileWrite (f, a.c_str (), a.Length ());<br />

FileWrite (f, b.c_str (), b.Length ());


FileClose (f);<br />

}<br />

}<br />

56<br />

Во втором примере создается файл, в него заносятся два числа, и он закрывается.<br />

Затем файл открывается для чтения и возвращается его содержимое.<br />

void Files1 () {<br />

AnsiString name = "test.dat";<br />

if (FileExists (name))<br />

if (Application->MessageBoxA ("Переписать файл?",<br />

"Есть такой файл", MB_YESNO | MB_ICONQUESTION)!=IDYES) return;<br />

int iFile = FileCreate (name);<br />

if (iFile == -1) { ShowMessage ("Не создать файл"); return; }<br />

AnsiString first = MyForm->Edit1->Text;<br />

AnsiString second = MyForm->Edit2->Text;<br />

char *firstStr = first.c_str (), *secondStr = second.c_str ();<br />

int err = FileWrite (iFile, firstStr, first.Length ());<br />

if (err == -1) { ShowMessage ("Не записать файл"); return;}<br />

err = FileWrite (iFile, secondStr, second.Length ());<br />

if (err == -1) { ShowMessage ("Не дописать файл"); return; }<br />

FileClose (iFile);<br />

iFile = FileOpen (name, fmOpenRead);<br />

if (iFile == -1) { ShowMessage ("Не открыть файл"); return; }<br />

int readLen = first.Length () + second.Length ();<br />

char *getStr;<br />

getStr = new char[readLen + 1];<br />

err = FileRead (iFile, getStr, readLen);<br />

if (err == -1) { ShowMessage ("Не прочитать файл"); return; }<br />

FileClose (iFile);<br />

}<br />

ShowMessage (AnsiString (getStr, readLen));<br />

В третьем примере после открытия первого файла определяется его<br />

размер, указатель возвращается в начало, и в массив читается его содержимое.<br />

Затем открывается второй файл для записи в него содержимого массива.


void Files2 () {<br />

AnsiString from = MyForm->Edit1->Text, to = MyForm->Edit2->Text;<br />

try {<br />

57<br />

int iFile = FileOpen (from, fmOpenRead);<br />

int len = FileSeek (iFile, 0, 2);<br />

FileSeek (iFile, 0, 0);<br />

char *c;<br />

c = new char [len + 1];<br />

len = FileRead (iFile, c, len);<br />

FileClose (iFile);<br />

iFile = FileOpen (to, fmOpenWrite);<br />

FileWrite (iFile, c, len);<br />

}<br />

ShowMessage (from + " -> " + to + '\n' + AnsiString (c, len));<br />

delete [] c;<br />

FileClose (iFile);<br />

} catch (...) { ShowMessage ("Ошибка одной из файловых операций"); }<br />

Аналогичный пример посвящен отображению содержания файла в<br />

надписи Label1:<br />

void __fastcall TForm1::LabelFromFileClick (TObject *Sender) {<br />

int f = FileOpen ("tempFile.txt", fmOpenRead);<br />

if (f != -1) {<br />

int size = FileSeek (f, 0, 2);<br />

char *s = new char[size+1];<br />

FileSeek (f, 0, 0);<br />

FileRead (f, s, size);<br />

Label1->Caption = AnsiString (s, size);<br />

FileClose (f);<br />

delete [] s;<br />

}<br />

}<br />

Далее показано, как файл с текстом, указанным в Label1, дополняется<br />

содержимым поля Edit1:<br />

void __fastcall TForm1::InsertToFileClick (TObject *Sender) {<br />

int f = FileOpen ("tempFile.txt", fmOpenWrite);<br />

if (f != -1) {<br />

AnsiString is = Label1->Caption, what = Edit1->Text;<br />

is += what;


Label2->Caption = is;<br />

FileWrite (f, is.c_str (), is.Length ());<br />

FileClose (f);<br />

}<br />

}<br />

58<br />

Следующий пример посвящен изменению содержимого файла. Вначале<br />

текст файла считывается в массив, который преобразуется в строку. В этой<br />

строке отыскивается заданное слово и заменяется фразой «[Удалено]», а<br />

файл заполняется пробелами. После этого измененная строка возвращается в<br />

файл.<br />

void Files3 () {<br />

AnsiString word = MyForm->Edit1->Text,<br />

file = MyForm->Edit2->Text,<br />

bText ="";<br />

try {<br />

int bFile = FileOpen (file, fmOpenReadWrite);<br />

int bLen = FileSeek (bFile, 0, 2);<br />

char *b = new char [bLen];<br />

FileSeek (bFile, 0, 0);<br />

FileRead (bFile, b, bLen);<br />

bText =AnsiString (b, bLen);<br />

delete [] b;<br />

int index = bText.AnsiPos (word.c_str());<br />

if (!index) {<br />

ShowMessage ("В файле нет такого слова");<br />

FileClose (bFile); return;<br />

}<br />

bText.Delete (index, word.Length ());<br />

bText.Insert ("[Удалено]", index);<br />

FileSeek (bFile, 0, 0);<br />

AnsiString c = AnsiString::StringOfChar (' ', bLen);<br />

FileWrite (bFile, c.c_str (), bLen);<br />

FileSeek (bFile, 0, 0);<br />

FileWrite (bFile, bText.c_str (), bText.Length ());<br />

FileClose (bFile);


} catch (...) { ShowMessage ("Ошибка файловой операции"); }<br />

ShowMessage (bText);<br />

}<br />

59<br />

Пример удаления фрагмента, заданного в Edit1, из файла, в котором содержится<br />

указанная в Label1 фраза:<br />

void __fastcall TForm1::DelFromFileClick (TObject *Sender) {<br />

AnsiString is = Label1->Caption, what = Edit1->Text, was = is;<br />

int f = is.AnsiPos (what.c_str ());<br />

ShowMessage (is+ ' ' +IntToStr (is.Length ()));<br />

If (f) is.Delete (f, what.Length ());<br />

ShowMessage (is + ' ' + IntToStr (is.Length ()));<br />

Label3->Caption = is;<br />

f = FileOpen ("tempFile.txt", fmOpenWrite);<br />

if (f != -1) {<br />

is += AnsiString::StringOfChar (' ', was.Length ());<br />

FileWrite (f, is.c_str (), is.Length ());<br />

FileClose (f);<br />

}<br />

}<br />

Еще один пример демонстрирует процесс поиска файла, в том числе – с<br />

символом подстановки *, в текущей папке и в папке Windows:<br />

void Files4 () {<br />

char buffer[256];<br />

}<br />

GetWindowsDirectory (buffer, sizeof (buffer));<br />

AnsiString file = FileSearch (MyForm->Edit1->Text, GetCurrentDir() +<br />

";" + AnsiString (buffer));<br />

if (file.IsEmpty ()) ShowMessage ("Нет файла " + MyForm->Edit1->Text);<br />

else ShowMessage ("Есть файл " + file + " !");<br />

Работа с двоичными файлами<br />

Разработано множество способов запуска исполнительных файлов из<br />

приложений C++.<br />

В файле заголовка PROCESS.H объявлена группа функций семейства<br />

exec… Эти функции загружают в память и открывают исполнительные файлы<br />

(дочерние процессы), одновременно лишая ресурсов и прекращая родительский<br />

процесс. Первым параметром этих функций всегда является путь к


60<br />

вызываемому дочернему процессу, а при запуске каждая такая функция передает<br />

несколько дополнительных параметров этому процессу:<br />

Например:<br />

exec…(путь, параметры).<br />

execl (“MyFile.exe”, “MyFile.exe”, NULL);<br />

Суффиксы l, v, p, e, добавляемые к имени exec, раскрывают требования<br />

к параметрам. Так, l свидетельствует о постоянном фиксированном числе параметров,<br />

v говорит о непостоянном массиве указателей в списке параметров,<br />

p указывает, что путь следует искать в переменной окружения path, а e – в<br />

списке параметров окружения дочернего процесса. В случае неудачного запуска<br />

exec… возвращает –1, а глобальная переменная errno устанавливается<br />

в одно из следующих значений: EACCESS – отказ в доступе, EMFILE – много<br />

открытых файлов, ENOENT – не найден путь, ENOEXEC – мало памяти.<br />

В файле заголовка SHELLAPI.H объявлена серия полезных функций<br />

запуска приложений без закрытия родительского процесса. Среди них – Win-<br />

Exec, ShellExecute, CreateProcess и другие. В числе их параметров всегда<br />

присутствует режим окна, который задается константами SW_MINIMIZE,<br />

SW_MAXIMIZE, SW_NORMAL, SW_RESTORE, SW_HIDE. Примеры:<br />

WinExec (“MyFile”, SW_RESTORE);<br />

ShellExecute (Handle, NULL, “MyFile.exe”, NULL, NULL, SW_RESTORE);<br />

ShellExecute (Handle, “open”, “File.doc”, NULL, NULL, SW_RESTORE);<br />

К двоичным файлам относятся, также, растровые файлы, значки, курсоры<br />

и файлы ресурсов (.RES). Все они просматриваются, создаются и редактируются<br />

графическим редактором Image Editor, доступным через меню<br />

Tools. Редактор достаточно прост в использовании и во многом напоминает<br />

известный редактор Microsoft Paint.<br />

Модули DLL и пакеты<br />

Одним из самых распространенных способов создания модульных приложений<br />

являеся использование библиотек динамической компоновки DLL и<br />

BPL. Такие библиотеки представляют особый вид выполняемого кода (компилированных<br />

функций, констант, форм, ресурсов), хранящийся в файлах с<br />

расширениями .DLL и .BPL, которые рассчитаны на одновременное использование<br />

несколькими программами. Пакеты (package) BPL оснащены средствами<br />

работы с компонентами VCL. Они располагают большей, чем DLL,<br />

информацией о вызывающем приложении, а приложение, в свою очередь,


61<br />

лучше контролирует поведение связанного модуля. К сожалению BPL обслуживают<br />

только приложения C++Builder, тогда как DLL более универсальны<br />

и работают с любыми приложениями в разных операционных системах.<br />

Модули DLL и BPL можно подключать к приложению статически (design<br />

time mode) и динамически (runtime mode). Статически загружаемые модули<br />

связываются с главной программой при ее компоновке и остаются загруженными<br />

в память все время, пока выполняется приложение. Такие модули<br />

содержат экспортируемые функции, описание которых помещается в файлы<br />

библиотек импорта LIB – Import Library File, которые также подключаются<br />

на этапе компоновки. Динамически загружаемые модули в процессе работы<br />

при необходимости можно выгружать из памяти, освобождая при этом<br />

ресурсы. В ряде режимов возможна работа таких приложений и без них. Дополнительные<br />

библиотеки импорта здесь не используются.<br />

Библиотека DLL создается командой File .New из шаблона DLLWizard.<br />

В состав проекта автоматически включаются файл проекта (например,<br />

myDLL.BPR) и файл модуля DLL (myDLL.CPP) без формы, которые следует<br />

дополнить файлом заголовка (myDLL.H) и при необходимости – файлами<br />

требуемых форм. Пример файла модуля, формирующего произвольное сообщение<br />

авторской функцией Message и открывающего форму О программе<br />

другой функцией About:<br />

//myDLL.CPP<br />

#include "myDLL.h"<br />

int WINAPI DllEntryPoint (HINSTANCE hinst,<br />

unsigned long reason, void* lpReserved) { return 1; }<br />

void Message (char *s) { ShowMessage ((AnsiString) s + " из DLL"); }<br />

void About (char *s) {<br />

Form1 = new TForm1 (0); Form1->Label1->Caption = (AnsiString) s + " из<br />

DLL";<br />

Form1->ShowModal (); delete Form1;<br />

}<br />

Функция DllEntryPoint является главной функцией DLL. <strong>Функции</strong> Message и<br />

About здесь служат примерами библиотечных функций, которые будут подключаться<br />

к приложению с выводом передаваемой оттуда строки. Объявление<br />

этих функций как функций экспорта следует поместить в файл заголовка:<br />

extern "C" void __declspec (dllexport) Message (char *s);<br />

extern "C" void __declspec (dllexport) About (char *s);


62<br />

Лексема __declspec со спецификатором класса памяти dllexport гарантирует<br />

совместимость библиотеки с Microsoft C/C++. После компоновки проекта будет<br />

создан файл библиотеки (.LIB) и исполняемый файл (.DLL).<br />

Для статического подключения DLL к имеющемуся проекту надо добавить<br />

в него файл .LIB (Project .Add to Project) из проекта DLL и включить<br />

файл заголовка из DLL. После компоновки можно обращаться к хранящимся<br />

там данным. Например:<br />

//myApplication.cpp<br />

#include "myDLL.h"<br />

void __fastcall TForm1::Button1Click (TObject *Sender) { Message ("Привет "); }<br />

void __fastcall TForm1::Button2Click (TObject *Sender) { About ("Привет "); }<br />

Динамическое подключение выполняется иначе:<br />

void __fastcall TForm1::Button1Click (TObject *Sender) {<br />

HINSTANCE dllp = LoadLibrary ("myDLL.dll");<br />

if (dllp) {<br />

void (__stdcall *p) (char *s);<br />

p = (void (__stdcall *) (char*)) GetProcAddress (dllp,"_Message");<br />

if (p) p ("Привет ");<br />

else ShowMessage ("Нет функции DLL");<br />

FreeLibrary (dllp);<br />

}<br />

else ShowMessage ("Нет файла DLL");<br />

}<br />

Функция LoadLibrary возвращает тип HINSTANCE из библиотеки Windows<br />

API и загружает модуль DLL, либо NULL при его отсутствии. GetProcAddress<br />

присваивает значение указателя функции DLL. Затем вызывается рабочая<br />

функция, а библиотека DLL выгружается из памяти функцией FreeLibrary.<br />

Аналогичным образом экспортируются целые классы DLL.<br />

Пакеты BPL создаются на базе шаблона Package. При проектировании<br />

пакета используются файлы со следующими расширениями:<br />

.BPK – исходный файл параметров проекта в формате XML;<br />

.BPL – runtime-пакет, представляющий разновидность .DLL со специальными<br />

функциями C++Builder;<br />

.BPI – библиотека импорта пакета;<br />

.CPP – файл исходного текста с главной функцией проекта DllEntry-<br />

Point;<br />

.H – файл заголовка;


63<br />

.LIB – статическая библиотека (коллекция файлов OBJ) для подключения<br />

в режиме design time, если установлен параметр компиляции GI (Generate<br />

LIB file);<br />

.OBJ – двоичный образ модуля пакета.<br />

Для динамического подключения пакетов вызывающее приложение<br />

следует оснащать функциями LoadPackage и FreePackage вместо LoadLibrary<br />

и FreeLibrary.<br />

Ресурсы<br />

Ресурсы создаются графическим редактором Image Editor и там же<br />

включаются в файл ресурсов .RES приложения как Bitmaps, Cursors и Icons.<br />

Для работы с обновленным файлом ресурсов следует закрыть приложение,<br />

затем вновь открыть и перестроить его. Пример:<br />

const Tcursor myCursor = 5;<br />

void __fastcall TForm1::FormCreate (TObject *Sender) {<br />

Screen->Cursor[myCursor] = LoadCursor (HInstance “имяРесурсаКурсора”);<br />

Cursor = myCursor;<br />

Icon->Handle = LoadIcon (HInstance, “имяРесурсаЗначка”);<br />

}<br />

Приложения баз данных<br />

«У памяти хороший вкус»<br />

Французское изречение<br />

Технологии создания приложений баз данных<br />

C++Builder создавать эффективные СУБД – системы управления базами<br />

данных (DBMS). Для поддержки таких приложений разработаны различные<br />

технологии, приведенные в табл. 14.<br />

Таблица 15.<br />

Варианты архитектур приложений баз данных<br />

Технология<br />

ADO<br />

BDE<br />

dbExpress<br />

Область применения<br />

Все базы данных, поддерживающие стандарт OLE DB<br />

Реляционные базы данных xBase, Paradox, InterBase<br />

Различные базы данных<br />

Универсальная технология ADO – ActiveX Database Objects – разработана<br />

в Microsoft в соответствии со стандартом OLE DB (Object Linking and<br />

Embedding of DataBases). Она является развитием ODBC (Open DataBase


64<br />

Connectivity) – открытой двухуровневой системы подключения баз данных,<br />

ставшей стандартом для большинства приложений. Верхний уровень (клиент<br />

ODBC) играет роль интерфейса с приложением и поддерживает стандартные<br />

функции API. Нижний уровень выполняет функции специализированного<br />

драйвера преобразования информации в соответствии с форматом конкретной<br />

СУБД (сервера ODBC). Средства поддержки ADO строятся как модели<br />

составных объектов COM (Component Object Model). Технология ориентирована<br />

на Интернет (HTTP, InternetExpress, FastNet) и сети, работающие под<br />

Windows, при взаимодействии с любыми СУБД, оснащенными драйверами<br />

ODBC. Кроме того, ADO предлагает разработчикам поддержку данных, хранящихся<br />

в не реляционном формате, например XML или сообщений электронной<br />

почты. Специальные версии ADO относятся к категории распределенных<br />

(многоуровневых) архитектур. В них программы разделяются на секции,<br />

взаимодействующие на основе разнообразных протоколов. Это CORBA<br />

для построения масштабируемых приложений, не зависящих от платформ;<br />

DCOM для реализации удаленного доступа; MIDAS для обслуживания приложений<br />

клиент/сервер.<br />

Базовая одноуровневая технология BDE – Borland Database Engine поддерживает<br />

форматы баз данных группы xBase, такие как dBase, FoxPro, Clipper,<br />

а также Paradox, текстовый формат с разделителями (ASCII-Delimited<br />

Text) и стандарт ODBC. Реализованная в виде набора системных файлов<br />

DLL, технология BDE удовлетворяет небольшим дешевым производительным<br />

приложениям. Для работы с корпоративными базами данных Oracle,<br />

SQL Server, DB2, InterBase между BDE и базой данных может быть помещен<br />

дополнительный уровень SQL Links – Structured Query Language Links. Архитектура<br />

BDE/SQL Links реализует более производительный сетевой обмен по<br />

технологии клиент/сервер. Выражения на языке SQL, непосредственно внедренные<br />

в программный код, позволяют манипулировать данными через<br />

BDE, обеспечивая наивысшую производительность, хотя при этом игнорируются<br />

визуальные компоненты и связанные с ними преимущества быстрой<br />

разработки приложений.<br />

Специализированные технологии dbExpress предусматривают высокоскоростное<br />

подключение к каждому конкретному типу базы данных с учетом<br />

особенностей операционной среды. Компромиссным решением служит подключение<br />

к ODBC через специализированные компоненты, поставляемые<br />

сторонними производителями под API конкретных СУБД. Популярен и вариант<br />

API СУБД без использования технологии визуальной разработки. Он<br />

применяется в простых базах данных и поддерживается их разработчиками<br />

самостоятельно за счет средств доступа к данным самой СУБД.<br />

Обычно для построения приложений баз данных используется организация,<br />

состоящая из нескольких своеобразных программных «слоев» (рис.<br />

2,а):


65<br />

• интерфейс пользователя (user interface);<br />

• компоненты связи интерфейса пользователя с наборами данных,<br />

именуемые источниками данных (data sources);<br />

• наборы данных (datasets), представляющие записи таблиц и запросов<br />

баз данных;<br />

• средства подключения к базам данных (data connections).<br />

Интерфейс пользователя строится на базе традиционных форм. Источники<br />

данных связывают интерфейс пользователя с наборами данных. При<br />

этом несколько элементов управления интерфейса можно подключать к одному<br />

источнику данных для синхронной обработки одной и той же информации.<br />

Основу приложений составляют наборы данных, представляющие даталогические<br />

модели одной или нескольких таблиц или запросов и отражающие<br />

состояние и изменение физических данных носителя информации. Каждой<br />

таблице или запросу ставится в соответствие свой набор данных.<br />

Архитектуры приложений баз данных<br />

Наборы данных различных типов используют разнообразные механизмы<br />

подключения к данным, которые, в свою очередь, определяют архитектуру<br />

приложений баз данных. К основным способам подключения относятся:<br />

Программа<br />

Программа<br />

Application server<br />

Интерфейс<br />

(User interface)<br />

Источник данных<br />

(Data source)<br />

Набор данных<br />

(Dataset)<br />

Подключение<br />

(Data connection)<br />

Данные<br />

Интерфейс<br />

пользователя<br />

Источник данных<br />

клиента<br />

Набор данных<br />

клиента<br />

Подключение<br />

к серверу<br />

Client<br />

Источник сервера<br />

(Provider)<br />

Целевой<br />

набор данных<br />

(Unidirectional<br />

data source)<br />

SQL-соединение<br />

(SQL connection)<br />

Сервер базы данных<br />

(Database server)<br />

а. б.<br />

Рис. 2. Варианты организации приложений баз данных


66<br />

• прямое подключение;<br />

• подключение через вспомогательный набор данных;<br />

• архитектура «клиент-сервер».<br />

В C++Builder все подключения по технологии ADO производятся через<br />

компонент TADOConnection вкладки ADO панели визуальных компонентов,<br />

источниками данных служат компоненты TADOTable, TADOQuery, TADOStoredProc,<br />

TADODataSet, TADOCommand вкладки ADO, а наборы данных<br />

обычно представляются компонентом TDataSource вкладки Data Access.<br />

Подключения по технологии BDE выполняются через компонент<br />

TDataBase без наборов данных или с TDataSource, а роль источников данных<br />

здесь играют компоненты TTable, TQuery, TStoredProc вкладки BDE панели<br />

визуальных компонентов. Для работы с корпоративными базами данных<br />

InterBase служат компоненты TIBDataBase, TIBTable, TIBQuery, TIB-<br />

StoredProc вкладки InterBase.<br />

Технологии dbExpress для подключения используют компонент<br />

TSQLConnection, источниками данных являются TSQLTable, TSQLQuery,<br />

TSQLStoredProc вкладки dbExpress, а наборы данных представляет<br />

TSQLDataSet.<br />

Прямое подключение обычно используется для организации постоянных<br />

соединений, которые устанавливаются в начале сеанса связи с базой<br />

данных и разрываются в конце сеанса. Такое подключение просто реализуется,<br />

но работает только в однопользовательских системах без разграничения<br />

прав доступа. Технологиями BDE и dbExpress поддерживается подключение<br />

через вспомогательные наборы данных TBDEClientDataSet, TIBClientDataSet,<br />

TSQLClientDataSet.<br />

Архитектура «клиент-сервер» применяется в приложениях, где данные<br />

распределены по нескольким таблицам или нескольким корпоративным и<br />

локальным базам данных, в системах с несколькими клиентами, требующих<br />

организации совместного доступа и коллективной защиты. Схема организации<br />

таких приложений показана на рис. 2,б. В отличие от предыдущих архитектур,<br />

для подключения здесь привлекается источник данных сервера – провайдер,<br />

к которому обращается клиент для получения или передачи данных.<br />

Приложение-сервер может предоставлять запрошенные данные или отказывать<br />

в сервисе в зависимости от принятой стратегии управления и ситуации в<br />

сети.<br />

Подключение к базам данных<br />

Хорошим стилем программирования считается отделение интерфейса<br />

пользователя от компонентов связи с базой данных. Этим достигается повышенная<br />

гибкость проекта, позволяющая менять средства связи независимо от<br />

интерфейса или усовершенствовать интерфейс без изменения подключений.<br />

Инструментом такого разделения в C++Builder служат модули данных Data


67<br />

Module из Object Repository. Это специальный тип формы, применяемый в<br />

качестве хранилища невидимых компонентов, в первую очередь – компонентов<br />

баз данных. Как и формы, каждый модуль данных строится с использованием<br />

файла заголовков (.H), файла кода (.CPP) и файла структуры модуля<br />

(.DFM).<br />

Окно модуля данных удобно применять вместе с вкладкой Diagram и с<br />

окном Object TreeView, так как модуль данных отражает компоненты, а<br />

вкладка Diagram – их иерархическую структуру. При проектировании компоненты<br />

можно помещать прямо в модуль данных или на ветви Object Tree-<br />

View, в результате чего они становятся дочерними компонентами того объекта,<br />

на который помещаются. До завершения привязки объекты Object Tree-<br />

View помечаются вопросительными знаками.<br />

В частности, для подключения базы данных через вспомогательный<br />

набор данных по технологии ADO в модуль данных помещается объект<br />

ADOConnection с вкладки ADO. Его редактор свойства ConnectionString<br />

предлагает сформировать строку подключения (или использовать файл<br />

.UDL). Для формирования такой строки (под кнопкой Build) при подключении<br />

к персональным базам данных Microsoft можно выбрать провайдер Microsoft<br />

Jet… На вкладке Connection этого же окна в поле Select or enter a database<br />

name указывается подключаемая база данных. Кнопкой Test Connection<br />

проверяется подключение. В сформированной таким образом строке<br />

подключения указано имя провайдера, имя и пароль пользователя и папка с<br />

базой данных. В большинстве случаев свойство LoginPrompt объекта<br />

ADOConnection устанавливается в false для открытия базы данных без пароля.<br />

Связь приложения с базой данных в архитектуре «клиент-сервер» выполняется<br />

с помощью источников данных, работающих через драйверы<br />

ODBC. С описанием таких источников в формате DSN (Data Source Notification)<br />

можно ознакомиться, если открыть Администратор источников данных<br />

ODBC в папке Панель управления Windows. Вкладки этой программы посвящены<br />

трем категориям источников: Пользовательский DSN единственного<br />

пользователя одного компьютера; Системный DSN всех пользователей<br />

компьютера; Файловый DSN, доступный всем пользователям сети, имеющих<br />

одинаковые драйверы, независимо от компьютера. На отдельной вкладке<br />

представлены установленные на компьютере драйверы ODBC. Для работы с<br />

конкретной базой данных обычно подключают имеющийся или создают новый<br />

источник данных, используя соответствующий драйвер. Этот источник<br />

впоследствии распространяют в комплекте с приложением и базой данных.<br />

Поэтому для формирования строки подключения под кнопкой Build<br />

обычно выбирается Microsoft OLE DB Provider for ODBC Drivers, служащий<br />

универсальным механизмом доступа к продуктам Microsoft. На вкладке Connection<br />

этого же окна в поле переключателя Use connection string под кнопкой<br />

Build выбирается источник данных. Если требуемый источник данных


68<br />

отсутствует и его следует создать, в поле DSN Name под кнопкой New указывается<br />

одна из строк:<br />

• Microsoft Access Driver, обслуживающая Access;<br />

• Microsoft dBase Driver для работы с базами xBase;<br />

• Microsoft Excel Driver для работы с таблицами Excel;<br />

• Microsoft Text Driver для подключения текстовых таблиц;<br />

• SQL Server для обращения к корпоративной базе данных<br />

и вводится произвольное имя нового источника. В следующем окне ODBC<br />

Microsoft Access Setup базу данных можно выбрать под кнопкой Select, либо<br />

создать новую под кнопкой Create, задав ее имя. В последнем случае должно<br />

поступить сообщение об успешном создании базы данных. Выделив найденный<br />

или созданный источник и вернувшись на вкладку Connection, остается<br />

убедиться в его работоспособности кнопкой Test Connection.<br />

А вот пример программного подключения, выполняемого в конструкторе<br />

модуля данных:<br />

__fastcall TDataModule1::TDataModule1 (TComponent* Owner)<br />

: TDataModule (Owner) {<br />

AnsiString s = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=\<br />

bRobots.mdb;Persist Security Info=False";<br />

try {<br />

ADOConnection1->Connected = false;<br />

ADOConnection1->ConnectionString = s;<br />

ADOConnection1->Connected = true;<br />

} catch (...) { ShowMessage ("Нет подключения к bRobots.mdb"); }<br />

}<br />

Фиксированную строку подключения можно заменить окном диалога:<br />

AnsiString s = PromptDataSource(0, "");<br />

Доступ к записям<br />

Доступ к записям баз данных реализуется через таблицы, запросы и<br />

хранимые процедуры.<br />

Для работы с таблицами с помощью компонента ADOTable вкладки<br />

ADO в модуль данных добавляются элементы управления доступом к каждой<br />

таблице. Их свойство Connection инициализируется именем объекта<br />

ADOConnection, а в свойство TableName вписываются имена соответствующих<br />

таблиц. Каждый элемент управления ADOTable аккумулирует все записи<br />

и поля своей таблицы.


69<br />

Для подключения к форме с помощью компонента DataSource вкладки<br />

Data Access панели компонентов в модуль данных добавляется источник<br />

данных для каждой таблицы. В их свойстве DataSet указываются имена соответствующих<br />

компонентов ADOTable. Свойство AutoEdit разрешает режим<br />

редактирования записей, вводимых в поля наборов данных.<br />

После подключения в модуле данных устанавливается в true свойство<br />

Active элементов доступа к таблицам для их открытия. Аналогично выполняется<br />

открытие таблиц из программы, например:<br />

ADOTable1->TableName = "Countries";<br />

ADOTable1->Active = true;<br />

Следующий пример демонстрирует подключение к файлу MYDBF.DSN<br />

с открытием таблицы T.<br />

void __fastcall TDBForm1::ConnectClick (TObject *Sender) {<br />

AnsiString s = "Extended Properties=\"FILEDSN=myDBF.dsn;\";";<br />

TADOConnection *cnn = DataModule1->ADOConnectionDBF;<br />

TADOTable *rst = DataModule1->ADOTableDBF;<br />

try {<br />

cnn->Connected = false;<br />

cnn->ConnectionString = s;<br />

cnn->Connected = true;<br />

rst->TableName = "T";<br />

rst->Active = true;<br />

} catch (...) {<br />

s = PromptDataSource(0, "");<br />

try {<br />

cnn->Connected = false;<br />

cnn->ConnectionString = s;<br />

cnn->Connected = true;<br />

rst->TableName = "T";<br />

rst->Active = true;<br />

} catch (...) { ShowMessage (s); }<br />

}<br />

}<br />

Возможно подключение и при отсутствии файла .DSN, в том числе – и<br />

без использования объектов ADOConnection, если известны все необходимые<br />

параметры командной строки. Еще один пример посвящен открытию доступа<br />

к таблице dBase и к листу Excel:<br />

void __fastcall TDBForm1::DBFConnectClick (TObject *Sender) {<br />

AnsiString s = "Provider=MSDASQL.1;\


Extended Properties=\"DBQ=D:\\DATABASES;\<br />

Driver={Microsoft dBase Driver (*.dbf)};DriverId=533;FIL=dBase 5.0;\";";<br />

TADOConnection *cnn = DataModule1->ADOConnectionDBF;<br />

TADOTable *rst = DataModule1->ADOTableDBF;<br />

try {<br />

cnn->Connected = false;<br />

cnn->ConnectionString = s;<br />

cnn->Connected = true;<br />

rst->TableName = "Т";<br />

rst->Active = true;<br />

} catch (...) { ShowMessage (“Увы…”); }<br />

}<br />

void __fastcall TDBForm1::XLSConnectClick (TObject *Sender) {<br />

AnsiString s = "Provider=MSDASQL.1;\<br />

Extended Properties=\"DBQ=D:\\DATABASES\\Сотрудники.xls;\<br />

Driver={Microsoft Excel Driver (*.xls)};DriverId=790;FIL=excel 8.0;\";";<br />

TADOTable *rst = DataModule1->ADOTableXLS;<br />

try {<br />

rst->Active = false;<br />

rst->ConnectionString = s;<br />

rst->TableName = "Лист1";<br />

rst->Active = true;<br />

DBGrid1->DataSource = DataModule1->DataSourceXLS;<br />

} catch (...) { ShowMessage (“Не подключились”); }<br />

}<br />

70<br />

К подключенной базе данных можно обращаться с запросами на выборку,<br />

создание, обновление и удаление таблиц. Для этого на вкладке ADO<br />

присутствует компонент ADOQuery, создающий объекты-запросы. В свойстве<br />

Connection этого компонента указывается имя объекта ADOConnection, а в<br />

редакторе свойства SQL формируется требуемое SQL-выражение. Примеры<br />

запросов на выборку и запросов действия:<br />

select * from Robots where radius > 10;<br />

select model, country, mass*radius*radius as inertia from Robots;<br />

create table Robots (model string (10), country string (10),<br />

coord string (15), radius float, mass float, primary key (model)) ;<br />

create table Countries (country string (10), primary key (country));<br />

insert into Robots values ('Тест', 'Россия', 'Сферическая', 3, 5 );<br />

update Robots set country='Франция' where country='Россия';<br />

delete from Robots where country='Франция';


71<br />

В свойстве SQL одного компонента сохраняется только один запрос.<br />

Поэтому ввод каждой строки запроса надо завершать установкой свойства<br />

Active в true для его проверки и выполнения. При этом C++Builder передает<br />

запрос серверу, который выполняет его и возвращает приложению результирующий<br />

набор (result set). Запросы действия, не возвращающие наборов данных,<br />

можно формировать, также, компонентом TADOCommand. После выполнения<br />

запроса в режиме проектирования или программным путем вводится<br />

следующее выражение SQL и вновь повторяется его активизация. Для<br />

подключения запросов на выборку к форме, в модуль данных добавляются<br />

источники данных DataSource.<br />

Формирование базы данных при необходимости продолжают на вкладке<br />

Diagram модуля данных, где из элементов управления ADOTable собирается<br />

структурная схема базы данных. На вкладке Diagram между ними можно<br />

просматривать и устанавливать связи четырех типов:<br />

• Property от дочернего элемента управления к родителю (сплошная<br />

линия со стрелкой), например, от полей к таблице;<br />

• Master/Detail от главной таблицы (master) к подчиненной (detail) (линия,<br />

на которой главная таблица помечена большим прямоугольником,<br />

а подчиненная – маленьким прямоугольником и указано поле<br />

связи);<br />

• типа Lookup (линия с изображением глаза);<br />

• ссылка Comment Allude (стрелка).<br />

Пользуясь контекстными меню, эти связи удобно удалять и редактировать.<br />

При этом тип Lookup отображает поля подстановки данных из других таблиц,<br />

а Comment Allude подключает комментарии Comment Block к компонентам<br />

структуры.<br />

Чтобы связать между собой две таблицы, следует при нажатой кнопке<br />

Master/Detail провести линию от главной таблицы к подчиненной, и в открывшемся<br />

окне указать имена зависимых полей и нажать кнопку Add. В результате<br />

автоматически заполнится свойство MasterFields подчиненной таблицы<br />

именем соответствующего поля главной таблицы MasterSource. Эти же<br />

данные можно занести вручную через редакторы названных свойств.<br />

Чтобы создать в неактивной таблице новое поле подстановки, следует в<br />

контекстном редакторе ее ветви Fields окна Object TreeView выбрать New<br />

Field и, установив переключатель Lookup, задать имя, тип и размер поля,<br />

ключи (Key), источник (Dataset) и поле подстановки (Result Field).<br />

Инструменты Comment Allude и Comment Block помогают оформить<br />

схему базы данных, а контекстное меню Print готовит ее к печати.<br />

Подготовленный модуль данных сохраняется, и в редакторе форм подключается<br />

к модулю формы (File .Include Unit Hdr). В форму помещаются<br />

элементы управления данными с вкладки Data Controls панели компонентов.<br />

Это таблицы DBGrid и навигаторы DBNavigator, а также поля таблиц DBText,


72<br />

DBEdit, DBMemo, DBImage и другие. Компоненты вкладки Data Controls отличаются<br />

от рассмотренных ранее компонентов вкладки Standard наличием<br />

свойств DataSource и DataField, обеспечивающих их совместную работу с<br />

источниками данных. В свойстве DataSource указываются соответствующие<br />

объекты модуля данных, а в DataField –поля таблиц и запросов. Здесь также<br />

возможна подстановка (свойство столбца PickList), но не из таблицы, а из<br />

фиксированного списка значений.<br />

Объектно-ориентированное программирование<br />

«Что посеешь, то и пожнешь»<br />

Русская пословица<br />

Инкапсуляция<br />

Создание авторских классов начинается с их объявления в файле заголовка<br />

модуля. После объявления или одновременно с ним класс определяется<br />

(описывается). Встроенное объявление и определение членов выполняется в<br />

формате обычных переменных и функций:<br />

class имяКласса {<br />

[Метка]<br />

[объявлениеЧленов…]<br />

[встроенноеОпределениеЧленов…] };<br />

Если не дается встроенное определение, в файле кода модуля приводится<br />

внешнее определение членов:<br />

Тип имяКласса :: имяЧлен-данного [ =Значение];<br />

Тип имяКласса :: имяЧлен-функции телоЧлен-функции<br />

Использование внешних определений повышает наглядность текста программы<br />

и облегчает его последующую модификацию. Члены класса могут<br />

принадлежать к другим классам, и их именуют в этом случае вложенными<br />

классами. Глубина вложенности не ограничивается, хотя «большое число<br />

уровней абстракции так же плохо, как и их отсутствие [Голуб]».<br />

После объявления и определения создаются объекты класса. Каждый<br />

класс может использоваться для порождения множества объектов:<br />

имяКласса имяОбъекта;<br />

имяКласса *имяОбъекта = new имяКласса;


73<br />

Инициализация и обращение к членам класса выполняются по разнообразным<br />

схемам. Обращение к членам своего класса производится по именам,<br />

без указания имени объекта:<br />

имяЧлена [= Значение];<br />

При этом адресация выполняется с помощью неявного аргумента this. Так<br />

как this – адрес, то конструкция this->имяЧлена является указателем на объект,<br />

а *this представляет сам объект:<br />

this->имяЧлена [= Значение];<br />

(*this).имяЧлена [= Значение];<br />

При обращении к членам других классов следует указывать имя объекта:<br />

имяОбъекта.имяЧлена [= Значение];<br />

ссылкаНаОбъект->имяЧлена [= Значение];<br />

Пусть, например, требуется периодически выводить информацию о<br />

компьютерах из различных функций некоторой программы. Чтобы не делать<br />

это в каждой функции, в проекте удобно создать новый модуль (File .New<br />

.Unit), и в его файле заголовка поместить объявление класса VPC, получающего<br />

сведения через функцию Set и представляющего их функцией Out, как<br />

это показано в следующем примере:<br />

class VPC {<br />

private:<br />

int type;<br />

AnsiString firm;<br />

public:<br />

double price;<br />

void Set (int a, AnsiString b) { type = a; firm = b; }<br />

void Out () {<br />

ShowMessage ("Пpоцессор фирмы " + firm + " стоит $" + price);<br />

}<br />

};<br />

Для создания объектов класса в других формах, в их файлах кода размещаются<br />

ссылки на файл заголовка класса VPC. Ниже приведен пример функции<br />

MainVPC, «воспользовавшейся услугами» класса VPC для обработки объекта<br />

myCPU, и таких функций может быть множество:<br />

void MainVPC () {<br />

AnsiString company = "Acer";<br />

VPC myCPU;


74<br />

myCPU.Set (6, company); myCPU.price = 100; myCPU.Out ();<br />

}<br />

Другой пример посвящен классу VIntegral, реализующему численное<br />

интегрирование тремя методами: методом прямоугольников, методом трапеций<br />

и методом парабол. Класс объявлен в файле заголовка модуля, его членфункции<br />

Rect, Trap, Para определены в файле кода модуля, а объекты созданы<br />

функцией MainVIntegral в файле кода формы:<br />

class VIntegral {<br />

double j, x, sum, sum1, f;<br />

public:<br />

void Rect (double, double, double, double (*fnc) (double, double));<br />

void Trap (double, double, double, double (*fnc) (double, double));<br />

void Para (double, double, double, double (*fnc) (double, double));<br />

};<br />

void VIntegral :: Rect (double xk, double h, double f,<br />

double (*fnc) (double x, double y))<br />

{ for (x=0, sum=0; x


}<br />

i.Rect (xk, h, f, pow);<br />

i.Trap (xk, h, f, pow);<br />

i.Para (xk, h, f, pow);<br />

75<br />

Пример реализации и использования класса дека:<br />

class VDeque {<br />

VDeque *next, *pre;<br />

char info[40];<br />

public:<br />

void Add (char *ptr);<br />

void AddNode ();<br />

void Out ();<br />

};<br />

VDeque *head;<br />

void VDeque :: Add (char *ptr) {<br />

int max = strlen (ptr); for (int i=0; ipre = this; pre = 0; }<br />

}<br />

void VDeque :: Out () {<br />

AnsiString result;<br />

VDeque *cursor = head;<br />

while (cursor != this) { result += AnsiString (cursor->info) +'\t'; cursor = cursor-<br />

>next; }<br />

ShowMessage (AnsiString (cursor->info));<br />

}<br />

void MainVDeque () {<br />

VDeque a, b, c, d;<br />

a.Add ("IBM"); a.AddNode (); b.Add ("Siemens"); b.AddNode ();<br />

c.Add ("Motorola"); c.AddNode (); d.Add ("Apple"); d.AddNode (); b.Out ();<br />

}<br />

Конструкторы и деструкторы<br />

Для автоматической инициализации объектов класса используются<br />

конструкторы – бестиповые, одноименные с классом член-функции:


76<br />

class VMicroPC {<br />

AnsiString type; int price;<br />

public:<br />

VMicroPC (AnsiString a, int b) { type = a; price = b; } // конструктор<br />

void Out () { ShowMessage ("Компьютер " + type + " стоит $" + price); }<br />

};<br />

void MainVMicroPC() {<br />

AnsiString comp1 = "IBM", comp2 = "Macintosh";<br />

VMicroPC myCPU (comp1, 990), yourCPU (comp2, 1500);<br />

myCPU.Out(); yourCPU.Out();<br />

}<br />

Конструктор вызывается не только при определении нового объекта<br />

класса для объявления данных, но и при копировании объекта или при динамическом<br />

выделении памяти новому объекту. В качестве аргументов копирующего<br />

конструктора (copying constructor) используются ссылки на копируемые<br />

объекты. Примером может служить конструктор типизированного<br />

класса AnsiString:<br />

struct computer { AnsiString name; int price; };<br />

class VList {<br />

computer *p;<br />

int i, max, current;<br />

public:<br />

VList (int);<br />

void Add (computer *);<br />

void Delete (computer *);<br />

AnsiString Out ();<br />

};<br />

VList::VList (int max) { VList::max = max; current = 0; p = new computer[max]; }<br />

void VList::Add (computer *v) {<br />

if (current < max) {<br />

p[current].name = v->name;<br />

p[current++].price = v->price;<br />

}<br />

}<br />

void VList::Delete (computer *v) {<br />

for (i=0; iname) {


}<br />

}<br />

while (i < current-1) {<br />

p[i].name = p[i+1].name;<br />

p[i].price = p[i+1].price;<br />

i++;<br />

}<br />

--current;<br />

}<br />

77<br />

AnsiString VList::Out () {<br />

AnsiString result;<br />

for (i=0; i


AnsiString temp;<br />

float m[3][3], k;<br />

public:<br />

VMatrix (float a[][3], float c = 1);<br />

void Out ();<br />

};<br />

78<br />

VMatrix::VMatrix (float a[][3], float c): k(c) {<br />

for (i=0; i


};<br />

79<br />

void Out () { ShowMessage (AnsiString (first) + '\n' + AnsiString (second)); }<br />

void MainVPair () {<br />

{ VPair num (2,3); // объект создан<br />

num.Out (); // 2-3<br />

} // объект удален<br />

{ VPair num (4,5); // объект создан<br />

num.Out (); // 4-5<br />

} // объект удален<br />

}<br />

За освобождением памяти можно наблюдать и без деструктора. В приведенной<br />

ниже реализации объектов str, поочередно помещаемых в стек и<br />

очередь, деструктора нет:<br />

class VStackAndQueue {<br />

char *s;<br />

int i, size, top;<br />

AnsiString result;<br />

public:<br />

VStackAndQueue (AnsiString str);<br />

void Push (AnsiString str);<br />

void PopStack ();<br />

void PopQueue ();<br />

int Empty () { return top == -1; }<br />

int Full () { return top == size; }<br />

};<br />

VStackAndQueue::VStackAndQueue (AnsiString str) {<br />

size = str.Length (); s = new char[size]; top = -1; result = str + ":\n";<br />

}<br />

void VStackAndQueue::Push (AnsiString str) {<br />

for (i = 1; i


void MainVStackAndQueue () {<br />

{ AnsiString str = "Проверка стека";<br />

VStackAndQueue test (str); test.Push (str); test.PopStack ();<br />

}<br />

{ AnsiString str = "Проверка очереди";<br />

VStackAndQueue test (str); test.Push (str); test.PopQueue ();<br />

}<br />

}<br />

80<br />

Дружественные функции и классы<br />

Иногда требуется разрешить доступ к закрытым членам класса со стороны<br />

функций, не относящихся к членам класса. Такие функции объявляются<br />

дружественными (friend) рассматриваемому классу. Объявление дружественной<br />

функции помещается в public-, либо в private-раздел объявления<br />

класса, которому она дружественна. Метки public, private не влияют на описание<br />

friend.<br />

class VFriendPC {<br />

int num;<br />

public:<br />

VFriendPC (int b): num (b) {}<br />

friend void Show (VFriendPC x);<br />

};<br />

void Show (VFriendPC x) { ShowMessage (AnsiString ( x.num )); }<br />

void MainVFriendPC () { VFriendPC price (900); Show (price); }<br />

В приведенном примере Show объявлена функцией, дружественной классу<br />

VFriendPC, поэтому она свободно обрабатывает закрытую переменную num<br />

этого класса.<br />

Особенно полезными аспектами механизма дружественных функций<br />

является то, что функция может быть дружественной по отношению к нескольким<br />

классам, член-функция одного класса может быть дружественной<br />

другому классу и, напротив, сразу группа функций или целый класс могут<br />

быть дружественными рассматриваемому классу:<br />

class VTwo;<br />

class VOne {<br />

friend void Out (VOne &c1, VTwo &c2);<br />

private:<br />

char *s1;<br />

public:<br />

VOne () { s1 = "Проверка: ";}


};<br />

81<br />

class VTwo {<br />

friend void Out (VOne &c1, VTwo &c2);<br />

private:<br />

char *s2;<br />

public:<br />

VTwo () { s2 = "Sony, Siemens, Fuji"; }<br />

};<br />

void Out (VOne &c1, VTwo &c2) {<br />

ShowMessage (AnsiString ( c1.s1) + AnsiString (c2.s2));<br />

}<br />

void MainVOneVTwo () {<br />

VOne c1; VTwo c2; Out (c1, c2);<br />

}<br />

// Проверка: Sony, Siemens, Fuji<br />

Если все функции одного класса дружественны второму классу, то говорят<br />

о дружественном классе (friend class):<br />

class VFirst;<br />

class VSecond {<br />

AnsiString model, firm;<br />

public:<br />

VSecond (AnsiString a, AnsiString b): model (a), firm (b) {}<br />

friend class VFirst;<br />

};<br />

class VFirst {<br />

VSecond set;<br />

public:<br />

VFirst (AnsiString a, AnsiString b): set (a,b) {}<br />

void Model () { ShowMessage (AnsiString (set.VSecond::model)); }<br />

void Firm () { ShowMessage (AnsiString (set.VSecond::firm)); }<br />

};<br />

void MainVFirstVSecond () {<br />

AnsiString mod = "ATT", frm = "IBM";<br />

VFirst device (mod, frm);<br />

device.Model ();<br />

// AT<br />

device.Firm ();<br />

// IBM<br />

}


82<br />

Используя дружественные функции, следует помнить, что «подобно<br />

оператору goto, друзья снижают надежность программного кода... Потребность<br />

в объявлении одних классов друзьями других свидетельствует о плохо<br />

продуманной иерархии классов» [Сван]. Поэтому обращаться к помощи друзей<br />

следует лишь при крайней необходимости.<br />

Полиморфизм<br />

Полиморфизм как способность одинаковых операторов и одноименных<br />

функций к избирательному (зависящему от типа) поведению реализуется в<br />

C++ в первую очередь через механизмы перегрузки (overloading) операторов<br />

и функций.<br />

Суть перегрузки операторов заключается в создании функцииоператора,<br />

использующей в качестве имени лексему operator и следующим за<br />

ней перегружаемый оператор. Объявление перегрузки аналогично объявлению<br />

обычной дружественной функции или член-функции класса:<br />

Тип operator знакОперации ( [объявлениеПараметров] );<br />

Перегрузке могут подлежать все простые операторы, за исключением «.»,<br />

«.*», «?:», «sizeof», «::». Язык не позволяет задать для перегружаемого оператора<br />

иной символ, кроме тех, что определены в списке операторов. Нельзя<br />

изменять синтаксис использования оператора: унарный оператор не перегружается<br />

в бинарный, префиксный – в постфиксный и т.п. Операторы «+»,<br />

«-», «*», «&» могут быть как бинарными, так и унарными.<br />

Перегружаются только опеpатоpы в операциях, для которых по крайней<br />

мере один из параметров является членом структуры или класса. С учетом<br />

неявного указателя this, при выполнении одноместных операций аргумент<br />

может опускаться, а в двухместных операциях допустимо оставлять<br />

один аргумент.<br />

Для знакомства с перегрузкой одноместных операторов рассмотрим<br />

одну из распространенных задач программирования – вывод текущего времени<br />

на экран. На основании показаний системных часов компьютера такая<br />

программа формирует на экране время в днях, часах и минутах. Чтобы в каждом<br />

такте часов не пересчитывать время заново, здесь переопределен оператор<br />

инкремента, что позволяет использовать не абсолютный, а относительный<br />

отсчет времени:<br />

class VClock {<br />

private:<br />

__int64 total, secs, mins, hours, days;<br />

public:<br />

VClock (__int64 i);<br />

void Out ();


};<br />

VClock operator++ ();<br />

83<br />

VClock::VClock (__int64 i) {<br />

total = i; secs = total % 60; mins = (total / 60) % 60;<br />

hours = (total / 3600) % 24; days = total / 86400L;<br />

}<br />

void VClock::Out () {<br />

ShowMessage (AnsiString (total) + " сек.= "<br />

+ AnsiString (days) + " дн. " + AnsiString (hours) + " час. "<br />

+ AnsiString (mins) + " мин. " + AnsiString (secs) + " сек. ");<br />

}<br />

VClock VClock::operator++ () {<br />

VClock temp (++total);<br />

secs = temp.secs; mins = temp.mins; hours = temp.hours;<br />

days = temp.days; return *this;<br />

}<br />

void MainVClock () {<br />

VClock t1 (59), t2 (172799L);<br />

}<br />

ShowMessage ("Старое время:");<br />

t1.Out ();<br />

// 0 дн. 0 час. 0 мин. 59 сек.<br />

t2.Out ();<br />

// 1 дн. 23 час. 59 мин. 59 сек.<br />

++t1; ++t2;<br />

ShowMessage ("Новое время:");<br />

t1.Out ();<br />

// 0 дн. 0 час. 1 мин. 0 сек.<br />

t2.Out ();<br />

// 2 дн. 0 час. 0 мин. 0 сек.<br />

Следующий пример посвящен операции транспонирования матрицы<br />

M[3][3], представленной элементом управления StringGrid1, для выполнения<br />

которой перегружается оператор «!»:<br />

class VMatr {<br />

int i, j;<br />

double temp;<br />

double M[3][3];<br />

public:<br />

VMatr ();<br />

void Out ();<br />

VMatr operator ! ();<br />

};


VMatr::VMatr () {<br />

for (i=0; iCells [i][j] );<br />

}<br />

void VMatr::Out () {<br />

for (i=0; iCells[i][j] = M[i][j];<br />

}<br />

VMatr VMatr::operator! () {<br />

for (i=0; i


85<br />

class VMatrixPlus {<br />

int i, j; double k, m[3][3];<br />

public:<br />

double n[3][3];<br />

VMatrixPlus (double a[][3]);<br />

VMatrixPlus (double c): k (c) {}<br />

VMatrixPlus operator ! ();<br />

VMatrixPlus operator + (VMatrixPlus c);<br />

void Out ();<br />

};<br />

VMatrixPlus::VMatrixPlus (double a[][3]) {<br />

for (i=0; i


86<br />

В теле определяемой функции один или несколько параметров используют<br />

типы, специфицированные в списке параметров шаблона. В качестве примера<br />

обратимся к сортировке членов структуры методом «пузырька». Чтобы<br />

дважды не использовать в этой программе процедуру обмена значений двух<br />

переменных, разработана функция-шаблон, в которой тип параметра самостоятельно подбирается компилятором:<br />

template void Swap (T &a, T &b) { T buf; buf=b; b=a; a=buf; }<br />

void MainSwap () {<br />

const N = 3;<br />

struct metallHeat { AnsiString name; int heat; };<br />

metallHeat m[N];<br />

m[0].name = "cuprum"; m[0].heat = 139; m[1].name = "aluminium";<br />

m[1].heat = 386; m[2].name = "ferrum"; m[2].heat = 234; int i, j, criteria;<br />

criteria = MessageDlg ("Сортировка по name?", mtConfirmation,<br />

TMsgDlgButtons ()


87<br />

void MainSort () {<br />

int i, weight[] = {10, 20, 5, 60, 15};<br />

AnsiString result;<br />

int len = sizeof (weight) / sizeof (weight[0]);<br />

}<br />

Sort (weight, len);<br />

for (i=0; i», используемый<br />

при сравнении элементов массива.<br />

class VPrice {<br />

__int64 dollars;<br />

int cents;<br />

public:<br />

VPrice (__int64 myDollars=0, int myCents=0):<br />

dollars (myDollars), cents (myCents) {}<br />

int operator > (const VPrice);<br />

friend AnsiString &operator (const VPrice amount) {<br />

return (dollars > amount.dollars) ||<br />

(dollars == amount.dollars && cents > amount.cents);<br />

}<br />

AnsiString &operator


88<br />

Для создания обобщенного определения семейства классов используются<br />

шаблоны классов. «Подобно тому, как класс представляет собой схему<br />

строения объекта, так и шаблон представляет схему функции или класса»<br />

[Сван]. Шаблон определяет член-данные и член-функции класса, на основании<br />

которых компилятор впоследствии cможет сгенерировать новый класс<br />

для конкретного типа. Иногда шаблон называют параметризованным типом,<br />

указывающим лишь спецификации для функций и классов, но не детали реализации.<br />

Шаблонная функция начинается строкой template , где вместо<br />

T пользователь будет впоследствии подставлять конкретное имя функции<br />

или класса. Можно задавать указатель T *param или ссылку T &param.<br />

Функция может обновлять несколько параметров и возвращать значение типа<br />

T. Hапpимеp, template T F(int a, Tb) – шаблонная функция F возвращает<br />

значение типа T и имеет в качестве параметров целое с именем a и<br />

неопределенный объект T с именем b. В дальнейшем это может оказаться,<br />

например, выражением double F(int a, double b). Возможно и такое объявление:<br />

template T1 F(T1 a, T2 b). Здесь функция F будет<br />

возвращать значение типа T1, и ей нужны два параметра типов T1, T2.<br />

Рассмотрим работу шаблона класса для простых стековых операций:<br />

const MAX = 10;<br />

template class VStack {<br />

int num;<br />

T *items;<br />

public:<br />

VStack (int size = MAX) { num = 0, items = new T[size]; }<br />

~VStack () { delete [] items; }<br />

void Push (T t) { items[num++] = t; }<br />

T Pop () { return items[--num]; }<br />

};<br />

void MainVStack () {<br />

VStack StackInt (10);<br />

StackInt.Push (33); StackInt.Push (44); StackInt.Push (55);<br />

ShowMessage ("В стеке целые числа: " + AnsiString (StackInt.Pop()) + ',' +<br />

AnsiString (StackInt.Pop()) + ',' + AnsiString (StackInt.Pop()));<br />

VStack StackDouble (10);<br />

StackDouble.Push (33.3); StackDouble.Push (44.2);<br />

StackDouble.Push (-55.1);<br />

ShowMessage ("В стеке вещественные числа: " +


}<br />

И еще один шаблон класса. Он работает с массивами произвольных типов:<br />

89<br />

AnsiString (StackDouble.Pop ()) + ',' +<br />

AnsiString (StackDouble.Pop ()) + ',' + AnsiString (StackDouble.Pop ()));<br />

template class VVector {<br />

T *elem;<br />

// тип и число элементов<br />

public:<br />

VVector ();<br />

T &operator[] (int i) { return elem[i]; }<br />

void Out ();<br />

};<br />

template VVector :: VVector () {<br />

elem = new T[size]; for (int i=0; i


90<br />

держивающие итерацию. Итераторы бывают прямыми и обратными, константными<br />

и неконстантными, изменяющими содержимое контейнера. Низкоуровневые<br />

функции распределения памяти контейнеров называют аллокаторами.<br />

С их помощью все члены STL автоматически выделяют и освобождают<br />

память, не нуждаясь в операторах new и delete. Для манипулирования<br />

данными в контейнерах служат алгоритмы. Обычно на них возлагаются<br />

стандартные операции сортировки, поиска, сравнения, перестановки и т.п.<br />

Контейнер vector, описанный в файле заголовка является динамическим<br />

массивом переменного размера. Подобно стеку, он обеспечивает<br />

добавление объектов в конец функцией push_back и удаление их оттуда<br />

функцией pop_back. Кроме того, в нем реализована вставка объектов в произвольное<br />

место функцией insert и удаление их оттуда функцией erase.<br />

Функция clear удаляет из контейнера все объекты, а функция size возвращает<br />

число объектов в контейнере. Функция empty сообщает, пуст ли контейнер,<br />

reserve резервирует память для будущих объектов, а capacity возвращает текущий<br />

объем выделенной памяти.<br />

#include <br />

using namespace std;<br />

void __fastcall TForm1::VectorClick (TObject *Sender) {<br />

vector n;<br />

vector::iterator j;<br />

vector::reverse_iterator rj;<br />

ListBox1->Items->Add (n.size ());<br />

ListBox1->Items->Add (n.capacity ()); // 0 0<br />

for (int i=0; iItems->Add (n.size ());<br />

ListBox1->Items->Add (n.capacity ()); // 10 16<br />

for (j = n.begin (); j!=n.end (); ++j) ListBox2->Items->Add (*j); // 0,1,...,9<br />

for (rj = n.rbegin (); rj!=n.rend (); ++rj) ListBox3->Items->Add (*rj); // 9,8,...,0<br />

n.erase (n.begin ()+5, n.begin ()+7);<br />

// удаление 5-6-го<br />

n.insert (n.end ()-2, -1);<br />

// вставка –1 3-м с конца<br />

n.push_back (10);<br />

// вставка 10 в конец<br />

ListBox1->Items->Add (n.size ());<br />

ListBox1->Items->Add (n.capacity()); // 10 16<br />

for (j=n.begin (); j!=n.end (); ++j) ListBox4->Items->Add (*j);<br />

// 0,1,2,3,4,7,-1,8,9,10<br />

n.clear ();<br />

}<br />

Контейнер deque из одноименного файла заголовка, в отличие от vector,<br />

может заполняться и освобождаться от объектов с двух сторон.


91<br />

#include <br />

using namespace std;<br />

void __fastcall TForm1::DequeClick (TObject *Sender) {<br />

deque n;<br />

deque::iterator j;<br />

deque::reverse_iterator rj;<br />

for (int i=0; iAdd (*j); // 4,3,2,1,0,5,6,7,8,9<br />

for (rj=n.rbegin (); rj!=n.rend (); ++rj) ListBox2->Items->Add (*rj);<br />

// 9,8,7,6,5,0,1,2,3,4<br />

n.pop_front ();<br />

n.pop_back ();<br />

for (j = n.begin (); j!=n.end (); ++j) ListBox3->Items->Add (*j); // 3,2,1,0,5,6,7,8<br />

n.erase (n.begin ()+5, n.begin ()+7);<br />

// удаление 5-6-го<br />

n.insert (n.end()-2, -1);<br />

// вставка –1 3-м с конца<br />

for (j = n.begin (); j!=n.end (); ++j) ListBox4->Items->Add (*j); // 3,2,1,0,-1,5,8<br />

n.clear ();<br />

}<br />

Контейнер map организован в виде множества упорядоченных пар<br />

(иногда троек). Каждая пара состоит из первого члена (first), являющегося<br />

ключом, и второго члена (second) – значения объекта. Сортировка пар выполняется<br />

автоматически по ключу, хотя возможна и программная сортировка.<br />

#include <br />

#include <br />

using namespace std;<br />

void __fastcall TForm1::MapClick (TObject *Sender) {<br />

map m;<br />

map::iterator j;<br />

double r = 10;<br />

m["pi"] = 3.14159;<br />

m["e"] = 2.718;<br />

m["Скорость света"] = 2.998E8;<br />

m["Площадь круга"] = pow(r, 2) * m["pi"];<br />

for (j=m.begin (); j!=m.end (); ++j)


}<br />

92<br />

ListBox1->Items->Add (j->first + '=' + FloatToStr (j->second));<br />

// e=2.718; pi=3.14159; Площадь круга=314.159; Скорость света=2998...<br />

Алгоритм find выполняет поиск объектов контейнера между двумя установленными<br />

итераторами с возвращением итератора найденного объекта, а<br />

в случае отсутствия такового – с возвращением второго итератора.<br />

#include <br />

#include <br />

using namespace std;<br />

void __fastcall TForm1::findClick (TObject *Sender) {<br />

vector v;<br />

vector::iterator j;<br />

int what = 3;<br />

for (int i=-5; iItems->Add (*j);<br />

j = std::find (v.begin (), v.end (), what);<br />

if (j != v.end ()) ShowMessage ("Нашел " + AnsiString (*j)); // Нашел 3<br />

else ShowMessage ("Не нашел");<br />

}<br />

Алгоритмы min и max отыскивают экстремумы.<br />

#include <br />

using namespace std;<br />

void __fastcall TForm1::minMaxClick (TObject *Sender) {<br />

ListBox1->Items->Add (max (15, 5)); // 15<br />

ListBox1->Items->Add (max ('a', 'b')); // b<br />

ListBox1->Items->Add (min (3.14, 24.5)); // 3,14<br />

}<br />

Алгоритмы random_shuffle и sort соответственно перемешивают и сортируют<br />

объекты контейнера.<br />

#include <br />

#include <br />

#include <br />

using namespace std;<br />

void __fastcall TForm1::sortClick (TObject *Sender) {<br />

vector v;


vector::iterator j;<br />

93<br />

randomize ();<br />

for (int i='a'; iItems->Add (*j);<br />

std::sort (v.begin (), v.end ());<br />

for (j = v.begin (); j!=v.end (); ++j) ListBox2->Items->Add (*j);<br />

std::sort (v.begin (), v.end (), greater ());<br />

// ab...z<br />

for (j = v.begin (); j!=v.end (); ++j) ListBox3->Items->Add (*j); // zy...a<br />

}<br />

Алгоритм binary_search ищет объекты на бинарном дереве.<br />

#include <br />

using namespace std;<br />

void __fastcall TForm1::BinSearchClick (TObject *Sender) {<br />

int i, d[] = { 5, 2, 3, 8, 3, 1, 3 };<br />

const int D = sizeof (d) / sizeof (int);<br />

for (i=0; iItems->Add (d[i]);<br />

std::sort (d, d+D);<br />

for (i=0; iItems->Add (IntToStr (i) +<br />

(binary_search (d, d+D, i) ? " есть" : " нет"));<br />

AnsiString s1[] = {"раз", "два", "четыре", "пять" },<br />

s2[] = { "раз", "три", "пять", "семь"};<br />

const int S1 = sizeof (s1) / sizeof (s1[0]), S2 = sizeof (s2) / sizeof (s2[0]);<br />

for (i=0; iItems->Add (s1[i]);<br />

std::sort (s1, s1+S1);<br />

for (i=0; iItems->Add (s2[i] +<br />

(binary_search (s1, s1+S1, s2[i]) ? " есть" : " нет"));<br />

}<br />

Контейнер set поддерживает уникальные ключи и двунаправленные<br />

итераторы, являясь хранилищем упорядоченных объектов, удобным для поиска<br />

и сравнения с аналогичными хранилищами. Он незаменим для выполнения<br />

классических операций на множествах: принадлежности, объединения,<br />

пересечения, вычитания, дополнения. В отличие от vector и map, контейнер<br />

set не позволяет непосредственно воздействовать на значения объектов, требуя<br />

вместо этого вставки измененных объектов извне. В следующем примере<br />

множество s1 заполняется числами, и эти же числа копируются в s2. Затем<br />

отыскивается и удаляется первая половина s2, остаток заполняется новыми


94<br />

числами, и вычисляются результаты объединения и пересечения s1 и s2. В<br />

программе работают алгоритмы advance – перемещение итератора, set_union<br />

– объединение, set_intersection – пересечение множеств.<br />

#include <br />

#include <br />

using namespace std;<br />

void __fastcall TForm1::SetClick (TObject *Sender) {<br />

set s1, s2, s3, s4;<br />

set::iterator j;<br />

for (int i=0; i>1);<br />

s2.erase (s2.begin (), j);<br />

for (int i=10; iItems->Add (*j); // 0...9<br />

for (j=s2.begin (); j!=s2.end (); ++j) ListBox2->Items->Add (*j); // 5...14<br />

for (j=s3.begin (); j!=s3.end (); ++j) ListBox3->Items->Add (*j); // 0...14<br />

for (j=s4.begin (); j!=s4.end (); ++j) ListBox4->Items->Add (*j); // 5...9<br />

}<br />

Наследование<br />

Объектно-ориентированное программирование предусматривает организацию<br />

иерархических связей между абстрактными типами, создаваемыми<br />

программистом с помощью классов. Такие связи реализуются через механизм<br />

наследования, то есть создания новых типов, которым автоматически<br />

передаются данные и функции порождающих их типов – базовых классов.<br />

Общий формат объявления производного, или порожденного, класса:<br />

class имяКласса : [ типДоступа ]<br />

имяБазовогоКласса… {[ Метка ] объявлениеЧленов... };<br />

Права доступа к наследуемым членам определяются типом доступа, назначаемого<br />

одной из лексем: private, protected и public. При любом типе доступа<br />

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


95<br />

классу. При типе доступа private или при отсутствии указания на тип доступа<br />

наследование считается закрытым, то есть наследуемые члены базового<br />

класса с типами доступа public и protected становятся закрытыми членами<br />

производного класса. При типе доступа protected речь идет о защищенном<br />

наследовании, при котором члены базового класса public и protected становятся<br />

защищенными членами производного класса. Тип доступа public соответствует<br />

открытому наследованию, сохраняющему тип доступа базового<br />

класса.<br />

Базовых классов может быть несколько, и в таком случае говорят о<br />

множественном наследовании (multiple inheritance) в отличие от наследования<br />

единичного (unit inheritance). Но класс не может наследовать самого себя.<br />

Пример интегрирования некоторой функции R*pow (sin(t), 2) методом<br />

прямоугольников с использованием класса VRectangle, производного от<br />

VFunction:<br />

class VFunction {<br />

public:<br />

float Time (float R, float t) { return R*pow (sin (t), 2); }<br />

};<br />

class VRectangle: public VFunction {<br />

public:<br />

float step;<br />

VRectangle (float tk) { step = tk / 100; }<br />

};<br />

void MainVIntegral () {<br />

float j, tk = 0.8, t, sum;<br />

VRectangle myIntegral (tk);<br />

for (t=0, sum=0; t


96<br />

Пример программы решения системы алгебраических уравнений с<br />

привлечением механизма наследования:<br />

class VDet {<br />

public:<br />

float re; VDet (float r[]) { re = r[0] * r[4] - r[1] * r[3]; }<br />

};<br />

class VEquX: public VDet {<br />

public:<br />

float rx; VEquX (float r[]): VDet (r) { rx = r[2] * r[4] - r[1] * r[5]; }<br />

};<br />

class VEquY: public VDet {<br />

public:<br />

float ry; VEquY (float r[]): VDet (r) { ry = r[0] * r[5] - r[2] * r[3]; }<br />

};<br />

void MainVEqu() {<br />

// x + 4y = 9; -x + 6y = 1<br />

float member[] = { 1, 4, 9, -1, 6, 1 }; VEquX x (member); VEquY y (member);<br />

}<br />

ShowMessage ("x = " + AnsiString (x.rx / x.re) + '\n' // x = 5<br />

+ "y = " + AnsiString (y.ry / y.re)); // y = 1<br />

Здесь списки инициализации конструкторов пpоизводных классов VEquX,<br />

VEquY содержат запись VDet (r). Они инициализируют в базовом классе VDet<br />

те части классов EquX, EquY, которые принадлежат Det.<br />

Еще один пример наследования:<br />

class VStudent {<br />

protected:<br />

int course, group;<br />

public:<br />

int fac;<br />

AnsiString result;<br />

VStudent (AnsiString name, int y) {<br />

course = 10 – y / 1000; group = y; fac = group / 100 % 10;<br />

result = "\nСтудент " + name + " с факультета " + fac;<br />

}<br />

};<br />

class VBachalour: public VStudent {<br />

public:


};<br />

int spe;<br />

VBachalour (AnsiString n, int a, int b): VStudent (n,a) {<br />

spe = b;<br />

result += "\n" + AnsiString (course) + " курса " + AnsiString (group) +<br />

" группы – бакалавр по специальности " + AnsiString(b);<br />

}<br />

97<br />

void MainBac () {<br />

VBachalour first ("Сидоров", 6431, 225);<br />

if (first.spe > 200 && first.fac == 4)<br />

first.result += "\nЭто специалист по автоматике";<br />

else<br />

first.result += "\nЭто специалист по электротехнике";<br />

ShowMessage (first.result);<br />

}<br />

Пример множественного наследования:<br />

AnsiString result;<br />

class VStud {<br />

protected:<br />

__int64 group, fac, course;<br />

public:<br />

VStud (AnsiString name, __int64 x) {<br />

group = x;<br />

course = 10 - group / 1000;<br />

fac = group / 100 % 10;<br />

if (course > 0 && course < 6)<br />

result = "\nСтудент " + name + " факультета " + AnsiString (fac)<br />

+ "\nкурса " + AnsiString (course) + " группы " + AnsiString (group);<br />

else course = 0;<br />

}<br />

};<br />

class VPensioner {<br />

protected:<br />

__int64 zip;<br />

public:<br />

VPensioner (AnsiString name, __int64 z): zip (z) {<br />

if (zip > 195000L && zip < 200000L)<br />

result = "\nПенсионер " + AnsiString (name) + " из Петербурга";<br />

else zip = 0;<br />

}<br />

};


98<br />

class VPassenger: VStud, VPensioner {<br />

public:<br />

VPassenger (AnsiString name, __int64 data):<br />

VStud (name, data), VPensioner (name, data) {<br />

if (course>0 || zip) { result +="\n имеет право на льготы"; }<br />

else result = "\n Пассажир не имеет прав на льготы";<br />

ShowMessage (result);<br />

}<br />

};<br />

void MainVPassenger() {<br />

VPassenger w ("Сидоров", 8431);<br />

VPassenger x ("Козлов", 197376L);<br />

VPassenger y ("Тихонов", 1234);<br />

VPassenger z ("Степанов", 1);<br />

}<br />

Виртуальные функции и абстрактные классы<br />

При обычном наследовании объект производного класса обладает<br />

свойствами и поведением объектов базового класса. В этом случае говорят о<br />

раннем, или статическом, связывании на стадии компиляции. Виртуальными<br />

(virtual function) называются функции, которые определяются на стадии исполнения,<br />

то есть производится их позднее, или динамическое, связывание.<br />

Для этого объект производного класса снабжается скрытым указателем на<br />

базовый класс, который используется компилятором на стадии выполнения<br />

программы.<br />

При объявлении виртуальной функции к ее имени добавляется лексема<br />

virtual, что позволяет производным классам при необходимости переопределять<br />

такую функцию без изменения ее имени в списке параметров:<br />

virtual Тип имя<strong>Функции</strong> ([ объявлениеПараметров ]);<br />

«Виpтуальные функции служат центpальным моментом объектноориентированного<br />

программирования, потому что они позволяют опpеделять<br />

базовый класс общего назначения, не тpебуя знания особенностей, котоpые<br />

могут быть пpедусмотpены пpоизводным классом. Пpогpамма на C++ без<br />

виpтуальных функций, веpоятно, пpосто невеpно спpоектиpована» [Голуб].<br />

Иными словами, виpтуальные функции опpеделяют возможности, котоpыми<br />

обладают пpоизводные классы, но котоpые не могут быть pеализованы на<br />

уpовне базового класса.


class VMarket {<br />

public:<br />

virtual void Market1 () { ShowMessage ("Телевизоры"); }<br />

void Market2 () { ShowMessage ("Компьютеры"); }<br />

};<br />

99<br />

class VSection: public VMarket {<br />

public:<br />

void Market1 () { ShowMessage ("SONY"); }<br />

void Market2 () { ShowMessage ("MACINTOSH"); }<br />

};<br />

void MainVirtual1 () {<br />

VMarket *m = new VSection; // ссылка на производный класс<br />

m->Market1 ();<br />

// SONY<br />

m->Market2 ();<br />

// Компьютеры<br />

}<br />

Более сложный пример помогает разобраться с различными вариантами<br />

наследования:<br />

AnsiString result;<br />

class VProcessor {<br />

int frequency;<br />

public:<br />

VProcessor (int j = 100): frequency (j) {}<br />

virtual void Out () { result += "\nПроцессор " + AnsiString (frequency); }<br />

void Out (AnsiString s) { result += "\n" + s + AnsiString (frequency); }<br />

};<br />

class VComputer: public VProcessor {<br />

int hardDisk;<br />

public:<br />

VComputer (int j = 800): VProcessor (133), hardDisk (j) {}<br />

void Out () { result += "\nКомпьютер " + AnsiString (hardDisk); }<br />

int Out (AnsiString s) {<br />

result += "\n" + s + AnsiString (hardDisk);<br />

return hardDisk;<br />

}<br />

};<br />

void MainVirtual2 () {<br />

VProcessor intel, motorola (150), *cirix;<br />

VComputer ibm, macintosh (200),


*proforma = &macintosh;<br />

100<br />

intel.Out (); // Процессор 100<br />

motorola.Out (); // Процессор 150<br />

ibm.Out (); // Компьютер 800<br />

macintosh.Out (); // Компьютер 200<br />

intel.Out ("Intel "); // Intel 100<br />

motorola.Out ("Motorola "); // Motorola 150<br />

ibm.Out ("IBM "); // IBM 800<br />

macintosh.Out ("Macintosh "); // Macintosh 200<br />

cirix = proforma;<br />

cirix->Out (); // Компьютер 200<br />

cirix->Out ("Macintosh "); // Macintosh 133<br />

proforma->Out (); // Компьютер 200<br />

proforma->Out ("Macintosh "); // Macintosh 200<br />

ShowMessage (result);<br />

}<br />

Виртуальная функция, не определенная в базовом классе, называется<br />

чистой виртуальной функцией (pure virtual function) и объявляется как<br />

virtual Тип имя<strong>Функции</strong> ([ объявлениеПараметров ]) = 0;<br />

Такая функция полностью определяется в производных классах. Базовый<br />

класс, содержащий, по крайней мере, одну чистую виртуальную функцию,<br />

называется абстрактным классом (abstract class).<br />

Абстрактный класс может использоваться лишь в качестве базового<br />

класса для некоторого другого класса. Никакие объекты абстрактного класса<br />

не могут создаваться иначе, как подобъекты, представляющие базовый класс<br />

в объектах некоторого производного от него класса.<br />

class VNewMarket {<br />

public:<br />

virtual void Market1 () = 0;<br />

void Market2 () { ShowMessage ("Компьютеры"); }<br />

};<br />

class VNewSection: public VNewMarket {<br />

public:<br />

void Market1 () { ShowMessage ("SONY"); }<br />

void Market2 () { ShowMessage ("MACINTOSH"); }<br />

};


101<br />

void MainVirtual3 () {<br />

VNewMarket *m = new VNewSection; // ссылка на производный класс<br />

m->Market1 ();<br />

// SONY<br />

m->Market2 ();<br />

// Компьютеры<br />

}<br />

В следующем примере абстрактный класс управляет выводом на экран<br />

разнообразной информации:<br />

class VMan {<br />

protected: long course;<br />

public: virtual AnsiString Out (AnsiString) = 0;<br />

};<br />

class VStudent: public VMan {<br />

protected:<br />

__int64 group, fac;<br />

public:<br />

VStudent (__int64 x) {<br />

group = x; fac = group / 100 % 10; course = 10 - x / 1000;<br />

if (course < 1 || course > 5) course = 0;<br />

}<br />

AnsiString Out (AnsiString name) {<br />

return "\nСтудент " + name + " факультета " + AnsiString (fac)<br />

+ "\nкурса " + AnsiString (course) + " группы " + AnsiString (group);<br />

}<br />

};<br />

class VPensioner: public VMan {<br />

public:<br />

VPensioner (__int64 x) {<br />

if (x > 195000L && x < 200000L) course = x;<br />

else course = 0;<br />

}<br />

AnsiString Out (AnsiString name) {<br />

return "\nПенсионер " + AnsiString(name) + " из Петербурга";<br />

}<br />

};<br />

class VPassenger: VStudent, VPensioner {<br />

public:<br />

AnsiString result;<br />

VPassenger (AnsiString name, __int64 data):<br />

VStudent (data), VPensioner (data) {<br />

If (!(VStudent::course) && !(VPensioner::course))<br />

ShowMessage (" Пассажир не имеет права на льготы.");


102<br />

};<br />

}<br />

}<br />

else {<br />

if (VStudent::course) result = VStudent::Out (name);<br />

if (VPensioner::course) result = VPensioner::Out (name);<br />

ShowMessage (result + "\nимеет право на льготы.");<br />

void MainVirtual4 () {<br />

VPassenger w ("Сидоров", 8431);<br />

VPassenger x ("Козлов", 197376L);<br />

VPassenger y ("Тихонов", 1234);<br />

VPassenger z ("Степанов", 1);<br />

}


103<br />

Список литературы<br />

1. C++Builder 5. Руководство разработчика. Уч. пособие в 2х т. //<br />

Д.Холингвэрт, Д.Баттерфилд, Б.Сворт, Д.Оллсоп. – М., Изд. дом «Вильямс»,<br />

2001. 880 и 832 с.<br />

2. Архангельский А.Я. Программирование в C++Builder 6. – М., Бином,<br />

2003. 1152 с.<br />

3. Бабэ Б. Просто и ясно о Borland C++ – М. Бином, 1995. 400 с.<br />

4. Голуб А. Пpавила пpогpаммиpования C & C++. – М.. Бином, 1996. 272 с.<br />

5. Кнут Д. Искусство программирования для ЭВМ. В 3-х кн. – М. Миp, 1999.<br />

6. Культин Н.Б. Самоучитель С++Builder. – СПб., БХВ-Петербург, 2004. 320<br />

с.<br />

7. Павловская Т.А. С/С++. Программирование на языке высокого уровня. –<br />

СПб., Питер, 2003. 360 с.<br />

8. Сван Т. Освоение Borland C++ 4.5. – Киев. Диалектика. 1996. 400 с.<br />

9. Шамис В.А. Borland C++Builder 6.0. Для профессионалов. – СПб., Питер,<br />

2003. 798 с.


104<br />

Валерий Михайлович Водовозов,<br />

Александр Константинович Пожидаев<br />

Разработка программ под Windows<br />

Учебное пособие<br />

Редактор Н. В. Лукина<br />

Подписано в печать Формат 60×84 1/16. Бумага офсетная.<br />

Печать офсетная. Гарнитура «Times». Печ. л. 4,5.<br />

Тираж 200 экз. Заказ<br />

Издательство СПбГЭТУ «ЛЭТИ»<br />

197376, С.-Петербург, ул. Проф. Попова, 5

Hurra! Ihre Datei wurde hochgeladen und ist bereit für die Veröffentlichung.

Erfolgreich gespeichert!

Leider ist etwas schief gelaufen!