Функции - eDrive
Функции - eDrive
Функции - eDrive
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 ¶m.<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