ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ ПРОГРАММИРОВАНИЕ И C++
3. ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ ПРОГРАММИРОВАНИЕ И C++
Объект - это абстрактная сущность, наделенная характеристиками объектов окружающего нас реального мира. Создание объектов и манипулирование ими - это вовсе не привилегия языка C++, а скорее результат методологии программирования, воплощающей в кодовых конструкциях описания объектов и операции над ними. Каждый объект программы, как и любой реальный объект, отличается собственными атрибутами и характерным поведением. Объекты можно классифицировать по разным категориям: например, мои цифровые наручные часы
"Cassio" принадлежат к классу часов. Программная реализация часов входит, как стандартное приложение, в состав операционной системы вашего компьютера.
Каждый класс занимает определенное место в иерархии классов, например, все часы принадлежат классу приборов измерения времени (более высокому в иерархии), а класс часов сам включает множество производных вариаций на ту же тему. Таким образом, любой класс определяет некоторую категорию объектов, а всякий объект есть экземпляр некоторого класса.
Объектно-ориентированное программирование (ООП) — это методика, которая концентрирует основное внимание программиста на связях между объектами, а не на деталях их реализации. В этой главе основные принципы ООП (инкапсуляция, наследование, полиморфизм, создание классов и объектов) интерпретируются и дополняются новыми понятиями и терминологией, принятыми интегрированной средой визуальной обработки C++Builder. Приводится описание расширений языка новыми возможностями (компоненты, свойства, обработчики событий) и последних дополнений стандарта ANSI C++ (шаблоны, пространства имен, явные и непостоянные объявления, идентификация типов при выполнении программы, исключения).
Глава носит обзорный характер, она призвана познакомить читателя со специальной терминологией ООП, к которой автор вынужден прибегать на протяжении всей книги. Это вызвано тем, что C++Builder является типичной системой ООП и претендует на кульминационную роль в истории его развития.
представляет член данных этого объекта, а кнопки управления - объектные методы. Нажимая кнопки часов, можно оперировать с установками времени на дисплее, т.е. следуя терминологии ООП, методы модифицируют состояние объекта путем изменения членов данных.
C++Builder вводит понятие компонент
(components) - специальных классов, свойства которых представляют атрибуты объектов, а их методы реализуют операции над соответствующими экземплярами компонентных классов. Понятие метод обычно используется в контексте компонентных классов и внешне не отличается от термина функция-член обычного класса. C++Builder позволяет манипулировать видом и функциональным поведением компонент не только с помощью методов (как это делают функции-члены обычных классов), но и посредством свойств и событий, присущих только классам компонент. Работая в среде C++Builder, вы наверняка заметите, что манипулировать с компонентным объектом можно как на стадии проектирования приложения, так и во время его выполнения.
Свойства (properties) компонент представляют собой расширение понятия членов данных и хотя не хранят данные как таковые, однако обеспечивают доступ к членам данных объекта. C++Builder использует ключевое слово _property для объявления свойств. При помощи событий (events) компонента сообщает пользователю о том, что на нее оказано некоторое предопределенное воздействие. Основная сфера применения методов в программах, разрабатываемых в среде C++Builder -это обработчики событий (event handlers), которые реализуют реакцию программы на возникновение определенных событий. Легко заметить некоторое сходство событий и сообщений операционной системы Windows. Типичные простые события —нажатие кнопки или клавиши на клавиатуре. Компоненты инкапсулируют свои свойства, методы и события.
На первый взгляд компоненты ничем не отличаются от других объектных классов языка C++, за исключением ряда особенностей, среди которых пока отметим следующие:
• Большинство компонент представляют собой элементы управления интерфейсом с пользователем, причем некоторые обладают весьма сложным поведением.
• Все компоненты являются прямыми или косвенными потомками одного общего класса-прародителя (TComponent).
• Компоненты обычно используются непосредственно, путем манипуляции с их свойствами; они сами не могут служить базовыми классами для построения новых подклассов.
• Компоненты размещаются только в динамической памяти кучи (heap) с помощью оператора
new, а не на стеке, как объекты обычных классов.
• Свойства компонент заключают в себе RTTI - идентификацию динамических типов.
• Компоненты можно добавлять к Палитре компонент и далее манипулировать с ними посредством Редактора форм интегрированной среды визуальной разработки C++Builder.
ООП интерпретирует взаимодействие с объектами как посылку запросов некоторому объекту или между объектами. Объект, принявший запрос, реагирует вызовом соответствующего метода. В отличие от других языков ООП, таких как SmallTalk, C++ не поощряет использование понятия "запрос". Запрос - это то, что делается с объектом, а метод - это то, как объект реагирует на поступивший запрос.
При ближайшем рассмотрении метод оказывается обычной функцией-членом, которая включена в определение класса. Чтобы вызвать метод, надо указать имя функции в контексте данного класса или в обработчике некоторого события.
Именно скрытая связь метода с включающим классом выделяет его из понятия простой функции. Во время выполнения метода он имеет доступ ко всем данным своего класса, хотя и не требует явной спецификации имени этого класса. Это обеспечивается передачей каждому, без исключения, методу скрытого параметра - непостоянного указателя this на экземпляр класса. При любом обращении метода к членам данных класса, компилятор генерирует специальный код, использующий указатель this.
3.3 Наследование
Одной из самых восхитительных особенностей живой природы является ее способность порождать потомство, обладающее характеристиками, сходными с характеристиками предыдущего поколения. Заимствованная у природы идея наследования решает проблему модификации поведения объектов и придает ООП исключительную силу и гибкость. Наследование позволяет, практически без ограничений, последовательно строить и расширять классы, созданные вами или кем-то еще. Начиная с самых простых классов, можно создавать производные классы по возрастающей сложности, которые не только легки в отладке, но и просты по внутренней структуре.
Последовательное проведение в жизнь принципа наследования, особенно при разработке крупных программных проектов, хорошо согласуется с техникой нисходящего структурного программирования (от общего к частному), и во многом стимулирует такой подход. При этом сложность кода программы в целом существенно сокращается. Производный класс (потомок) наследует все свойства, методы и события своего базового класса (родителя) и всех его предшественников в иерархии классов.
При наследовании базовый класс обрастает новыми атрибутами и операциями. В производном классе обычно объявляются новые члены данных, свойства и методы. При работе с объектами программист обычно подбирает наиболее подходящий класс для решения конкретной задачи и создает одного или нескольких потомков от него, которые приобретают способность делать не только то, что заложено в родителе. Дружественные функции позволяют производному классу получить доступ ко всем членам данных внешних классов.
Кроме того, производный класс может перегружать (overload)
наследуемые методы в том случае, когда их работа в базовом классе не подходит потомку. Использование перегрузки в ООП всячески поощряется, хотя в прямом понимании значения этого слова перегрузок обычно избегают. Говорят, что метод перегружен, если он ассоциируется с более чем одной одноименной функцией. Обратите внимание, что механизм вызовов перегруженных методов в иерархии классов полностью отличается от вызовов переопределенных функций. Перегрузка и переопределение - это разные понятия. Виртуальные методы
используются для переопределения функций базового класса.
Чтобы применить концепцию наследования, к примеру, с часами, положим, что следуя принципу наследования, фирма "Casio" решила выпустить новую модель, дополнительно способную, скажем, произносить время при двойном нажатии любой из существующих кнопок. Вместо того, чтобы проектировать заново модель говорящих часов (новый класс, в терминологии ООП), инженеры начнут с ее прототипа (произведут нового потомка базового класса, в терминологии ООП). Производный объект унаследует все атрибуты и функциональность родителя. Произносимые синтезированным голосом цифры станут новыми членами данных потомка, а объектные методы кнопок должны быть перегружены, чтобы реализовать их дополнительную функциональность. Реакцией на событие двойного нажатия кнопки станет новый метод, который реализует произнесение последовательности цифр (новых членов данных), соответствующей текущему времени. Все вышесказанное в полной мере относится к программной реализации говорящих часов.
3.4 Разработка классов
В классы разрабатываются для достижения определенных целей. Чаще всего программист начинает с нечетко очерченной идеи, которая постепенно, по мере разработки проекта, пополняется деталями. Иногда дело заканчивается несколькими классами, весьма похожими друг на друга. Чтобы избежать подобного дублирования кодов в классах, следует разбить их на две части, определив общую часть в родительском классе, а отличающиеся оставить в производных.
Объявление класса должно предшествовать его использованию. Как правило, прикладной программист пользуется готовыми базовыми классами, причем ему вовсе не обязательно разбираться во всех спецификациях и во внутренней реализации. Однако, чтобы использовать базовый класс C++, надо обязательно знать какие члены данных и методы вам доступны (а если применяется компонента C++Builder - еще и предоставляемые свойства и события).
3.4.1 Объявление базового класса
C++Builder дает вам возможность объявить базовый класс, который инкапсулирует имена своих свойств, данных, методов и событий. Помимо способности выполнять свою непосредственную задачу объектные методы получают определенные привилегии доступа к значениям свойств и данных класса.
Каждое объявление внутри класса определяет привилегию доступа к именам класса в зависимости от того, в какой секции имя появляется. Каждая секция начинается с одного из ключевых слов: private, protected и public. Листинг 3.1 иллюстрирует обобщенный синтаксис объявления базового класса.
class className
private:
<приватные члены данных> <приватные конструкторы> <приватные методы>
protected:
<защищенные члены данных> <защищенные конструкторы> <защищенные методы>
public:
<общедоступные свойства> <общедоступные члены данных> “збщедоступные конструкторы> <общедоступный деструктор> общедоступные методы>
Листинг 3.1. Объявление базового класса.
Таким образом, объявление базового класса на C++ предоставляет следующие права доступа и соответствующие области видимости:
• Приватные private имена имеют наиболее ограниченный доступ, разрешенный только методам данного класса. Доступ производных классов к приватным методам базовых классов запрещен.
• Защищенные protected имена имеют доступ, разрешенный методам данного и производных от него классов.
• Общедоступные public имена имеют неограниченный доступ, разрешенный методам всех классов и их объектов.
Следующие правила применяются при образовании различных секций объявления класса:
1. Секции могут появляться в любом порядке, а их названия могут встречаться повторно.
2. Если секция не названа, компилятор считает последующие объявления имен класса приватными. Здесь проявляется отличие объявлений класса и структуры - последняя рассматривается по умолчанию как общедоступная.
3. По мере возможности не помещайте члены данных в общедоступную секцию, если только вы действительно не хотите разрешить доступ к ним отовсюду. Обычно их объявляют защищенными, чтобы разрешить доступ только методам производных классов.
4. Используйте методы для выборки, проверки и установки значений свойств и членов данных.
5. Конструкторы и деструкторы являются специальными функциями, которые не возвращают значения и имеют имя своего класса. Конструктор строит объект данного класса, а деструктор его удаляет.
6. Методы (так же как конструкторы и деструкторы), которые содержат более одной инструкции C++, рекомендуется объявлять вне класса.
Листинг 3.2 представляет попытку наполнить объявление базового класса некоторым конкретным содержанием. Отметим характерное для компонентных классов C++Builder объявление свойства Count в защищенной секции, а метода SetCount, реализующего запись в член данных FCount -
в приватной секции.
class TPoint { private:
int FCount; // Приватный член данных void _fastcall SetCount(int Value);
protected:
_property int Count = // Защищенное свойство
{ read= FCount, write=SetCount };
double x; // Защищенный член данных
double у; // Защищенный член данных public:
TPoint(double xVal, double yVal); // Конструктор |
double getX(); |
double getY() ;
Листинг 3.2. Объявление базовой компоненты TPoint.
Объявления и определения методов хранятся в разных файлах (с расширениями .h и .срр, соответственно). Листинг 3. 3 показывает, что когда методы определяются вне класса, их имена следует квалифицировать. Синтаксис такой квалификации метода, определяющей его область видимости, имеет следующий вид:
<имя класса>::<имя метода>
TPoint::TPoint(double xVal, double yVal)
( // Тело конструктора
void _fastcall TPoint::SetCount( int Value )
{
l
if ( Value i= FCount ) // Новое значение члена данных? {
FCount = Value; // Запись нового значения Update(); // Вызов метода Update } }
double TPoint::getX()
// Тело метода getX,
квалифицированного в классе^TPoint
}
Листинг 3.3. Определения конструктора и методов вне класса.
После того, как вы объявили класс, его имя можно использовать как идентификатор типа при объявлении объекта этого класса (например,
TPoint* MyPoint;).
3.4.2 Конструкторы и деструкторы
Как следует из названий, конструктор - это метод, который строит в памяти объект данного класса, а деструктор - это метод, который его удаляет. Конструкторы и деструкторы отличаются от других объектных методов следующими особенностями:
• Имеют имя, идентичное имени своего класса.
• Не имеют возвращаемого значения.
• Не могут наследоваться, хотя производный класс может вызывать конструкторы и деструкторы базового класса.
• Автоматически генерируются компилятором как public, если не были вами объявлены иначе.
• Автоматически вызываются компилятором, чтобы гарантировать надлежащее создание и уничтожение объектов классов.
• Могут содержать неявные обращения к операторам new и delete,
если объект требует выделения и уничтожения динамической памяти.
Листинг 3.4 демонстрирует обобщенный синтаксис объявлений конструкторов и деструктора.
class className
{ public:
// Другие члены данных className(); // Конструктор по умолчанию | className(<список параметров;-);// Конструктор с аргументами | className(const className&); // Конструктор копирования
// Другие конструкторы "className(); // Деструктор
// Другие методы };
Листинг 3.4. Объявления конструкторов и деструктора.
Класс может содержать любое число конструкторов, в том числе ни одного. Конструкторы не могут быть объявлены виртуальными. Не помещайте все конструкторы в защищенной секции и старайтесь уменьшить их число, используя значения аргументов по умолчанию. Существует три вида конструкторов:
• Конструктор по умолчанию не имеет параметров. Если класс не содержит ни одного конструктора, компилятор автоматически создаст один конструктор по умолчанию, который просто выделяет память при создании объекта своего класса.
• Конструктор с аргументами позволяет инициализировать объект в момент его создания - вызывать различные функции, выделять динамическую память, присваивать переменным начальные значения и т.п.
• Конструктор копирования предназначен для создания объектов данного класса путем копирования данных из другого, уже существующего объекта этого класса. Такие конструкторы особенно целесообразны для создания копий объектов, которые моделируют динамические структуры данных. Однако, по умолчанию компилятор создает так называемые конструкторы поверхностного копирования (shallow copy constructors), которые копируют только члены данных. Поэтому если какие-то члены данных содержат указатели, сами данные не будут копироваться. Для реализации "глубокого" копирования в код конструктора надо включить соответствующие инструкции.
Класс может объявить только один общедоступный деструктор, имени которого, идентичному имени своего класса, должен предшествовать знак ~ (тильда). Деструктор не имеет параметров и может быть объявлен виртуальным. Если класс не содержит объявления деструктора, компилятор автоматически создаст его.
Обычно деструкторы выполняют операции, обратные тем, что выполняли соответствующие конструкторы. Если вы создали объект класса файл, то в деструкторе этот файл, вероятно, будет закрываться. Если конструктор класса выделяет динамическую память для массива данных (с помощью оператора new), то деструктор, вероятно, освободит выделенную память (с помощью оператора delete) и т.п.
3.4.3 Объявление производных классов
C++ Builder дает возможность объявить производный класс, который наследует свойства, данные, методы и события всех своих предшественников в иерархии классов, а также может объявлять новые характеристики и перегружать некоторые из наследуемых функций. Наследуя указанные характеристики базового класса, можно заставить порожденный класс расширить, сузить, изменить, уничтожить или оставить их без изменений.
Наследование позволяет повторно использовать код базового класса в экземплярах производного класса. Концепция повторного использования
имеет параллель в живой природе: ДНК можно рассматривать как базовый материал, который каждое порожденное существо повторно использует для воспроизведения своего собственного вида. <
Листинг 3.5 иллюстрирует обобщенный синтаксис объявления производного класса. Порядок перечисления секций соответствует расширений привилегий защиты и областей видимости заключенных в них элементов: от наиболее ограниченных к самым доступным.
class className : [^спецификатор доступа;”] parentClass {
<0бъявления дружественных классов>
private:
<приватные члены данных>
<приватные конструкторы>
<приватные методы> protected:
<защищенные члены данных>
<защищенные конструкторы>
<защищенные методы> public:
<общедоступные свойства>
<общедоступные члены данных>
<общедоступные конструкторы>
<общедоступный деструктор>
<общедоступные методы> _published:
•<общеизвестные свойства>
<общеизвестные члены данных>
<Объявления дружественных функций>
Листинг 3.5. Объявление производного класса.
Отметим появление новой секции с ключевым словом _published - дополнение, которое C++Builder вводит в стандарт ANSI C++ для объявления общеизвестных элементов компонентных классов. Эта секция отличается от общедоступной только тем, что компилятор генерирует информацию RTTI о свойствах, членах данных и методах объекта и C++Builder организует передачу этой информации Инспектору объектов во время исполнения программы. В главе 6 мы остановимся на этом более подробно.
Помимо способности выполнять свою непосредственную задачу объектные методы получают определенные привилегии доступа к значениям свойств и данных других классов.
Когда класс порождается от базового, все его имена в производном классе автоматически становятся приватными по умолчанию. Но его легко изменить, указав следующие спецификаторы доступа базового класса:
• private. Наследуемые (т.е. защищенные и общедоступные) имена базового класса становятся недоступными в экземплярах производного класса.
• public. Общедоступные имена базового класса и его предшественников будут доступными в экземплярах производного класса, а все защищенные останутся защищенными.
Можно порождать классы, которые расширяют
возможности базового класса:
он вполне приемлем для вас, однако содержит функцию, требующую небольшой доработки. Написание заново нужной функции в производном классе является пустой тратой времени. Вместо этого надо повторно использовать код в базовом классе, расширяя его настолько, насколько это необходимо. Просто переопределите в производном классе ту функцию базового класса, которая вас не устраивает. Подобным образом можно порождать классы, которые ограничивают возможности базового класса: он вполне приемлем для вас, но делает что-то лишнее.
Рассмотрим применение методик расширения и ограничения характеристик на примере создания разновидностей объекта кнопки - типичных производных классов, получаемых при наследовании базовой компоненты TButtonControl из Библиотеки Визуальных Компонент
C++Builder. Кнопки различного вида будут часто появляться в диалоговых окнах графического интерфейса ваших программ.
Рис. 3.1 показывает, что базовый класс TButtonControl способен с помощью родительского метода Draw отображать кнопку в виде двух вложенных прямоугольников: внешней рамки и внутренней закрашенной области.
Чтобы создать простую кнопку без рамки (Рис. 3.2), нужно построить производный класс
SimpleButton, использовав в качестве родительского TButtonControl, и перегрузить метод
Draw с ограничением его функциональности (Листинг 3.6)
class SimpleButton: public : TButtonControl { public:
SimpleButton(int x, int y) ;
void Draw() ;
-SimpleButton() { }
};
SimpleButton::SimpleButton(int x, int y) :
TButtonControl(x, y) { }
void SimpleButton::Draw()
{ ; outline->Draw();
}
Листинг 3.6. Ограничение характеристик базового класса.
Единственная задача конструктора объекта для SimpleButton - вызвать базовый класс с двумя параметрами. Именно переопределение метода SimpleButton: : Draw () предотвращает вывод обводящей рамки кнопки (как происходит в родительском классе). Естественно, чтобы изменить код метода, надо изучить его по исходному тексту базовой компоненты TButtonControl.
Теперь создадим кнопку с пояснительным названием (Рис. 3.3). Для этого нужно построить производный класс TextButton из базового TButtonControl, и перегрузить метод
Draw с рас-Рис. 3.3. Кнопка с текстом, ширением его функциональности.
Листинг 3.7 показывает, что объект названия title класса Text создается конструктором TextButton, а метод
SimpleButton:-.Draw () отображает его. :
class Text { public:
Text(int x, int y, char* string) { } void Draw() { } };
class TextButton: public : TButtonControl {
Text* title;
public:
TextButton(int x, int y, char* title);
void Draw();
-TextButton() { } );
TextButton::TextButton(int x, int y, char* caption)
TButtonControl(x, y) {
title = new Text(x, y, caption);
}
void TextButton::Draw () {
TextButton::Draw() ;
title->Draw() ;
}
Листинг 3.7. Расширение характеристик базового класса.
В заключение раздела с изложением методики разработки базовых и производных классов приводится фрагмент C++ программы (Листинг 3.8), в которой объявлена иерархия классов двух простых геометрических объектов: окружности и цилиндра.
Программа составлена так, чтобы внутренние значения переменных г-радиус окружности и h-высота цилиндра определяли параметры создаваемых объектов. Базовый класс
Circle моделирует окружность, а производный класс Cylinder моделирует цилиндр.
class SimpleButton: public : TButtonControl { public:
SimpleButton (int x, int y) ;
void Draw() ;
-SimpleButton() { } );
SimpleButton::SimpleButton(int x, int y) :
TButtonControl(x, y) { }
I void SimpleButton::Draw()
I { i outline->Draw();
1 )
Листинг 3.6. Ограничение характеристик базового класса.
Единственная задача конструктора объекта для SimpleButton - вызвать базовый класс с двумя параметрами. Именно переопределение метода SimpleButton: : Draw () предотвращает вывод обводящей рамки кнопки (как происходит в родительском классе). Естественно, чтобы изменить код метода, надо изучить его по исходному тексту базовой компоненты TButtonControl.
Теперь создадим кнопку с пояснительным названием (Рис. 3.3). Для этого нужно построить производный класс TextButton из базового TButtonControl, и перегрузить метод
Draw с рас-Рис. 3.3. Кнопка с текстом, ширением его функциональности.
Листинг 3.7 показывает, что объект названия title класса Text создается конструктором TextButton, а метод
SimpleButton: : Draw () отображает его.
const double pi = 4 * atan(l);
class Circle { protected:
double r ;
public:
Circle (double rVal =0) : r(rVal) {}
void setRadius(double rVal) { r = rVal; }
double getRadiusO { return r; } .double Area() { return
pi*r*r; }
void showData() ;
};
class Cylinder : public Circle { protected:
double h;
public:
Cylinder(double hVal = 0, double rVal = 0)
: getHeight(hVal), Circle(rVal) { }
void setHeight(double hVal) { h = hVal; }
double getHeight() { return h; }
double Area() { return 2*Circle::Area()+2*pi*r*h; }
void showData() ;
void Circle::showData() {
cout “ "Радиус окружности = " “ getRadius() “ endl
“ "Площадь круга = " “ Area О “ endl “ endl;
}
void Cylinder::showData()
{
cout “ "Радиус основания = " “ getRadius() “ endl
“ "Высота цилиндра = " “ getHeight() “ endl
“ "Площадь поверхности = " “ Area () “ endl;
}
void main()
{
Circle circle(2) ;
Cylinder cylinder(10, 1);
circle.showData () ;
cylinder.showData() ;
Листинг 3.8. Простая иерархия классов окружности и цилиндра.
Объявление класса Circle содержит единственный член данных r, конструктор и ряд методов. При создании объекта конструктор инициализирует член данных r начальным значением радиуса окружности. Отметим новый синтаксис конструктора: при вызове он может обратиться к конструктору базового класса, а также к любому члену данных, указанному после двоеточия. В нашем случае член данных r "создается" обращением к нему с параметром rVal и инициализируется нулевым значением.
Метод setRadius
устанавливает, a getRadius возвращает значение члена данных г. Метод Area возвращает площадь круга. Метод
showData выдает значения радиуса окружности и площади круга.
Класс Cylinder,
объявленный как производный от Circle, содержит единственный член данных h,
конструктор и ряд методов. Этот класс наследует член данных г для хранения радиуса основания цилиндра и методы setRadius и getRadius. При создании объекта конструктор инициализирует члены данных г и h начальными значениями. Отметим новый синтаксис конструктора: в нашем случае член данных h инициализируется значением аргумента hVal, а член данных г - вызовом конструктора базового класса
Circle с аргументом rVal.
Функция setHeight устанавливает, a getHeight возвращает значение члена данных h. Circle::Area перегружает унаследованную функцию базового класса, чтобы теперь возвращать площадь поверхности цилиндра. Функция showData выдает значения радиуса основания, высоты и площади поверхности цилиндра.
Функция main
создает окружность circle класса Circle с радиусом 2 и цилиндр cylinder класса Cylinder с высотой 10 и радиусом основания 1, а затем обращается к showData для вывода параметров созданных объектов:
Радиус окружности = 2 Площадь круга = 12.566
Радиус основания = 1 Высота цилиндра = 10 Площадь поверхности = 69.115
3.5 Полиморфизм
Слово полшюрфизм от греческих слов poly (много) и morphos (форма) означает множественность форм. Полиморфизм - это свойство родственных объектов (т.е. объектов, классы которых являются производными от одного родителя) вести себя по-разному в зависимости от ситуации, возникающей в момент выполнения программы. В рамках ООП программист может влиять на поведение объекта только косвенно, изменяя входящие в него методы и придавая потомкам отсутствующие у родителя специфические свойства.
Для изменения метода необходимо перегрузить его в потомке, т.е. объявить в потомке одноименный метод и реализовать в нем нужные действия. В результате в объекте-родителе и объекте-потомке будут действовать два одноименных метода, имеющие разную кодовую реализацию и, следовательно, придающие объектам разное поведение. Например, в иерархии родственных классов геометрических фигур (точка, прямая линия, квадрат, прямоугольник, окружность, эллипс и т.д.) каждый класс имеет метод Draw, который отвечает за надлежащий отклик на событие с требованием нарисовать эту фигуру.
Благодаря полиморфизму, потомки могут перегружать общие методы родителя с тем, чтобы реагировать специфическим образом на одно и то же событие.
3.5.1 Виртуальные функции
В ООП полиморфизм достигается не только описанным выше механизмом наследования и перегрузки методов родителя, но и виртуализацией,
позволяющей родительским функциям обращаться к функциям потомков. Полиморфизм реализуется через архитектуру класса, но полиморфными могут быть только функции-члены.
В C++ полиморфная функция привязывается к одной из возможных одноименных функций только в момент исполнения, когда ей передается конкретный объект класса. Другими словами, вызов функции в исходном тексте программы лишь обозначается, без точного указания на то, какая именно функция вызывается. Такой процесс известен как позднее связывание. Листинг 3.9 показывает, к чему может привести не полиморфное поведение обычных функций-членов.
I class Parent { public:
double Fl(double x) { return x*x; }
double F2(double x) { return Fl(x)/2; }
class Child : public Parent { public:
double Fl(double x) { return x*x*x; } };
void main() {
Child child;
cout “ child.F2(3) “ end1;
}
Листинг 3.9. Неопределенное позднее связывание.
Класс Parent
содержит функции-члены Fl и F2, причем F2 вызывает, El,. Класс Child, производный от класса Parent,
наследует функцию F2, однако переопределяет функцию Fl. Вместо ожидаемого результата 13.5 программа выдаст значение 4.5. Дело в том, что компилятор оттранслирует выражение child. F2 (3) в обращение к унаследованной функции Parent: :F2, которая в свою очередь вызовет Parent: :F1, а не Child: :F1, что поддержало бы полиморфное поведение.
C++ однозначно определяет позднее связывание в момент выполнения и обеспечивает полиморфное поведение функций посредством их виртуализации. Листинг 3.10 обобщает синтаксис объявления виртуальных функций в базовом и производном классах.
jclass classNamel {
// Другие функции-члены
virtual returnType functionName(<список параметров>) ;
};
class className2 : public classNamel {
// Другие функции-члены
virtual returnType functionName(<cmicoK параметров>) ;
};
Листинг 3.10. Объявление виртуальных функции в иерархии классов.
Чтобы обеспечить полиморфное поведение функции F1 в объектах классов Parent и Child, необходимо объявить ее виртуальной. Листинг 3.11 содержит модифицированный текст программы.
class Parent {
public:
virtual double F1(double x) { return x*x; }
double F2(double x) { return Fl(x)/2; }
};
class Child : public Parent { public:
virtual double F1(double x) { return x*x*x; }
);
void main() {
Child child;
cout “ child.F2(3) “ endl;
}
Листинг 3.11. Позднее связывание виртуальных функций.
Теперь программа выдаст ожидаемый результат 13.5. Компилятор оттранслирует выражение child. F2 (3) в обращение к унаследованной функции Parent: : F2, которая в свою очередь вызовет переопределенную виртуальную функцию потомка Child: :F1.
Если функция объявлена в базовом классе как виртуальная, ее можно переопределять только в производных классах и обязательно с тем же списком параметров. Если виртуальная функция производного класса изменила список параметров, то ее версия в базовом классе (и во всех его предшественниках) станет недоступной. Поначалу такая ситуация может показаться тупиковой - и на деле оказывается таковой в языках ООП, которые не поддерживают механизм перегрузки. C++ решает проблему, допуская использовать не виртуальные, а перегруженные функции с тем же именем, но с другим списком параметров.
Функция, объявленная виртуальной, считается таковой во всех производных классах - независимо от того, объявлена ли она в производных классах с ключевым словом virtual, или нет.
Используйте виртуальные функции для реализации специфического поведения объектов данного класса. Не объявляйте все ваши методы виртуальными - это приведет к дополнительным вычислительным затратам при их вызовах. Всегда объявляйте деструкторы виртуальными. Это обеспечит полиморфное поведение при уничтожении объектов в иерархии классов.
3.5.2 Дружественные функции
Дружественные функции, хотя и не принадлежат какому-то классу, однако имеют доступ ко всем приватным и защищенным членам данных внешних классов. Листинг 3.12 обобщает синтаксис объявления дружественных функций с помощью ключевого слова friend перед указанием возвращаемого типа.
class className
( 1
public: ~
className(); // Конструктор по умолчанию // Другие конструкторы friend
returnType friendFunction(<список параметров>) ;
};
Листинг 3.12. Объявление дружественных функций.
Если обычные функции-члены имеют автоматический доступ ко всем данным своего класса за счет передачи скрытого параметра - указателя this на экземпляр класса, то дружественные функции требуют явной спецификации этого параметра. Действительно, объявленная в классе Х дружественная функция F не принадлежит этому классу, а, значит, не может быть вызвана операторами х. F и xptr->F (где х- экземпляр класса X, a xptr- его указатель). Синтаксически корректными будут обращения F (& х) или F (xpt r).
Таким образом, дружественные функции могут решать задачи, реализация которых посредством функций-членов класса оказывается неудобной, затруднительной и даже невозможной.
3.6 Новые возможности языка C++
C++Builder обеспечивает не только поддержку последних нововведении стандарта ANSI
C++, но и расширяет язык новыми возможностями. Компоненты, свойства, методы, обработчики событии, а также шаблоны, пространства имен, явные и непостоянные объявления, RTTI и исключения - вся мощь этих средств доступна программистам, использующим C++Builder для визуальной разработки приложений.
Важно понять, что расширения языка никогда не являются самоцелью, и вы по-прежнему сможете компилировать тексты, написанные в рамках стандартного C++. Однако, чтобы воспользоваться в полной мере преимуществами, которые предоставляет C++Builder для технологии быстрой разработки приложений (RAD), вам придется принять введенные расширения языка.
Некоторые из расширений (например, _classid) C++ Builder резервирует, главным образом, для внутреннего использования. Другие расширения совершенно очевидны (_int8, _intl6 и т.д.), и здесь не рассматриваются. Наше внимание будет сфокусировано на наиболее значимых расширениях C++, которые, в основном, относятся к компонентным классам и будут постоянно встречаться как в тексте книги, так и в ваших приложениях, разрабатываемых в среде C++Builder.
3.6.1 Компоненты
Компоненты часто достигают более высокой степени инкапсуляции, нежели стандартные C++ классы. Проиллюстрируем это на простом примере разработки диалога, содержащего кнопку. В типичной C++ программе для Windows нажатие мышью на кнопку приводит к генерации сообщения
WM_LBUTTONDOWN. Это сообщение должно быть "поймано" программой либо в операторе switch, либо в соответствующей строке таблицы откликов (RESPONSE_TABLE), а затем передано процедуре реакции на это сообщение. Так, приложение, написанное в рамках OWL (Object Windows Library), использует макрос
DEFINE_RESPONSE_TABLE1(TEventTestDlgClient, TDialog)
//({TEventTestDlgClientRSP_TBL_BEGIN}}
EV_BN_CLICKED(IDEVENTBUTTON, EventBNClicked),
//({TEventTestDlgClientRSP_TBL_END}}
END_RESPONSE_TABLE;
чтобы ассоциировать событие (сообщение WM_LBUTTONDOWN),
генерируемое кнопкой IDEVENTBUTTON в диалоге TEventTestDlgClient, с функцией реакции EventBNClicked.
C++Builder покончил с этими трудно осваиваемыми программистскими трюками. Компонента кнопки уже запрограммирована так, чтобы реагировать на нажатие кнопки событием OnClick. Все, что надо сделать - это выбрать готовый (или написать собственный) метод и с помощью Инспектора объектов включить его в обработчик данного события.
3.6.1.1 Объявления компонентных классов
Опережающие объявления классов Библиотеки Визуальных Компонент VCL, входящей в состав C++Builder, используют модификатор _declspec:
_declspec(<спецификатор>)
Это ключевое слово может появляться в любом месте перечня объявлений, а не только непосредственно перед модифицируемым объявлением, причем спецификатор принимает одно из следующих значений:
delphiclass используется для опережающего объявления прямых или косвенных производных от VCL класса TObject. Он определяет правила совместимости VCL при обращении с RTTI, конструкторами, деструктором и исключениями.
delphireturn используется для опережающего объявления прямых или косвенных производных от VCL классов
Currency, AnsiString, Variant, TDateTime и Set. Он определяет правила совместимости VCL при обращении с параметрами и возвращаемыми значениями функций-членов.
pascalimplementation указывает, что компонентный класс реализован на Объектном Паскале.
VCL класс имеет следующие ограничения:
• Запрещено наследование виртуальных базовых классов.
• Компонентные классы сами не могут служить базовыми классами для наследования.
• Компонентные объекты создаются в динамической памяти кучи с помощью оператора new.
3.6.1.2 Объявления свойств
C++BuiIder использует модификатор _property для идентификации свойств компонентных классов. Синтаксис описания свойства имеет вид:
property <тип свойства> <имя свойства> = {<список атрибутов>} ;
где список атрибутов содержит перечисление следующих атрибутов свойства:
write = < член данных или метод записи > определяет способ присваивания значения члену данных;
read = < член данных или метод чтения > определяет способ получения значения члена данных;
default = < булева константа > разрешает или запрещает сохранение значения свойства по умолчанию в файле формы с расширением .dim;
stored = < булева константа или функция > определяет способ сохранения значения свойства в файле формы с расширением .dfm.
C++BuiIder использует модификатор
_published для спецификации тех свойств компонент, которые будут отображаться Инспектором объектов на стадии проектирования приложения. Если разработчик компоненты желает разрешить модификацию значения некоторого свойства, оно не объявляется как _published. Правила видимости, определяемые этим ключевым словом, не отличаются от правил видимости членов данных, методов и свойств, объявленных как public. Единственное отличие проявляется в том, что во время работы программы Инспектору объектов передается информация RTTI.
3.6.1.3 Объявления обработчиков событий '
C++BuiIder использует модификатор _closure для объявления функции обработчиков событий:
<тип> (_closure * <name>) (<список параметров>)
Это ключевое слово определяет указатель функции с именем name. В отличие от 4-байтового адресного указателя обычной функции (который передается в кодовые регистры CS:IP) 8-байтовый _closure передает еще и скрытый параметр (непостоянный указатель
this на экземпляр текущего класса).
Введение 8-байтовых указателей делает возможным не только вызывать некоторую функцию определенного класса, но и обращаться к функции в определенном экземпляре этого класса. Эта способность была заимствована из Объектного Паскаля, а _closure оказался жизненно необходимым для реализации механизма событий в Библиотеке Визуальных Компонент.
3.6.1.4 Объявления автоматизированных свойств и методов
OLE Automation - это разновидность механизма связи Object Linking and Embedding,
позволяющего приложениям для Windows управлять друг другом. Автоматизированный OLE контроллер является приложением, которое способно автоматизировать другое приложение - автоматизированный OLE сервер. По существу, OLE Automation представляет собой протокол обмена, посредством которого контроллер управляет действиями сервера. Все компонентные
OLE объекты, экспортируемые автоматизированным сервером своим контроллерам, являются производными от базового класса TAutoObject.
При создании автоматизированного сервера необходимо определить его интерфейс с контроллером, содержащий объявления свойств и методов OLE
объекта с тем, чтобы контроллер получил к ним доступ. Никогда не удаляйте уже включенные в интерфейс свойства и методы - это приведет к ошибкам в работе существующих контроллеров. C++Builder использует модификатор _automated в объявлениях автоматизированного метода (Листинг 3.13). Это объявление может заканчиваться еще одним новым ключевым словом _dispid, которое ассоциирует значение идентификатора диспетчеризации OLE Automation с данной функцией.
class MyOLEAutoClass : TAutoObject
{ _automated: void_fastcall function(void) _dispid(1000); };
Листинг 3.13. Объявление автоматизированного метода.
Правила видимости, определяемые этим ключевым словом, не отличаются от правил видимости, объявленных в секции public. Единственное отличие проявляется в том, что генерируемая компилятором информация о типах свойств и методов
OLE Automation, делает возможным создание автоматизированных серверов.
C++Builder поставляется вместе с примером (удивительным по внешней простоте и лаконичности кода) взаимодействия приложений контроллера и сервера посредством механизма OLE Automation. Этот пример проливает свет на данную методику, изложенную в системной документации весьма сбивчиво и туманно. Автоматизированный сервер
Autosrv демонстрирует использование:
• компонентных объектов TAutoObject и TAutoClassInfo;
• метода RegisterAutoClass автоматизированного объектного класса для регистрации сервера;
• свойств и методов, объявленных с ключевым словом _automated. Контроллер Autocon управляет сервером и демонстрирует:
• установку и выборку свойств объекта сервера;
• использование вариантов (детальная информация об типе Variant содержится в параграфе 3.6.1.6 "Расширенные типы данных Delphi".
Чтобы испытать на практике, что дает взаимодействие приложений OLE Automation, выполните следующие действия:
=> По команде главного меню File [ Open Project откройте диалог выбора проектов. => Войдите в каталог \...\CBuilder\Examples\Apps\Autosrv => Выберите проектный файл с именем Autosrv и нажмите кнопку
Open.
=> Командой главного меню Run | Run запустите процесс компиляции и сборки автоматизированного сервера. => Снова откройте диалог выбора проектов, войдите в каталог
\...\CBuilder\Examples\Apps\Autocon,
выберите проектный файл с именем
Autocon и нажмите кнопку Open. => Запустите процесс компиляции и сборки контроллера.
Вводя сообщения в области редактируемого ввода и нажимая кнопки контроллера, вы можете моделировать некоторые процессы управления сервером. получая результаты, которые отображены на нижеследующих рисунках:
Рис. 3.4. Контроллер готовит сообщение и посылает его серверу.
Рис. 3.5. Контроллер принимает сообщение, "обработанные" сервером.
Рис. 3.6. Контроллер снимает старое сообщение с сервера.
Чтобы узнать, как реализовано такое взаимодействие, необходимо разобраться в текстах модулей автоматизированного сервера (Листинг 3.14 и Листинг 3.15) и контроллера (Листинг 3.16 и Листинг 3.17), которые заслуживают того, чтобы привести их целиком, снабдив необходимыми комментариями. Сервер содержит единственный объект Edit1
компоненты TEdit для редактируемого ввода и приема сообщений от контроллера в свойство
Text. Контроллер записывает введенное пользователем сообщение в свойство Text
своего объекта Edit1, а управляет сервером посредством трех кнопок Buttoni, Button2 и Buttons компоненты TButton (с названиями "Послать", "Принять" и "Очистить").
#ifndef Auto2H
#define Auto2H
ftinclude <Classes.hpp> ftinclude <01eAuto.hpp>
#include <System.hpp>
// Класс сервера ButtonServer.
производный от TAutoOb-iect class
ButtonServer : public TAutoObject {
private: // Приватные свойства и методы AnsiString _fastcall
GetEditStr() ;
void _fastcall SetEditStr(AnsiString NewVal);
int _fastcall GetEditNum() ;
void _fastcall SetEditNum(int NewVal);
_automated: // Автоматизированные свойства и методы property AnsiString EditStr = // свойство типа AnsiString / / с атрибутами функции чтения/записи значении {read=GetEditStr, write=SetEditStr} ;
property int EditNum = //
свойство типа int / / с атрибутами функций чтения/записи значений {read=GetEditNum, write=SetEditNum); :
void _fastcall Clear (); //
метод очистки сообщения void_fastcall
SetThreeStr // метод составления текстового / / сообщения из строк в параметрах si, s2, s3 типа AnsiString (AnsiString si, AnsiString s2, AnsiString s3);
void _fastcall SetThreeNum // метод составления текстового / / сообщения из чисел в параметрах п 1, п2, п3 типа int (int п1, int п2, int п3) ;
public: // Общедоступные свойства и методы
_fastcall ButtonServer О; // конструктор объекта сервера
};
//----------------------------------------------------------
#endif
Листинг 3.14. Файл объявлений Auto1.h модуля автоматизированного сервера.
#include <vcl.h>
#pragma hdrstop
#undef RegisterClass
#include "Auto2.h"
#include "Autol.h"
int Initialization();
static int Initializer = Initialization();
// Создание объекта автоматизированного сервера _fastcall
ButtonServer::ButtonServer() : TAutoObject()
{ }
// Чтение текстового значения автоматизированного свойства AnsiString _fastcall ButtonServer::GetEditStr() { return
Forml->Editl->Text;
// Запись текстового значения автоматизированного свойства void _fastcall ButtonServer:-.SetEditStr (AnsiString NewVal) { Forml->Editl->Text = NewVal;
} // Чтение численного значения автоматизированного свойства
int _fastcall ButtonServer: :GetEditNum()
( int val;
sscanf(Forml->Editl->Text.c_str(), "%d", &val) ;
return val;
// Запись численного значения автоматизированного свойства void _fastcall ButtonServer::SetEditNum(int NewVal) { Forml->Editl->Text = NewVal;
}
// Очистка значения автоматизированного свойства void_fastcall
ButtonServer::Clear() { Forml->Editl->Text = "";
// Составление текстового значения свойства из трех строк void _fastcall ButtonServer::SetThreeStr (AnsiString si, AnsiString s2, AnsiString s3) { Forml->Editl->Text = si + ", " + s2 + ", " + s3;
) // Составление текстового значения свойства из трех чисел
void _fastcall ButtonServer::SetThreeNum (int nl, int n2, int
n3) { AnsiString sl(nl), s2(n2), s3(n3);
Forml->Editl->Text = si + ", " + s2 + ", " + s3;
// Регистрация объекта автоматизированного сервера void _fastcall RegisterButtonServer() { TAutoClassInfo AutoClassInfo;
// Инициализация полей структуры типа TAutoClassInfo AutoClassInfo.AutoClass = _classid(ButtonServer) ;
AutoClassInfo.ProgID = "BCBAutoSrv.EditServer" ;
AutoClassInfo.ClassID =
"{61E124E1-C869-11CF-9EA7-OOA02429B18A}";
AutoClassInfo.Description =
"Borland C++Builder AutoSrv Example Server Class";
AutoClassInfo.Instancing = acMultiInstance;
// Регистрация класса автоматизированного сервера Automation->RegisterClass(AutoClassInfo) ;
}
// Инициализация объекта автоматизированного сервера
int Initialization()
{ RegisterButtonServer() ;
return 0;
}
Листинг 3.15. Кодовый файл Auto2cpp модуля автоматизированного сервера.
#ifndef AutolH #efine AutolH
class TFormI : public TForm {
published: // IDE-managed Components
TEdit *Editl;
TButton *Buttonl
TButton *Button2
TButton *Button3
TLabel * Label 1;
void _fastcall ButtonlClick(TObject *Sender) ;
void _fastcall Button2Click(TObject * Sender);
void _fastcall Button3Click(TObject * Sender);
private: // User declarations
Variant AutoServer;
public: // User declarations
virtual _fastcall TFormI(TComponent *0wner) ;
extern TFormI *Forml;
#endif
Листинг 3.16. Файл объявлений Auto1.h, все строки которого (за исключением выделенной строки объявления варианта) C++Builder генерирует автоматически при размещении компонент на форме контроллера.
include <vcl.h> #pragma hdrstop
#include "autol.h"
#pragma resource "*.dfm" #ormI *Forml;
_fastcall TFormI::TFormI(TComponent *0wner) : TForm(Owner) { try
{ // Создание автоматизированного сервера как объекта OLE AutoServer = CreateOleObject("BCBAutoSrv.Edit Server");
}
catch (...) { // Обработка исключения
ShowMessage("Сначала компилируйте и запустите AutoSrv");
Application-terminate () ;
}
} // Обработчик события при нажатии кнопки Buttoni
void _fastcall TFormI::ButtonlClick(TObject *Sender) { // Установка автоматизированного свойства сервера AutoServer.OlePropertySet("EditStr", Editl->Text);
)
// Обработчик события при нажатии кнопки Button2
void _fastcall TFormI::Button2Click(TObject *Sender)
{ // Чтение автоматизированного свойства сервера
Editl->Text = AutoServer.OlePropertyGet("EditStr");
// Обработчик события при нажатии кнопки Button3 void _fastcall
TFormI::Button3Click(TObject *Sender) { //
Очистка автоматизированного свойства сервера AutoServer.OleProcedure("Clear") ;
Листинг 3.17. Кодовый файл Auto1.cpp модуля контроллера.
3.6.1.5 Быстрый вызов функций
При объявлении функций, параметры которых передаются через процессорные регистры, используется модификатор _fastcall:
<возвращаемый тип> _fastcall
<name>(<список параметров^
Это ключевое слово определяет, что первые три типизированных параметра функции с именем name (слева направо по списку) передаются не через стек, а через процессорные регистры AX, BX и DX. Регистры не используются, если значение параметра не умещается в регистр, т.е. при передаче через параметр чисел с плавающей точкой, структур и функций.
Строго говоря, быстрый вызов функций не является прерогативой компилятора
C++Builder. В предыдущей главе я уже обращал внимание читателя на использование _fastcall в объявлениях функций обработки событий, которые C++Builder генерирует автоматически.
3.6.1.6 Расширенные типы данных Delphi
C++Builder не позволяет посредством известного ключевого слова typedef просто переопределить некоторые сложные типы данных Объектного Паскаля. C++Builder реализует такие расширенные типы в виде обычных или шаблонных классов
(template class). Каждый такой класс содержит все необходимые конструкторы, деструкторы, свойства и объектные методы. Многие компоненты VCL используют реализацию расширенных типов, а кроме того, они требуются при разработке новых компонент на базе оригиналов из Delphi.
Ниже приводится сводная таблица встроенных типов Delphi и соответствующих им типов C++Builder:
Delphi | Длина и значения | C++Builder | Реализация |
Shortint | 8-битовое целое | char | typedef |
Smallint | 16-битовое целое | short | typedef |
Longint | 32-битовое целое | long | typedef |
Byte | 8-битовое целое без знака | unsigned char | typedef |
Word | 16-битовое целое без знака | unsigned short | typedef |
Integer | 32-битовое целое | int | typedef |
Cardinal | 32-битовое целое без знака | unsigned long | typedef |
Boolean | true/false | bool | typedef |
ByteBool | true/false или 8-битовое целое без знака | unsigned char | typedef |
WordBool | true/false или 16-битовое целое без знака |
unsigned short | typedef |
LongBool | true/false или 32-битовое целое без знака |
unsigned long | typedef |
AnsiChar | 8-битовый символ без знака | unsigned char | typedef |
WideChar | Слово - символ Unicode | wchar t | typedef |
Char | 8-битовый символ | char | typedef |
String | Текстовая строка Delphi | AnsiString | typedef |
Single | 32-битовое плавающее число | float | typedef |
Double | 64-битовое плавающее число | double | typedef |
Extended | 80-битовое плавающее число | long double | typedef |
Real | 32-битовое плавающее число | float | typedef |
Comp | 64-битовое плавающее число | double | typedef |
Pointer | 32-битовый указатель | void * | typedef |
PChar | 32-битовый указатель на символы без знака | unsigned char * | typedef |
PansiChar | 32-битовый указатель на ANSI символы без знака | unsigned char * | typedef |
Set | Множество 1..32 байт | Set<type, minval, maxval> | template class |
AnsiString | Текстовая строка Delphi | AnsiString | class |
Variant | Вариантное значение, 16 байт | Variant | class |
TdateTime | Значение даты и времени, 64-битовое плавающее число | TDateTime | class |
Currency | Валюта, 64-битовое плавающее число, 4 цифры после точки | Currency | class |
Set (множество) служит для спецификации типа параметров объектных методов VCL
или типа значений, возвращаемых этими методами. C++Builder реализует этот встроенный тип Delphi с помощью одноименного шаблонного класса Set<type, minval, maxval> со следующими параметрами:
type тип элементов множества (обычно, int. char или enum):
minval минимальное (положительное) значение, которое могут принимать элементы множества;
maxval максимальное (не более 255) значение, которое могут принимать элементы множества.
Подстановка разных значений параметров приводит к созданию экземпляров шаблонного класса Set
различных типов, поэтому оператор сравнения if (si == s 2) объектов, описанных как
Set<char, 'A', 'C'> si;
Set<char, 'X', 'Z'> s2;
вызовет ошибку компиляции. Для создания множественных экземпляров типа Set необходимо использовать ключевое слово typedef. Например, объявив typedef Set<char, 'A','Z'> UpperCaseSet; можно создать множества UpperCaseSet si; и UpperCaseSet s2; а затем инициализировать эти объекты:
s1 “ 'А' “ 'В' “ 'С' ;
s2 “ 'X' “ 'Y' “ '?.' ;
AnsiString используется для спецификации типа текстовых строк произвольной длины, имеющих следующую характерную внутреннюю структуру:
C++Builder реализует этот встроенный тип
Delphi как одноименный класс. Если при создании экземпляров данного класса не указано начальное значение строки, конструктор AnsiString автоматически присваивает всем переменным нулевые значения. Среди методов данного класса отметим наиболее часто вызываемый метод с str (), который возвращает указатель на символьный массив, оканчивающийся 0 и содержащий копию символов, заключенных в исходном объекте типа AnsiString. Листинг 3.18 иллюстрирует "применение методов чтения и записи значения члена данных FNames свойства Names типа AnsiString в экземпляре MyFamily объявленного компонентного класса Family". Предыдущее предложение кажется полной абракадаброй, если не проникнуться терминологией объектно-ориентированного программирования. Рассматривайте его как своеобразное словесное упражнение по краткому курсу ООП.
#include <vcl/dstring.h> #include <stdio.h> class Family // объявление класса
{
private:
AnsiString FNames[10]; // массив имен AnsiString GetName(int Index); // метод чтения void
SetName(int, AnsiString); // метод записи public:
_property AnsiString Names[int Index] =
{read=GetName, write=SetName} ;
Family(){} // .конструктор -Family(){) // деструктор
};
AnsiString Family::GetName(int i)
{
return FNames[i]; // GetName
возвращает значение }
void Family::SetName(int i, const AnsiString s) { FNames[i]=s; // SetName присваивает значение
}
void main()
{
Family My Family; // создание объекта MyFamily // Инициализация 4- х строк массива имен методом SetName() MyFamily.Names[0]="Иван" ;
MyFamily.Names[1]="Анна" ;
MyFamily.Names[2]="Марья";
MyFami ly. Names [ 3 ] = "
Андрей " ;
// Вывод 4-х строк массива имен методом GetName() for (int i=0; i<=3; i++)
puts(MyFamily.Names[i].c_str()) ;
}
Листинг 3.18.
Пример использования типа AnsiString в C++ программе с компонентным классом Family
(Семья).
Variant служит для спецификации значений, меняющих тип динамически. Переменная вариантного типа, в отличие от обычных статически типизированных переменных, способна менять свой тип во время исполнения программы. C++Builder объявляет этот тип Delphi как class __declspec(delphireturn) Variant: public TVarData. Заметим, чтс синтаксис вариантов, принятый в Delphi,
например:
V: Variant;
V := VarArrayCreate([0,Hi9hVal,0,HighVal],varlnteger) ;
отличается от способа записи вариантного массива в C++Builder:
Variant V(OPENARRAY(int,(0,HighVal,0,HighVal)),varlnteger);
Вариант может быть сконструирован из следующих типов данных: short, int, float, double.
Currency, TDateTime, bool, WordBool, Byte, AnsiString&, char *, wchar_t
* const. 01e2::lDispatch* const или 01e2::IUnknown* const.
Компилятор автоматически выполнит необходимые преобразования типа. При создании вариантных переменных они всегда инициализируются специальным значением Unassigned (не определено). Специальное значение Null
указывает, что данные отсутствуют.
Внимание: Варианты предоставляют ООП чрезвычайную гибкость, однако требуют большей памяти, нежели статически типизированные переменные, а операции над ними выполняются заметно медленнее.
TDateTime используется для спецификации переменных даты и времени. C++Builder реализует этот встроенный тип Delphi как одноименный класс, который инкапсулирует член данных типа double, содержащий значение даты в целой части числа, а значение времени в мантиссе (считая от полудня 30 декабря 1899 года). В следующей таблице приведены значения переменной типа TDateTime и их эквиваленты в выражениях даты и времени:
Значение | Дата | Время | Примечания |
0 | 12/30/1899 | 12:00 | +0 дней, +0 часов |
2.75 | 01/01/1900 | 18:00 | +2 дня, +6 часов |
-1.25 | 12/29/1899 | 06:00 | -1 день, -б часов |
35065 | 01/01/1996 | 12:00 | +35065 дней, +0 часов |
3.6.2 Расширения стандарта ANSI C++
Рассматриваемые расширения стандарта ANSI C++, в основном, представляют интерес для разработчиков новых классов и компонент, а также для программистов, которые работают в большом коллективе над созданием сложного проекта.
Вы можете прочитать остаток главы "по диагонали" или вовсе пропустить его, если не занимаетесь указанной деятельностью, и тонкости языка вас не интересуют.
3.6.2.1 Шаблоны
Шаблоны (параметризованные типы)позволяют конструировать семейство связанных функций или классов. Обобщенный синтаксис определения шаблона имеет вид
template <список шаблонных типов> { объявление } ;
Различают шаблоны функций и шаблоны классов.
Шаблон функции задает образец определений перегружаемых функций. Рассмотрим шаблон функции тах(х, у), которая возвращает больший из двух аргументов, которые могут быть любого, допускающего упорядочивание, типа:
template <class Т> Т тах(Т х. Ту) { return (x > у) ? х : у; } ;
причем тип данных, представленный аргументом шаблона <class T>, может быть любым. При его использовании в программе компилятор генерирует код функции тах в соответствии с фактическим типом передаваемых ей параметров:
int i;
Myclass a, b;
int j = max(i, 0); // тип аргументов int Myclass m = max(a, b); // тип аргументов
Myclass
Фактические типы должны быть известны во время компиляции. Без шаблонов пришлось бы многократно перегружать функцию max - для каждого поддерживаемого типа, хотя код всех версий функции по существу был бы идентичным. Стандарт C++ настоятельно не рекомендует использовать для этой цели макрос:
#define max(x,y) ((х > у) ? х : у) из-за блокировки механизма проверки типов, который дает такие преимущества языку C++ над обычным С. Очевидно, задача функции тах(х, у) - сравнить совместимые типы. К сожалению, использование макроса допускает сравнение несовместимых типов, например, int и
struct.
Шаблон классов задает образец определений семейства классов. Рассмотрим пример шаблона Vector - генератора классов одномерного массива данных:
template <class T> class Vector
Над типизированными элементами этого класса выполняются одинаковые базовые операции (вставить, вычеркнуть, индексировать и т.д.), вне зависимости от конкретного типа элементов. Если обращаться с типом как с параметром, то компилятор будет генерировать классы векторов с элементами заданного типа.
Как и в случае шаблонов функций, разрешается явно переопределять тип шаблон классов:
class Vector<char *> ( ... };
причем символ Vector должен всегда сопровождаться типом данных, заключенным в угловые скобки.
3.6.2.2 Пространства имен
Большинство нетривиальных приложений состоят из нескольких файлов с исходным текстом программы. Эти файлы могут создаваться и обслуживаться группой программистов. В конце концов, все файлы собираются вместе и проходят через финальную процедуру сборки готового приложения. Традиционно принято, чтобы все имена, не заключенные в некоторой локальной области (функции, теле класса или модуле трансляции), разделяли общие глобальные имена. Поэтому повторное определения имен, обнаруженное в процессе сборки отдельных модулей, приводит к необходимости каким-то образом различать каждое имя. Решение этой проблемы в C++ возложено на механизм пространства имен (namespace).
Этот механизм позволяет разбить приложение на ряд подсистем, причем каждая подсистема свободна в отношении выбора имен, и ее автор не должен беспокоиться о том, что такие же имена будет использовать кто-то другой. Каждая подсистема идентифицирует свое появление в общем пространстве глобальных имен уникальным идентификатором, который следует за ключевым словом namespace:
namespace
<идентификатор> { [<объявления>] } Существует три способа доступа к элементам идентифицированного пространства имен:
• Явная квалификация доступа к конкретному элементу:
ALPHA :: varl; // доступ к переменной из ALPHA BETA :: Fl; // доступ к функции из BETA
• Доступ ко всем элементам:
using namespace :: ALPHA; // доступ ко всем именам из ALPHA
• Объявление нового идентификатора в локальном пространстве имен:
using :: new_name; //
добавление идентификатора
3.6.2.3 Явные объявления
Обычно объектам класса, в котором объявлен конструктор с одним параметром, можно присвоить значения, тип которых автоматически (неявно) преобразуется к своему классовому типу. При объявлении конструктора можно использовать модификатор explicit:
explicit <объявление конструктора> Тогда при объявлении конструкторов данного класса с ключевым словом explicit всем объектам класса можно присвоить только те значения, тип которых явно преобразуется к классовому типу (Листинг 3.19). Другие присваивания приведут к ошибке компиляции.
class X
public:
explicit X(int);
explicit X(const char*, int = 0);
};
void f(X arg)
(
X a = X (1) ;
X b = Х("строка",0);
a = Х(2);
} :(
Листинг 3.19. Явные объявления конструкторов.
Явные объявления конструкторов требуют, чтобы значения в операторах присваивания были преобразованы к тому классовому типу, объектам которого эти значения присваиваются.
3.6.2.4 Непостоянные объявления
При объявлении переменной, которая может быть изменена фоновой задачей, обработчиком прерывания или портом ввода-вывода, используется модификатор volatile:
volatile <тип> <имя объекта>;
В C++ применение ключевого слова
volatile распространяется на классы и функции-члены. Это ключевое слово запрещает компилятору делать предположения относительно значения указанного объекта, поскольку при вычислении выражений, включающих этот объект, его значение может измениться в любой момент. Кроме того. непостоянная переменная не может быть объявлена с модификатором register. Листинг 3.20 показывает пример реализации таймера, в котором переменная ticks модифицируется обработчиком временных прерываний.
volatile int ticks;
void timer( ) // Объявление функции таймера
ticks++;
void wait (int interval)
ticks = 0;
while (ticks < interval); // Цикл ожидания
}
Листччг 3.20. Изменение непостоянной переменной volatile.
Положим, что обработчик прерывания
timer был надлежащим образом ассоциирован с аппаратным прерыванием от часов реального времени. Процедура wait реализует цикл ожидания, пока значение переменной
ticks не станет равным интервалу времени, заданному ее параметром. Компилятор C++ обязан перезагружать значение переменной
volatile ticks перед каждым сравнением внутри цикла - несмотря на то, что внутри цикла значение переменной не изменяется. Некоторые оптимизирующие компиляторы могли бы допустить эту "роковую" ошибку.
Другой вид непостоянной переменной, которая может быть изменена даже если она входит в константное выражение, объявляется с помощью модификатора mutable:
mutable <имя переменной>;
Назначение ключевого слова mutable состоит в спецификации членов данных некоторого класса, которые могут быть модифицированы константными функциями этого класса. Листинг 3.21 показывает пример, в котором член данных count модифицируется константной функцией F1.
class A {
public: mutable int count; int F1 (int p = 0) const
// Объявление функции F1
count = p++ return count; //PI
возвращает count
) I
void main() {
A a;
cout “ a.Fl(3) “ end.1; // main
выдает значение 4 )
Листинг 3.21. Изменение непостоянной переменной mutable.
3.6.2.5 Идентификация типов RTTI
Идентификация типов при выполнении программы RTTI (Run-Time Туре Identification) позволяет вам написать переносимую программу, которая способна определять фактический тип объекта в момент выполнения даже в том случае, если программе доступен только указатель на этот объект. Это дает возможность, например, преобразовывать тип указателя на виртуальный базовый класс в указатель на производный тип фактического объекта данного класса. Таким образом, преобразование типов может происходить не только статически - на фазе компиляции, но и динамически - в процессе выполнения. Динамическое преобразование указателя в заданный тип осуществляется с помощью оператора dynamic_cast.
Механизм RTTI также позволяет проверять, имеет ли объект некоторый определенный тип, или принадлежат ли два объекта одному и тому же типу. Оператор typeid определяет фактический тип аргумента и возвращает указатель на объект класса typeinfo, который этот тип описывает.
Передавая RTTI Инспектору объектов во время выполнения, C++Builder информирует его о типах свойств и членов данного класса.
3.6.2.6 Исключения
Язык C++ определяет стандарт обслуживания исключений в рамках ООП. C++Builder предусматривает специальные механизмы для обработки исключений (ошибок), которые могут возникнуть при использовании Библиотеки Визуальных Компонент. C++Builder также поддерживает обработку исключений самой операционной системы и модель завершения работы приложения.
Когда программа встречает ненормальную ситуацию, на которую она не была рассчитана, можно передать управление другой части программы, способной справиться с этой проблемой, и либо продолжить выполнение программы, либо завершить работу. Переброс исключений (exception throwing) позволяет собрать в точке переброса информацию, которая может оказаться полезной для диагностики причин, приведших к нарушению нормального хода выполнения программы. Вы можете определить обработчик исключения (exception handler), выполняющий необходимые действия перед завершением программы. Обслуживаются только так называемые синхронные исключения, которые возникают внутри программы. Такие внешние события, как нажатие клавиш Ctrl+C, не считаются исключениями.
Блок кода, который может сгенерировать исключение, начинается ключевым словом try и заключается в фигурные скобки. Если блок try обнаруживает исключение внутри этого блока, происходит программное прерывание и выполняется следующая последовательность действий:
1. Программа ищет подходящий обработчик исключения.
2. Если обработчик найден, стек очищается и управление передается обработчику исключения.
3. Если обработчик не найден, вызывается функция terminate для завершения приложения.
Блок кода, который обрабатывает возникшее исключение, начинается ключевым словом catch и заключается в фигурные скобки. По меньшей мере один кодовый блок обработчика исключения должен следовать непосредственно за блоком try. Для каждого исключения, которое может сгенерировать программа, должен быть предусмотрен свой обработчик. Обработчики исключений просматриваются по порядку и выбирается обработчик исключения, тип которого соответствует типу аргумента в операторе catch. При отсутствии в теле обработчика операторов goto, выполнение программы будет возобновлено, начиная с точки, следующей за последним обработчиком исключений данного блока try. Листинг 3.22 демонстрирует обобщенную схему обработки исключений.
try
i
// Любой код, который может сгенерировать исключение
} 1
catch (Т X) |
{ I
// Обработчик исключения Х типа Т, которое могло быть |
// ранее сгенерировано внутри предыдущего блока try Я // Обработчики других исключений предыдущего блока
try catch (...)
// Обработчик любого исключения предыдущего блока try
Листинг 3.22. Обработка исключении.
Когда возникает исключение, выражение присваивания в операторе throw <выражение> инициализирует временный объект, тип которого соответствует типу аргумента выражения. Другие копии этого объекта могут быть сгенерированы, например, при помощи конструктора копирования объекта исключения.
Следование принятой стандартной методике обработки ошибок - одна из гарантий построения надежных приложений, хотя операционная система и постарается сама "вытянуть" исполняемую программу из непредвиденных ситуаций. Осознавая важность этого элемента ООП, я включил в развернутые примеры по программированию баз данных главы 5 необходимые коды для обработки исключений.
3.7 Итоги
Мы вкратце ознакомились с терминологией объектно-ориентированного программирования на языке C++ и с некоторыми расширениями языка. ООП оперирует абстрактными понятиями классов, объектов, методов, инкапсуляции, наследования и полиморфизма. Читатель быстро научится находить аналоги терминов ООП в реальном мире, что облегчит освоение в общем-то простых понятий с довольно туманными и, зачастую, неудачными определениями.
Мы по достоинству оценили введение новых компонентных классов и подготовились к манипулированию с ними в рамках интегрированной визуальной среды быстрой разработки приложений.
ОБЩИЕ ХАРАКТЕРИСТИКИ
1. ОБЩИЕ ХАРАКТЕРИСТИКИ
И ОСОБЕННОСТИ ПРИМЕНЕНИЯ
Новейшая система объектно-ориентированного программирования C++ Builder производства корпорации Borland предназначена для операционных систем Windows 95 и NT. Интегрированная среда C++ Builder обеспечивает скорость визуальной разработки, продуктивность повторно используемых компонент в сочетании с мощью языковых средств C++, усовершенствованными инструментами и разномасштабными средствами доступа к базам данных.
C++ Builder может быть использован везде, где требуется дополнить существующие приложения расширенным стандартом языка C++, повысить быстродействие и придать пользовательскому интерфейсу качества профессионального уровня.
Эта глава носит обзорный и отчасти рекламный характер, что объясняется давнишней приверженностью автора программным продуктам корпорации Borland, в особенности базирующимся на языке C++. Если излагаемый материал покажется вам скучным или малоинформативным - просто пропустите его. Большие возможности системы и маленькие секреты программирования без проблем откроются перед вами в следующих главах книги.
1.1 Основные характеристики
Рассмотрим отличительные характеристики, которые выдвигают Borland C++ Builder на авангардные позиции систем объектно-ориентированного программирования для быстрой разработки современного математического обеспечения персональных компьютеров. Borland C++ Builder выпускается в трех вариантах: Standard, Professional и Client/Server Suite.
Характеристики | Standard | Professional | Client/Server Suite | ||
Язык C++ с поддержкой расширений стандартов
ANSI/ISO |
V | v | v | ||
Высокопроизводительный 32-разрядный оптимизирующий компилятор | V | v | v | ||
Быстрый инкрементальный компоновщик приложений | V | v | v | ||
Интегрированная среда разработки IDE | V | v | v | ||
Механизмы двунаправленной разработки | V | v | v | ||
Интегрированный отладчик CPU View и инструменты командной строки | V | v | v | ||
Создание библиотек DLL, LIB и исполняемых программных файлов ЕХЕ | V | v | ^ | ||
Полный комплект общих элементов управления
Windows 95 |
V | v | v | ||
Объекты модулей данных | V | v | v | ||
Полный доступ к Windows API | V | v | v | ||
Хранилище объектов | V | v | v | ||
Визуальное наследование форм | V | v | v | ||
Контроллеры и серверы OLE Automation | V | v | v | ||
Библиотека Визуальных Компонент VCL | V | v | v | ||
Мастер для помощи в создании компонент | V | v | v | ||
Компоненты для работы с базами данных | V | v | v | ||
Расширенная справочная служба on-line | V | v | v | ||
Проводник баз данных | V | v | v | ||
Компоненты для создания отчетов | V | v | v | ||
Исходные тексты VCL | v | v | |||
Масштабируемый словарь баз данных | v | v | |||
Поддержка соединений ODBC | v | v | |||
Компонента визуализации и редактирования баз данных Multi-Object Grid | v | v | |||
32-разрядный однопользовательский сервер Local InterBase | v | v | |||
Полный комплект печатной документации | V | V | |||
Генератор дистрибутивов InstallShield Express | V | V | |||
Internet Solutions Pack для разработки Web-приложений | V | V | |||
WinSight32 для мониторинга сообщений Windows | V | V | |||
Открытые инструменты API | V | V | |||
Расширенный набор компонент ActiveX | V | V | |||
Драйверы SQL Links для баз данных Oracle, Sybase, Informix, DB2, Microsoft SQL Server и InterBase | V | ||||
SQL Database Explorer | V | ||||
SQL Monitor | V | ||||
Visual Query Builder | V | ||||
Интегрированная система контроля версий в коллективных проектах Intersolv PVCS | V | ||||
InterBase NT | V | ||||
Механизм кэшируемых обновлений | V |
1.1.1 Скорость визуальной разработки
Интегрированная среда разработки
объединяет Редактор форм. Инспектор объектов. Палитру компонент. Администратор проекта и полностью интегрированные Редактор кода и Отладчик - инструменты быстрой разработки программных приложений, обеспечивающие полный контроль над кодом и ресурсами.
Профессиональные средства языка C++ интегрированы в визуальную среду разработки. C++Builder
предоставляет быстродействующий компилятор с языка Borland C++, эффективный инкрементальный загрузчик и гибкие средства отладки как на уровне исходных инструкций, так и на уровне ассемблерных команд - в расчете удовлетворить высокие требования программистов-профессионалов.
Конструирование по способу
"drag-and-drop "
позволяет создавать приложение простым перетаскиванием захваченных мышью визуальных компонент из Палитры на форму приложения. Инспектор объектов предоставляет возможность оперировать со свойствами и событиями компонент, автоматически создавая заготовки функций обработки событий, которые наполняются кодом и редактируются в процессе разработки.
Механизмы двунаправленной разработки (two-way-tools)
устраняют барьеры между программистом и его кодом. Технология двунаправленной разработки обеспечивает контроль за вашим кодом посредством гибкого, интегрированного и синхронизированного взаимодействия между инструментами визуального проектирования и Редактором кода.
Свойства, методы и события - это именно те элементы языка, которые обеспечивают быструю разработку приложений в рамках объектно-ориентированного программирования. Свойства
позволяют легко устанавливать разнообразные характеристики объектов. Методы производят определенные, иногда довольно сложные, операции над объектом. События
связывают воздействия пользователя на объекты с кодами реакции на эти воздействия. События могут возникать при таких специфических изменениях состояния объектов как обновление данных в интерфейсных элементах доступа к базам данных. Работая совместно, свойства, методы и события образуют среду RAD (Rapid Application Development) быстрого и интуитивного программирования надежных приложений для Windows.
Визуальное наследование форм воплощает важнейший аспект объектно-ориентированного программирования в удобном для пользования инструменте визуального проектирования. Характеристики новой формы приложения могут быть унаследованы от любой другой существующей формы, что обеспечивает централизованную репродукцию изменений пользовательского интерфейса, облегчает контроль за кодом и уменьшает временные затраты на введение новых качественных атрибутов.
Испытание прототипа позволяет без труда переходить от прототипа приложения к полностью функциональному, профессионально оформленному программному продукту, действуя в пределах интегрированной среды. Чтобы удостовериться, что ваша программа производит ожидаемые результаты, раньше приходилось многократно проходить по циклу редактирование => компиляция => сборка, непроизводительно расходуя время.
C++Builder объединяет три этапа разработки в единый производственный процесс. В результате удается строить приложения, базирующиеся на текущих требованиях заказчика, вместе с тем гибкие настолько, чтобы быстро адаптировать их к новым запросам пользователей.
Мастер инсталляции руководит созданием унифицированных дистрибутивных пакетов для разработанных приложений.
Исходные тексты Библиотеки Визуальных Компонент облегчают разработку новых компонент на базе готовых примеров.
Отрытые инструменты API могут быть непосредственно интегрированы в визуальную среду системы. Вы сможете подключить привычный текстовый редактор или создать собственного мастера для автоматизации выполнения повторяющихся процедур.
Расширенная математическая библиотека
содержит дополнительные унифицированные функции статистических и финансовых вычислений.
1.1.2 Продуктивность компонент
Библиотека Визуальных Компонент VCL приобрела статус нового промышленного стандарта и в настоящее время применяется более чем полумиллионом пользователей, существенно ускоряя разработку надежных приложений любой степени сложности. VCL содержит около 100 повторно используемых компонент, которые реализуют все элементы пользовательского интерфейса операционной системы Windows
95. Кроме того, VCL предоставляют в распоряжение программистов такие оригинальные объекты, как записные книжки с закладками, табличные сетки для отображения содержимого баз данных и даже органы управления устройствами мультимедиа. Находясь в среде объектно-ориентированного Программирования C++Builder, компоненты можно использовать непосредственно, менять их свойства, облик и поведение или порождать производные элементы. обладающие нужными отличительными характеристиками.
Хранилище объектов является инструментом новой методики хранения и повторного использования модулей данных, объектов, форм и программной бизнес-логики. Поскольку построение нового приложения на существующем фундаменте значительно экономит временные затраты, хранилище объектов предоставляет для повторного использования готовые структуры: формы и законченные программные модули. Создавая прототип нового приложения, вы можете наследовать, ссылаться или просто копировать существующую структуру - точно так же архитектор приступает к проектированию нового здания.
Компонента ChartFX обеспечивает немедленное построение на вашей форме разнообразных графиков, диаграмм, таблиц и предусматривает проверку правописания на многих языках. В варианте C++Builder Standard эта компонента является единственным представителем группы
ActiveX.
Интеграция компонент ActiveX позволяет расширить Библиотеку Визуальных Компонент, включив компоненты стандарта ActiveX для разработки приложений в сети Internet.
Если ваш компьютер имеет выход в
Internet, соединитесь с сервером по адресу
http://www.borland.com/devsupport/bcppbuilder/netmanage.html
и скачайте файл автоматической интеграции OCXINTEG.ZIP (12 Кбайт) и файл управления сетью AX602.ZIP (6.5 байт), разработанный фирмой NetManage. Файл интеграции автоматически проведет для вас процедуру добавления новых компонент к VCL и установит их на Палитру. В результате вы получите возможность разрабатывать приложения для Internet, вплоть до создания собственного сетевого коммуникатора
(HTML browser).
1.1.3 Мощность языковых средств C++
Оптимизирующий 32-разрядный компилятор
построен по проверенной ведущей компиляторной технологии корпорации Borland, обеспечивающей исключительно падежную и быструю оптимизацию как длины выходного исполняемого кода, так и расходуемой памяти.
Новые элементы стандарта ANSI/ISO языка C++ представлены шаблонами, пространствами имен, исключениями, информацией о типах времени выполнения (RTTI), наряду с расширением набора ключевых слов
bool, explicit, mutable, typename. automated
и др.
Инкрементальный линкер осуществляет быструю и надежную сборку приложении в формате ЕХЕ файлов сравнительно меньшего размера. Автоматически устраняя повторную сборку не изменившихся исходных объектных файлов и подключение неиспользуемых функций, инкрементальный линкер строит эффективную выполняемую программу с минимальными потерями времени.
Чистый и доступный код приложений, которые C++Builder строит на основе предоставляемых разработчику компонентных свойств, событий и методов, исключает скрытые и трудные в отладке макросы.
Поддержка промышленных стандартов ActiveX, OLE, СОМ, MAPI, Windows Sockets TCP/IP, ISAPI. NSAPI, ODBC, Unicode и MBCS.
Отладчик низкого уровня CPU View позволяет проникнуть в специфику работы вашего приложения на уровне машинных кодов. Окно отладчика разделено на пять панелей. Панель ассемблерных команд интерпретирует исполнение исходной C++ программы. Панель памяти показывает содержимое блока памяти, доступного загруженному и исполняемому в данный момент модулю. Панель стека отображает текущее содержимое верхушки программного стека. Панель регистров и панель флагов показывают текущие значения регистров и служебных битов центрального процессора. Каждая панель включает собственное меню, управляющее ее видом и поведением.
Инструменты командной строки включены в систему по требованию профессионалов, которые всегда стремятся сохранить детальный контроль над процессами компиляции и сборки своих программных файлов.
Создание DLL, LIB, и ЕХЕ файлов предоставляет свободу выбора формата целевого приложения в соответствии с требованиями конкретного проекта.
Прямое обращение к системным функциям Windows 95 и NT дает возможность программистам, работающим в среде C++Builder. при необходимости воспользоваться всеми усовершенствованиями современных операционных систем.
Механизм OLE Automation предоставляет вашему приложению возможность управлять другими типовыми программными комплексами для Windows (такими как Microsoft Word, Excel, Visual Basic, Lotus 1-2-3, dBASE и Paradox) по схеме сетевого взаимодействия контроллер/сервер.
1.1.4 Масштабируемые соединения с базами данных
Разработка по способу
"drag-and-drop"
многократно упрощает и ускоряет обычно трудоемкий процесс программирования СУБД в архитектуре клиент/сервер. Широкий выбор компонент управления визуализацией и редактированием позволяет легко изменять вид отображаемой информации и поведение программы. C++Builder использует Проводник баз данных (Database Explorer) и масштабируемый Словарь данных (Data Dictionary ), чтобы автоматически настроить средства отображения и редактирования применительно к специфике вашей информации.
Проводник баз данных предоставляет графический способ проводки пользователя по содержимому базы данных, обеспечивая создание и модификацию таблиц, иерархических указателей и псевдонимов.
Словарь данных поддерживает целостность изменяющейся информации о содержимом таблиц баз данных. Пользователь может динамически модифицировать состав Словаря. Словарь содержит информацию о расширенных атрибутах полей в записях: минимальные и максимальные значения, свойства отображения, маски редактирования и т.п.
Живые данные (live data) предоставляются разработчику в процессе визуального проектирования прототипов и при испытании приложений баз данных. Вам не потребуется более писать тестовые ловушки или многократно перетранслировать и запускать приложение - данные на стадии проектирования будут точно такими же и представлены точно так же, как их увидит пользователь законченной программы.
Механизм BDE ( Borland Database Engine) поддерживает высокопроизводительный 32-разрядный доступ к базам данных dBASE, Paradox: Sybase. Oracle, DB2. Microsoft SQL Server. Informix, InterBase и Local InterBase. C++Builder
использует контроллер ODBC (Open Database Connectivity) производства Microsoft для связи с серверами баз данных Excel, Access, FoxPro и Btrieve. Являясь фундаментом любого приложения базы данных, BDE тесно связан с Хранилищем объектов и Модулями данных.
Объекты Модулей данных действуют как связующий каркас приложения - они определяют источники и бизнес-логику базы данных, фиксируют взаимосвязи компонент. В централизованной модели доступа к данным бизнес-логика отделена от разработки графического интерфейса с пользователем (GUI). Любое изменение бизнес-логики вашей базы данных сказывается на поведении только соответствующего Модуля данных, а результаты изменения проявляются немедленно во всех приложениях, использующих данный модуль. Работая с модулями данных, вы однократно устанавливаете связи вашего приложения с адресуемой базой данных, а затем по способу
"drag-and-drop" можете перетаскивать поля записей на новые формы - в любой узел вашей сети. Никакого дополнительного кодирования при этом не требуется.
Фильтры поля ссылок устанавливают ограничения поиска и отображения информации базы данных простым нажатием кнопки. Изменяя значения свойства Filter в компонентах доступа, можно специфицировать некоторое подмножество интересующих вас данных. Ссылки обеспечивают автоматическое отображение данных из нескольких таблиц.
Копируемые обновления (cached updates) заметно ускоряют отклик SQL сервера за счет уменьшения общего числа сетевых обменов с клиентом. Будучи упакованными, множественные коммуникации проявляют себя как одиночные транзакции, тем самым снижая загруженность сервера и улучшая производительность вашего приложения.
Отчеты Quick Reports позволяют визуально конструировать стилизованные отчеты по данным, поставляемым любым источником, включая таблицы и запросы компонент доступа к базам данных. Отчеты могут содержать поля заголовков, колонтитулов, сносок и итогов. Quick Reports предоставляют мощные средства отображения отчетов в разных видах, автоматического подведения итогов и подсчета полей - на любом уровне группировки данных.
1.2 Другие программные продукты Borland
Корпорация Borland давно завоевала приоритетную позицию на рынке мощных систем разработки программного обеспечения персональных компьютеров. Появление C++Builder ни в коей мере не затормозило развитие других программных продуктов, имеющих своих приверженцев и потребителей.
Borland C++ остается одной из самых популярных систем разработки математического обеспечения на языке C++. Система предоставляет в распоряжение программистов-профессионалов высокопродуктивные средства разработки 16- и 32-разрядных приложений для операционных систем Windows 95, NT. Windows 3.1, и DOS.
Модифицированные библиотеки Object Windows Library (OWL) и Microsoft Foundation Classes (MFC), а также инструменты Visual Database Tools (VDBT) для визуальной разработки эффективных приложений баз данных - выводят Borland
C++ на новый качественный уровень систем объектно-ориентированного программирования. Borland C++ "понимает" последние языковые расширения стандарта ANSI/ISO, включая пространства имен, стандартные шаблоны и др. Последние версии Borland C++ также содержат новые инструменты программирования на языке Java.
Delphi появился на рынке в начале 1995 года и быстро завоевал титул первой системы быстрой разработки приложений для Windows,
сочетающей в единой среде высокопроизводительный компилятор, визуальные механизмы двунаправленного проектирования и новую методику масштабируемого доступа к базам данных. Уже через два года число зарегистрированных потребителей Delphi достигло полумиллиона. Новый 32-разрядный оптимизирующий компилятор Delphi 2.0 для Windows 95 и
NT дает 3 - 4-кратное повышение производительности по сравнению с Delphi 1.0 и работает в 15-20 раз быстрее, чем интерпретаторы языка Паскаль. Delphi 2.0 также включает так называемый Borland Internet Solutions Puck -
собрание компонент ActiveX для построения приложений в сетях Internet и Intranet.
IntraBuilder - это мощный инструментарий визуальной разработки приложений на языке Java. предназначенных для работы в сети Internet.
Система поддерживает масштабируемые соединения с популярными серверами сетевых коммуникаторов Netscape. Microsoft и O'Reilly.
InterBase - это масштабируемый высокопроизводительный SQL сервер баз данных, совершенный по простоте инсталляции, использования и обслуживания. Выпуск нескольких версий InterBase
для операционных систем Windows, NT, NetWare и UNIX делает его независимым от платформы, что позволяет обеспечить надежную работу реляционных баз данных в рамках локальной компьютерной сети крупного отдела или фирмы.
Visual dBASE
предоставляет проверенные двунаправленные инструменты и легкий в освоении язык для быстрой визуальной разработки приложений базы данных в сети Intranet
под Windows З.х. Windows95 или NT. Пользователи Visual dBASE легко осваивают навыки объектно-ориентированного программирования в архитектуре клиент/сервер.
1.3 Проблемы совместимости
C++ Builder предоставляет свою мощность и широкие возможности языка C++ всему семейству систем объектно-ориентированного программирования. C++ Builder может быть использован везде, где требуется дополнить существующие приложения расширенным промышленным стандартом языка C++, повысить быстродействие и придать пользовательскому интерфейсу профессиональный облик.
1.3.1 C++ Builder и
Borland C++
Между программными продуктами C++ Builder и Borland C++ существует полная и взаимная функциональная совместимость. C++ Builder добавляет процессу программирования новое качество - быструю визуальную разработку приложений на языке C++. Если вы привыкли работать в системах программирования Borland C++ (версий 3.1. 4.5 и 5.0), то ни переход к новой системе, ни возврат к старой не окажутся слишком затруднительными. В вашем распоряжении остаются удобный администратор проектных файлов, компилятор и инкрементальный загрузчик.
Поначалу C++ Builder разочарует многих разработчиков тем, что вместо привычных библиотек OWL и MFC он использует неведомую дотоле библиотеку VCL. Каждая библиотека представляет собой специфическую модель объектно-ориентированного программирования под Windows. Каждая библиотека решает проблемы ООП по-своему, и нельзя сказать, что какая-то одна справляется с задачей лучше или хуже других. Впервые инсталлировав C++Builder, я продолжал разделять приверженность к
OWL с большинством C++ программистов. Уже через несколько дней работы я начал ощущать всю прелесть, легкость и удобство использования компонентной модели. Чем больше я проникал в суть VCL, тем больше она мне нравилась, и теперь уже не могу отказаться от работы в среде C++Builder. Время от времени я обращаюсь к проверенному в боях заслуженному ветерану ВС45, чтобы повозиться со старыми OWL программами, постоянно ловя себя на мысли: "Это было бы гораздо легче сделать с помощью C++Builder". И эта мысль отдаляет меня от OWL
все дальше и дальше, так что скоро я вовсе распрощаюсь со своей прежней привязанностью.
Borland C++ версий 5.0 и выше поддерживает внедрение в ранее разработанные OWL или
MFC программы типовых форм, спроектированных в среде C++Builder на базе VCL. Разработчик идет на такого рода переделки из-за простой причины: C++Builder позволяет включить в форму все диалоги с пользователем чрезвычайно быстро, придавая приложению для Windows законченный профессиональный облик, устраняя из программы всю шелуху вспомогательного кода и оставляя только содержательную часть, воплощающую основную идею алгоритма. Таким образом, совместимость обеспечивается на уровне форм. Перенос отдельных компонент из VCL оказывается затруднительным, да и не нужным, поскольку в предельном случае форма может содержать единственную компоненту. Далее ваша комбинированная программа сможет использовать форму как окно диалога или окно клиента. Подробное описание процедур интеграции OWL или MFC программ с VCL формами и примеры создания комбинированных приложений можно найти в сети Internet no
адресу http://www.borland.com/bcppbuilder/papers
Не удивительно, что совместная работа с двумя библиотеками объектных классов заметно увеличивает размер ЕХЕ файла.
1.3.2 C++Builder и
Delphi
Все компоненты, формы и модули данных, которые накопили программисты, работающие в
Delphi, могут быть повторно использованы в приложениях C++Builder для
Windows без каких бы то ни было изменении. Delphi пока еще продолжает оставаться самой легкой в использовании и самой продуктивной системой RAD. Поэтому C++Builder идеально подойдет тем разработчикам, которые предпочитают выразительную мощность языка C++, однако хотят сохранить продуктивность Delphi. Уникальная взаимосвязь этих систем программирования позволяет при создании приложения без труда переходить из одной среды разработки в другую.
Политика, проводимая руководством отделов математического обеспечения большинства фирм и организаций, допускает свободу разработчиков в выборе языка программирования, при условии совместимости производимых ими объектов и кодов. Неукоснительно следуя этой политике, C++Builder сохраняет материальные вложения в
Delphi, вобрав в себя Библиотеку Визуальных Компонент, интуитивную интегрированную среду, визуальные механизмы двунаправленной разработки, методику наследования форм и разномасштабные средства доступа к базам данных.
Гамлетовская проблема выбора между двумя альтернативами вообще не ставится - в мире программистов достаточно жизненного пространства для "мирного сосуществования" обеих систем. Оставьте ненужные споры политического толка о преимуществе одной системы над другой. Используйте тот аппарат программирования, который сделает решение ваших задач более продуктивным, который вам больше нравится и который вам представляется более выразительным. C++ и Delphi равноправны в вашей работе - почти все, что вы написали в Delphi, можно легко использовать в C++Builder, и наоборот. Не забывайте об основном направлении своей профессиональной деятельности - создании качественных и надежных программ под операционную систему
Windows.
Преимущества разделения кода между C++Builder ч Delphi:
• Программисты могут работать в той среде, которая лучше и быстрее обеспечит реализацию поставленной текущей задачи. Delphi и C++Builder скомпилируют и соберут готовое приложение с одинаковым успехом.
• Написав некоторый объект для проекта Delphi, вы сможете повторно использовать его, без изменений, в проекте C++Builder.
• Delphi предлагает программистам очень простой, легкий в освоении синтаксис языка. Ваше начальство может не опасаться, что разработки Delphi не пойдут на C++Builder. При реализации сложных проектов большой командой программистов любой ее участник волен выбрать язык Объектный Паскаль или C++ в соответствии с индивидуальным вкусом, навыками и приверженностью. В любых комбинациях результатом совместной разработки будет единая высокоэффективная исполняемая программа.
• Выбирайте Delphi, когда вам надо как можно скорее создать какой-нибудь простой модуль или объект, даже если вы привыкли работать на C++.
Таким образом, C++Builder в симбиозе с Delphi образует исключительно продуктивную пару систем для быстрой коллективной разработки сложнейших приложений для Windows.
1.3.2.1 Какую систему выбрать?
Для тех, кто впервые остановился на перепутье, неизменным ответом на этот сакраментальный вопрос будет: "Смотря для чего".
Вы должны знать (если уже не знаете), что Delphi использует язык Объектный Паскаль, который преподается во многих специализированных школах и учебных институтах. Если вы только начинаете пробовать свои силы в программировании для
Windows, лучше, видимо, выбрать Delphi.
Придуманный Н.Виртом как средство для обучения студентов программированию, язык Паскаль стараниями специалистов корпорации
Borland видоизменился в Объектный Паскаль для Windows и стал основой мощной профессиональной системы, которой по силам любые задачи - от создания простых вычислительных программ, до разработки сложнейших реляционных СУБД.
C++Builder, как следует из названия, построен на языке C++, который наиболее распространен в крупных фирмах, занимающихся разработкой математического обеспечения профессионального уровня. Если вы обладаете определенными познаниями в языке C++ и программирование должно стать, или уже является, вашей основной специальностью - выбирайте C++Builder. Не излишне заметить, что на западном рынке профессиональные C++ разработки пользуются гораздо большим спросом, нежели продукты системы Delphi, да и оплачиваются C++ программисты несколько выше, чем "паскальщики".
Современные программисты, как правило, владеют по меньшей мере одним из этих языков. Если вы уже работаете в среде Delphi, переход на C++Builder не покажется слишком болезненным, и наоборот. Обе системы находятся на примерно одинаковом уровне популярности среди потребителей. Обе системы построены на идентичном фундаменте - Библиотеке Визуальных Компонент.
1.3.2. 2 Какая система является более мошной?
"Конечно C++Builder" - вот моя первая реакция на подобный вопрос. Однако, хорошо известно, что опытные разработчики способны справиться с поставленной задачей на Паскале лучше, чем большинство рядовых C++ программистов. Я знаком с несколькими настоящими мастерами программирования на Паскале и могу сказать, что в их руках этот инструмент способен производить удивительно красивые, эффективные и надежные творения. Если вы намерены специализироваться в области разработки компонент для VCL, используйте более удобные и гибкие средства Delphi, хотя созданные вами компоненты предназначены для внедрения в обе системы.
Для большинства рядовых пользователей обе системы покажутся поначалу (и надолго будут считаться) одинаково мощными.
1.3.2.3 Как использовать код Delphi в C++Builder
C++Builder не делает различия между тем, какие программные модули вы добавляете к проекту своего приложения - написаны они на C++ (файлы с расширением СРР) или на Delphi (файлы с расширением PAS). Компилятор свободно принимает следующие кодовые конструкции модулей Delphi 2.0: компоненты,формы, объекты, константы, простые методы и функции - все перечисленные коды можно прямо подставлять в свои проектные файлы. Технология визуального наследования форм дает возможность модифицировать формы Delphi в среде C++Builder без каких бы то ни было проблем.
Опытным программистам откроются следующие возможности системы C++Builder:
• Подгружать к проекту динамические библиотеки Delphi (файлы с расширением DLL). Разделение кода с помощью динамически подгружаемых DLL особенно целесообразно при ограниченном объеме оперативной памяти.
• Использовать OLE объекты Delphi и компоненты ActiveX. Связь OLE объектов позволяет разделять код не только с Borland C++Builder, но и с программными комплексами производства других фирм
(Microsoft Word, Excel или Visual Basic).
• Разделять с Delphi общие объекты посредством унифицированных Таблиц Виртуальных Методов (VMTs).
1.3.2.4 Как использовать код C++Builder в Delphi
В C++ разрешен ряд синтаксических конструкций (таких как множественное наследование и перегрузка функций), которые делают невозможным прямой перенос кода C++Builder в проект для Delphi. Однако, существуют следующие обходные пути:
• Компоновать СОМ объекты, написанные на C++, которые поддерживают типизированные библиотеки - все необходимые для сборки проекта приложения файлы Delphi 97 генерирует автоматически из соответствующих библиотек.
• Любая функция, написанная на C++ и помещенная в DLL, может быть прямо вызвана из Delphi - Delphi поддерживает все общие спецификаторы вызова функций CDECL, PASCAL и STDCALL.
• Обращаться к объектам, написанным на C++, из Delphi посредством упомянутых Таблиц Виртуальных Методов.
В принципе, существует возможность подгружать объектные модули (файлы с расширением
OBJ) непосредственно к Delphi в процессе сборки приложения. Однако, эта мощная методика имеет ряд ограничений, которые зачастую бывает трудно выдержать.
1.3.2.5 Перевод кода Delphi на C++Builder
Быть может, вы действительно собираетесь ретранслировать все свои программы из Delphi в C++Builder. Или, скорее всего, вам понадобилось наскоро переложить какие-то интересные примеры Delphi на язык C++. В любом случае следует остерегаться подстерегающих вас ловушек и "ложных друзей переводчика". Безусловно, вы преодолеете принципиальные различия синтаксиса объектно-ориентированных языков, а компилятор поможет вам в этом. Опасность скорее таится в ошибочной трактовке простых операторов и понятий, на которые следует обратить внимание.
Оператор членства используется для доступа к любой функции, свойству или переменной, принадлежащих некоторому классу - объявленному на Паскале или на C++. Все объекты Паскаля создаются в куче (heap) и для обращения к перечисленным членам класса используется оператор "точка" (.), например:
Label.Text := 'Hello There;';
Ha C++ дело обстоит несколько сложнее, поскольку объекты могут быть созданы не только в куче, но и на стеке. Для обращения к объекту C++, созданному на стеке, служит оператор прямого членства "точка", например:
TMyCiass myClass;
myClass.DoSomething() ;
Синтаксически приведенная кодовая конструкция сходна с паскалевой. Различие заключается в способе распределения памяти для хранения объекта. Для создания объекта в куче C++ использует оператор new, а для доступа к нему - оператор косвенного членства (->):
TMyCiass* myClass = new TMyCiass;
myClass->DoSomething() ;
delete myClass;
Важно отметить, что если вы можете создавать собственные объекты на стеке или в куче, по своему усмотрению, то объекты VCL (и производные от VCL классов) размещаются только в куче. Поэтому в операциях с объектами VCL всегда используется оператор косвенного членства.
Оператор присваивания в Паскале записывается в виде двоеточия со знаком равенства (: =), а на C++ - это одиночный знак равенства (=). Таким, образом код Delphi:
Label.Top := 20;
должен выглядеть на C++ как:
Label->Top = 20;
Приобретенная в Паскале привычка записи оператора присваивания приводит к раздражающему обилию синтаксических ошибок на C++, впрочем, легко обнаруживаемых компилятором.
Оператор логического равенства в Паскале записывается в виде одиночного знака равенства (=), а на С - это удвоенный знак равенства (==). Таким, образом код Delphi:
if Labell.Text = Label2.Text then
... должен записываться на C++ как:
if (Labell->Text == Label2->Text) { ... } Если вы случайно написали одиночный знак равенства в предыдущей инструкции сравнения, то она будет воспринята как операция присваивания - не совсем так, как вы подразумевали. Предупреждение "Possibly incorrect assignment", которое выдаст компилятор, здесь надо трактовать как ошибку. Вообще следует приучить себя доводить программу до полного отсутствия такого рода предупреждений -неизвестно, к каким последствиям они могут привести.
Одиночные и двойные кавычки служат для обозначения текстовых строк. Код Delphi:
Label. Text := 'Иван Иванов' должен выглядеть на C++ как:
Label->Text = "Иван Иванов";
Это различие будет, вероятно, раздражать вас не меньше, чем разница в записи операторов присваивания. Запутывая ситуацию еще более, C++ также использует одиночные кавычки, но совсем для другой цели.
Символьный тип служит для объявления байтовых величин. В Паскале значения символов типа Char
заключены в диапазоне от 0 до 255. В C++ символ без знака
(unsigned char) эквивалентен описанию Char Паскаля, однако символ со знаком
(signed char) имеет другой диапазон значении от -127 до +128. Два варианта присваивания значения некоторому символу на Паскале выглядят как:
с : Char ;
с := #65;
с := Char(0rd('А' ) ) ;
что эквивалентно следующему C++ коду:
char с ;
с = 65;
с = 'А'; // Здесь используются одиночные кавычки.
Заглавные и строчные буквы различаются в C++, поэтому C++Builder требует внимательного обращения с клавишей Shift. Для Паскаля же не имеет значения, верхний или нижний регистр клавиатуры использовался при подготовке текста программы. Если в C++ программе вы используете переменную, объявленную с заглавной буквы, то такая инструкция как:
top = 20;
вызовет ошибку компиляции "Undefined symbol "top"'. Булевы значения "истина" и "ложь" могут записываться на Паскале как true. True, TRUE, TruE и т.д. - как вам больше нравится, хотя по традиции используется нотация True и False (с заглавной буквы). Компилятор C++Builder разрешает использовать типизированные булевы переменные
bool, значения которых принято писать строчными буквами.
Ключевое слово Паскаля "with" на C++ не имеет эквивалента, поэтому переводя программу в C++Builder, вам придется добавлять спецификатор класса ко всем свойствам и методам. Таким образом, фрагмент кода на Паскале:
with Label do begin
Top := 20;
Left := 20;
Caption := 'Здравствуй, мир!' ;
end;
на C++ следует переписать в следующем виде:
Label ->Top =20;
Label->Left = 20;
Label->Caption = "Здравствуй, мир!";
Ключевое слово Паскаля "as" на C++ снова не имеет эквивалента, поэтому, переводя программу в C++Builder, следует использовать оператор динамического преобразования типа dynamic_cast. Этот оператор кажется менее удобным, однако его преимущество заключается в том, что проверка допустимости преобразования данного типа не связана с анализом исключений. Таким образом, фрагмент кода на Паскале:
with Sender as TLabel do
Caption := 'Здравствуй, мир1';
на C++ следует переписать в следующем виде:
TLabel* label = dynamic_cast<TLabel*>(Sender);
if (label)
label->Caption = "Здравствуй, мир1";
Паскалевы множества. C++ не поддерживает концепцию типа множества (set), как такового. Вместо множества C++ программисты используют битовые поля, которые не менее эффективны, однако более сложны для понимания и обращения. Поскольку VCL повсеместно использует паскалевы множества, для их интерпретации в C++Builder введен шаблонный класс Set. Множества очищаются посредством метода Clear. Для добавления элементов к множеству используется оператор вставки (“), а для удаления - оператор экстракции (”).
ПЕРВЫЕ ОПЫТЫ
2. C++BUILDER:
ПЕРВЫЕ ОПЫТЫ
В этой главе мы будем следовать плану первоначального знакомства со средой визуальной разработки C++Builder, изложенному в разделе Быстрый Тур (Quick Tour) системной документации.
C++Builder предназначен для быстрой разработки приложений (RAD), построенных на современном фундаменте объектно-ориентированного программирования (ООП). C++Builder сам постепенно будет помогать вам овладевать премудростями RAD, ООП и языка C++, поначалу требуя лишь минимальных предварительных знаний. Ваши навыки будут расширяться по мере роста сложности ваших разработок: чем сложнее задача, тем больший по объему код потребуется написать для ее реализации. Получение знаний перестает быть самоцелью. Вы сами убедитесь в том, что C++Builder в корне меняет процесс разработки насколько легче и быстрее вы сможете получать работающие и надежные программы для операционных систем Windows, чем при использовании традиционных интерфейсных оболочек других систем.
Иллюстрировать мощность и гибкость C++Builder будем на примере построения тестовых приложений, развивая их от простейших прототипов до законченных рабочих программ. Хотя наше первое приложение сложнее предлагаемого Быстрым Туром, автор надеется, что понять логику его разработки сможет новичок не только в ООП, но и в языке C++.
2.1 Первое знакомство
Как начинающие, так и опытные программисты начинают знакомство с новой системой с попытки создать простую программу, а чтение документации откладывается до лучших времен. При этом оценивают разные аспекты разработки:
насколько полезными оказываются ваши интуиция и опыт, лаконичность и объем кода, достоинства сервиса среды, временные затраты, удобства отладки и многое-многое другое. Последуем и мы по этому пути знакомства со средой программирования C++Builder.
Мы получим общее представление о работе со следующими основными инструментами интегрированной среды:
• Палитра компонент
содержит более 100 повторно используемых компо-нент,предлагаемых для построения приложений.
• Редактор форм
предназначен для создания интерфейса программы с пользователем.
• Редактор кода предназначен для написания текста программы, в частности, функций обработки событий.
• Инспектор объектов
позволяет визуально устанавливать свойства объектов без необходимости рутинного программирования и содержит события, которые можно связывать с кодами реакции объектов на их возникновение.
• Хранилище объектов
содержит такие объекты, как формы и модули данных, которые разделяются многими приложениями с целью уменьшения временных затрат при разработке.
C++Builder реализует визуальную методику построения приложений посредством выбора из Палитры компонент нужных управляющих элементов (Рис. 2.1). С каждой компонентой (например, кнопкой) связаны свойства, которые меняют ее вид и поведение. Любая компонента может вызывать серию событий, которые определяют ее реакцию на различные воздействия. В дальнейшем изложении символы => обозначают те действия, которые вы будете совершать в среде C++Builder.
=> Вызовите C++Builder и начните работу над новым приложением по команде File | New
Application из главного меню.
=> Щелкая мышью по вкладкам Палитры компонент, просмотрите имеющийся ассортимент элементов интерфейса программы с пользователем.
Рис. 2.1. Основные визуальные инструменты интегрированной среды C++ Builder.
Переходя от вкладки к вкладке Палитры, можно заметить, что набор доступных компонент меняется. Когда курсор мыши останавливается на значке компоненты, возникает подсказка с ее названием. Если нажать клавишу F1, справочная служба системы выдаст полную информацию о выбранной компоненте. Назначение всех компонент вы найдете в данной главе, а подробное их описание - в главе 4.
2.1.1 Визуальное проектирование
Наше первое приложение будет генерировать детскую считалочку "Десять негритят". В начальной версии потребуется только три объекта: список, поле редактирования и кнопка. Перенесем компоненты на форму проектирования и начнем постепенно развивать приложение. Метод перетаскивания (drag-and-drop) состоит в следующем: нажмите кнопку мыши на выбранной компоненте, переведите курсор на любое место формы, а затем снова нажмите кнопку мыши. Для начала ограничимся "стандартными" компонентами Палитры:
=> Выберите вкладку Standard.
=> Перетащите компоненту списка
ListBox на форму.
=> Перетащите компоненту поля редактируемого ввода EditBox.
2.1 Первое знакомство
Как начинающие, так и опытные программисты начинают знакомство с новой системой с попытки создать простую программу, а чтение документации откладывается до лучших времен. При этом оценивают разные аспекты разработки:
насколько полезными оказываются ваши интуиция и опыт, лаконичность и объем кода, достоинства сервиса среды, временные затраты, удобства отладки и многое-многое другое. Последуем и мы по этому пути знакомства со средой программирования C++Builder.
Мы получим общее представление о работе со следующими основными инструментами интегрированной среды:
• Палитра компонент
содержит более 100 повторно используемых компо-нент.предлагаемых для построения приложений.
• Редактор форм
предназначен для создания интерфейса программы с пользователем.
• Редактор кода предназначен для написания текста программы, в частности, функций обработки событий.
• Инспектор объектов
позволяет визуально устанавливать свойства объектов без необходимости рутинного программирования и содержит события, которые можно связывать с кодами реакции объектов на их возникновение.
• Хранилище объектов
содержит такие объекты, как формы и модули данных, которые разделяются многими приложениями с целью уменьшения временных затрат при разработке.
C++Builder реализует визуальную методику построения приложений посредством выбора из Палитры компонент нужных управляющих элементов (Рис. 2.1). С каждой компонентой (например, кнопкой) связаны свойства, которые меняют ее вид и поведение. Любая компонента может вызывать серию событий, которые определяют ее реакцию на различные воздействия. В дальнейшем изложении символы ==> обозначают те действия, которые вы будете совершать в среде C++Builder.
=> Вызовите C++Builder и начните работу над новым приложением по команде File | New
Application из главного меню.
=> Щелкая мышью по вкладкам Палитры компонент, просмотрите имеющийся ассортимент элементов интерфейса программы с пользователем.
=> Перетащите компоненту кнопки
Button.
=> Расположите компоненты и измените их размеры так, как вы хотели бы их видеть в окне вашего приложения.
С помощью Инспектора объектов определите начальные значения свойств компонент. В графе значений свойства Items списка нажмите кнопку в открывшемся окне редактора введите 7 первых строк стихотворения. В свойстве Caption формы и кнопки укажите их смысловые названия (соответственно, "Десять негритят" и "Результат"). В свойстве Text поля редактирования задайте строку подсказки результата ("Девять негритят").
Рис. 2.2. Визуальное проектирование формы приложения.
Рис. 2.2 показывает вид формы приложения и свойства компоненты списка TListBox после выполнения указанных действий. Теперь можно переключиться на Редактор кода и написать, как было принято, любую программу на языке C++, включая последние расширения стандарта ANSI/ISO. Однако, попытаемся сначала воспользоваться новыми средствами быстрой разработки приложений и дополнительными атрибутами компонент, заложенными в
C++Builder.
2.1.2 Свойства, методы и события
Быстрая разработка приложений подразумевает поддержку свойств, методов и событий компонент в рамках объектно-ориентированного программирования. Свойства позволяют вам легко устанавливать разнообразные характеристики компонент, такие как названия, контекстные подсказки или источники данных. Методы
(функции-члены) производят определенные операции над компонентным объектом, в том числе и такие сложные как воспроизведение или перемотка устройства мультимедиа. События связывают воздействия пользователя на компоненты, такие как активизация, нажатие кнопок или редактируемый ввод - с вашими кодами реакции на эти воздействия. Кроме того, события могут возникать при таких специфических изменениях состояния компонент как обновление данных в интерфейсных элементах доступа к базам данных. Работая совместно, свойства, методы и события образуют среду RAD интуитивного программирования надежных приложений для Windows.
=> В Инспекторе объектов укажите вкладку Событий (Events),
чтобы увидеть все события, ассоциированные с выбранным объектом.
=> Дважды щелкните мышью по компоненте кнопки, которую вы поместили на форму.
=> В открывшемся окне Редактора кода курсор покажет позицию для ввода инструкций в тело функции ButtonlClick, предназначенной для обработки события OnClick,
возникающего при нажатии кнопки.
Рис. 2.3 показывает простой код, который в ответ на очередное нажатие кнопки "Результат" присоединяет предыдущую подсказку
prev к концу списка и заносит следующую подсказку next в поле редактирования. Инструкция ListBoxl->Items->Append (prev) присоединяет, с помощью метода Append, строку prev к свойству Items объекта списка ListBoxl.
Инструкция Editl->Text = next присваивает строку
next свойству
Text объекта редактируемого ввода Editl. Строки подсказок хранятся в двумерном массиве count и индексируются целой переменной типа static, сохраняющей текущее значение между вызовами функции обработки события, возникающего при нажатии кнопки Buttonl.
Рис. 2.3. Редактор кода обеспечивает ввод и редактирование текста исполняемого модуля в файле Unit1.cpp.
Этап проектирования первой версии приложения на этом завершается и можно приступить к созданию рабочей программы.
=> Командой главного меню Run | Run запустите процесс компиляции и сборки приложения.
=> После вызова программы несколько раз нажмите на кнопку "Результат".
Рис. 2.4 показывает вид формы приложения после девяти нажатий на кнопку "Результат". Чтобы игнорировать последующие нажатия кнопки, в функции обработчика события предусмотрена инструкция возврата
if (i == 9)
return;
Разумеется, вместо предлагаемых программой подсказок, можно вводить любую строку результата - синтаксический (и, тем более, семантический) анализ текста не предусмотрен.
Рис. 2.4. Работа первой версии приложения "Десять негритят".
2.1.3 Технология двунаправленной разработки
C++ Builder не ставит никаких барьеров между программистом и его кодом. Технология двунаправленной разработки Two-Way Tools
обеспечивает контроль за вашим кодом посредством гибкого, интегрированного и синхронизированного взаимодействия между инструментами визуального проектирования и Редактором кода.
Чтобы проследить за тем, как действуют инструменты двунаправленной разработки, выполните следующие операции:
=> Откройте контекстное меню Редактора кода щелчком правой кнопки мыши, а затем с помощью опции Swap Cpp/Hdr Files переключитесь на файл объявлений Unit1.h..
=> Организуйте экранное отображение инструментов так, чтобы одновременно видеть проектируемую форму и файл Unitl.h в окне Редактора кода.
=> Перетащите еще одну компоненту кнопки OK Button на форму. В свойстве Caption кнопки укажите ее смысловое название "Новый куплет".
Рис. 2.5. C++Builder автоматически генерирует объявления в файле Unit1.h,
поддерживая постоянный контроль программиста над его кодом
Проследите за тем, что как только вы перенесли кнопку на форму, объявление объекта Button2 моментально появится в файле Unit1.h, а определение события OnClick
сгенерирует объявление метода
Button2Click обработчика этого события (Рис. 2.5). Такая синхронизация процессов проектирования формы и автоматической генерации кода действительно ускоряет визуальную разработку C++ приложения, полностью сохраняя контроль над исходным текстом программы.
Сделаем еще один шаг в разработке нашего первого приложения - заставим его генерировать куплеты автоматически. Для этого придется наполнить содержанием функцию обработки события OnClick при нажатии кнопки "Новый куплет".
Рис. 2.6. Новый обработчик события в файле Unit1.cpp.
Рис. 2.6 показывает простой код, который в ответ на очередное нажатие кнопки "Новый куплет" выводит друг за другом семь строк нового куплета, причем первая и третья строки берутся из переменной prev. Поскольку значение этой переменной присваивает обработчик события кнопки "Результат", пришлось переопределить ее в секции public пользовательских объявлений класса формы (выделенная строка на Рис. 2.5), чтобы сделать доступной обработчикам событий обеих кнопок. Смысл этого, пока еще непонятного действия ООП, прояснится в главе 3.
Рис. 2. 7 показывает работу законченной версии приложения "Десять негритят" после девяти парных нажатии на кнопки "Результат" и "Новый куплет". Список приобрел вертикальную линейку прокрутки, чтобы можно было просматривать все стихотворение целиком.
Следует запомнить, что C++Builder ассоциирует с каждым приложением три исходных файла со следующими именами по умолчанию:
• Unitl.cpp хранит исполняемый код реализации вашего приложения. Именно в нем вы записываете обработчики событий, отвечающие за реакцию программы при воздействии пользователя на объекты компонент.
• Unitl.h содержит объявления всех объектов и их конструкторов. Обратите внимание на ключевое слово _fastcall в объявлениях функций обработки событий, которые C++Builder генерирует автоматически. Благодаря _fastcall передача параметров организуется не через стек, а через регистры центрального процессора. Вызовы обработчиков событий происходят очень часто, поэтому экономия времени, затрачиваемого на выборку параметров из памяти стека, оказывается весьма ощутимой. Здесь кроется одна из причин высокого быстродействия приложений, которые компилирует и собирает C++Builder.
• Projectl.cpp
обслуживает все объекты, заключенные в приложении. Любая новая форма, программный модуль или модуль данных автоматически включаются в проектный файл. Вы можете просмотреть в окне Редактора кода содержание исходного текста проектного файла с помощью команды главного меню View | Project Source или выбрав одноименную опцию из контекстного меню Администратора проекта. Ни в коем случае не редактируйте проектный файл вручную!
Быть может, завершив разработку первого приложения, вы захотите сохранить исходные файлы для следующего сеанса, выполнив одно из следующих действий:
=> Команда File | Save All сохраняет все исходные файлы приложения.
Команда File | Save сохраняет оба файла программного модуля, а команда File
| Save As позволяет дать им новое имя.
Команда File | Save Project As сохраняет изменения всех составляющих проектного файла, используя текущие имена файлов.
2.1.4 Использование проектных шаблонов
Использование готовых проектных шаблонов из Хранилища объектов дает вам возможность начать разработку, опуская типичные для многих приложений предварительные операции по составлению главного меню и панели кнопок быстрого вызова, организации стандартных диалогов вызова и сохранения файлов. Внесенные вами изменения никак не отражаются на использовании того же проектного шаблона другими разработчиками.
Чтобы создать на основе проектного шаблона прототип приложения для работы в режиме многодокументного интерфейса (MDI), произведите следующие действия:
Рис. 2.8. Проектные шаблоны. усмотрению свойства компонент главной формы. В окне редактора фильтров, в графе значений свойства Filter компоненты TOpenDialog, укажите название и расширение файлов текстовых документов.
Рис. 2.9 показывает главную форму прототипа приложения, спроектированную на базе готового шаблона.
Рис. 2.9. Главная форма прототипа MDJ приложении для роботы с текстовыми файлами.
Если вы скомпилируете и соберете такое приложение, то увидите, что оно "умеет" только оперировать с окнами в режиме
MDI и вызывать диалог открытия файлов, не заполняя окна текстовым содержимым выбранных файлов. То есть прототип оказался нефункциональным и практически бесполезным. Чтобы придать приложению некоторое осмысленное поведение, выполните следующие действия:
Дайте команду View | Forms из главного меню и выберите из списка дочернюю форму с именем MDIChild (Рис. 2.10).
Memo
Перетащите компоненту
многострочного поля редактирования из вкладки Standard Палитры на дочернюю форму.
Очистите поле редактирования компоненты TMemo, вызвав нажатием строчный редактор свойства
Lines. Установите значение alClient свойства Align с тем, чтобы поле редактирования занимало все дочернее окно. Установите значение ssBoth свойства ScrolBars, чтобы сделать просмотр длинных текстовых файлов более удобным (Рис. 2.11).
Рис. 2.11
Проектирование дочерней формы MDlChild.
=> Снова вернитесь к главной форме, активизировав ее с помощью мыши, и выберите команду File | Open из меню приложения.
=> В окне Редактора кода курсор покажет позицию для ввода инструкции в обработчик события OnClick, возникающего при выборе соответствующего элемента меню. C++Builder автоматически генерирует объявление этой функции для компоненты главной формы TOpenDialog
(из вставки Dialogs Палитры компонент).
Рис. 2.12 показывает необходимые инструкции, составляющие тело функции FileOpenItemClick обработчика этого события.
Рис. 2.12. Реализация загрузки дочернего окна в файле Main.cpp.
Выделенная инструкция загружает строки Lines объекта Memo1 дочернего окна Child содержимым открытого текстового файла с именем
OpenDialog->FileName.
Конечно, разработка этого приложения еще далека до завершения. Когда вы скомпилируете и соберете его, то сможете редактировать текстовые файлы одновременно в нескольких окнах (Рис. 2.13). Однако сохранение результирующих файлов пока не предусмотрено - читатель без труда сам напишет код для команд меню File [Save и File |Save As.
Рис. 2.13. Работа приложения "MDI Application ".
Логичным развитием приложения, превращающим его в простейший текстовый редактор, было бы добавление команд поиска и замены в выпадающий список элемента главного меню под названием Edit.
2.2 Палитра компонент - краткий обзор
2.2.1 Компоненты вкладки Standard
Компоненты этой вкладки осуществляют включение в ваше приложение следующих типовых интерфейсных элементов Windows:
ТМашМепи | Создает панель команд главного меню для формы. |
TPopUpMerm | Создает "выскакивающее" меню для формы или для другой компоненты. |
TLabel | Отображает на форме текст названия, который нельзя редактировать. |
TEdit | Отображает область редактируемого ввода одиночной строки информации на форме. |
TIVlemo | Отображает область редактируемого ввода множественных строк информации на форме. |
TButton | Создает кнопку с надписью. |
TCheckBox | Создает элемент управления с двумя состояниями. |
TRadioButton | Создает элемент управления с двумя состояниями. |
TListBox | Отображает область списка текстовых строк. |
TComboBox | Создает комбинацию области редактирования и выпадающего списка текстовых строк. |
TScrollBar | Создает линейку прокрутки для просмотра содержимого окна, формы, списка или диапазона значений. |
TGroupBox | Создает контейнер, объединяющий на форме логически связанную группу некоторых компонент. |
TRadioGroup | Создает контейнер, объединяющий на форме группу логически взаимоисключающих радио-кнопок. |
TPanel | Создает панель инструментов или строк состояния. |
2.2.2 Компоненты вкладки Win95
Компоненты этой вкладки осуществляют включение в ваше приложение следующих типовых интерфейсных элементов Windows 95:
TTabControl | Отображает набор полей, имеющих вид частично перекрывающих друг друга картотечных вкладок. |
TPageControl | Отображает набор полей, имеющих вид частично перекрывающих друг друга картотечных вкладок, для организации многостраничного диалога. |
TTreeView | Отображает древовидный перечень элементов - заголовков документов, записей в указателе, файлов или каталогов на диске. |
TListView | Отображает древовидный перечень элементов в различных видах - по столбцам с заголовками, вертикально, горизонтально, с пиктограммами. |
TImageList | Создает контейнер для группы изображений. |
THeaderControl | Создает контейнер для заголовков столбцов. |
TRichEdit | Отображает область редактируемого ввода множественных строк информации в формате RTF. |
TStatusBar | Создает строку панелей состояния для отображения статусной информации. |
TTrackBar | Создает шкалу с метками и регулятором текущего положения. |
TProgressBar | Создает индикатор процесса выполнения некоторой процедуры в приложении. |
TUpDown | Создает спаренные кнопки со стрелками "вверх" и "вниз". Нажатие этих кнопок вызывает увеличение или уменьшение значения свойства Position. |
THotKey | Используется для установки клавиш быстрого вызова во время выполнения программы. |
Компоненты этой вкладки осуществляют включение в ваше приложение следующих специализированных интерфейсных элементов Windows:
TBitBtn | Создает кнопку с изображением битового образа. |
TSpeedButton | Создает графическую кнопку быстрого вызова. |
TMaskEdit | Создает область редактируемого ввода данных специфического формата. |
TStringGrid | Создает сетку для отображения строк по строкам или столбцам. |
TDrawGrid | Создает сетку для отображения графических данных по строкам или столбцам. |
TImage | Создает на форме контейнер для отображения битового образа, пиктограммы или метафайла. |
TShape | Рисует простые геометрические фигуры. |
TBevel | Создает линии и рамки с объемным видом. |
TScrollBox | Создает контейнер переменного размера с линейками прокрутки, если это необходимо |
Следуя пионерской традиции нисходящего визуального
ПРЕДИСЛОВИЕ
Следуя пионерской традиции нисходящего визуального стиля программирования
Delphi на Объектном Паскале, корпорация Borland выпускает на рынок систем быстрой разработки приложений RAD (Rapid Application Development) мощную систему под названием C++ Builder на языке C++.
Вместо отдельного инструментария, оперирующего визуальными элементами управления, в C++ Builder
интегрирована так называемая Палитра компонент, разделенная картотечными вкладками на несколько функциональных групп. Функциональные возможности поставляемых компонент можно достаточно просто модифицировать, а также разрабатывать компоненты, обладающие совершенно новым оригинальным поведением.
Система содержит Библиотеку из более 100 повторно используемых визуальных компонент, которые перетаскиваются мышью на форму и сразу становятся элементами управления прототипа вашей программы. Помимо известных элементов управления Windows (кнопки, линейки прокрутки, поля редактирования, простые и комбинированные списки и т.д.) Библиотека содержит новые компоненты поддержки диалогов, обслуживания баз данных и многие другие
После размещения компонент на форме. Инспектор объектов поможет вам устанавливать их свойства и предписывать событиям коды обработки. Ваш проект будет строиться постепенно, на фоне производимых вами изменений в свойствах, событиях и функциях используемых элементов. Хорошо продумано разделение и редактирование программного модуля по двум его частям: интерфейсной и собственно кодовой.
Опытным C++ программистам понравится синтаксис и структура кода разрабатываемых на C++Builder программ, хотя его графическое обрамление заметно отличается от традиционных оболочек систем разработки. Благодаря графическим средствам интегрированной среды C++Builder, новички смогут быстрее освоить стиль объектно-ориентированного программирования на C++, чем при использовании традиционного программно-текстового интерфейса других систем.
C++Builder поддерживает основные принципы объектно-ориентированного программирования - инкапсуляцию, полиморфизм и множественное наследование, а также нововведенные спецификации и ключевые слова в стандарте языка
C++.
C++ Builder обеспечивает высокое быстродействие при компиляции и сборке 32-разрядных приложений для современных операционных систем Windows 95 и Windows NT, включая OLE взаимодействие клиент-сервер. Система даже отображает время, затраченное на основные этапы построения программ. Результирующие программы хорошо оптимизированы по скорости исполнения и затратам памяти. Хотя отладочный режим низкого уровня полностью интегрирован в среду C++Builder, к отладке также пришлось привыкать. Дизайнер форм. Инспектор объектов и другие средства остаются доступными во время работы программы, поэтому вносить изменения можно в процессе отладки.
C++Builder поставляется в трех вариантах: Standard (стандартный). Professional (для профессионалов разработчиков, ориентированных на сетевую архитектуру) и Client/Server Suite (для разработки систем в архитектуре клиент/сервер). Последние два варианта дополняют стандартный исходными текстами визуальных компонент, разномасштабным словарем данных, новыми функциями языка запросов SQL для баз данных, пакетом поддержки систем
Internet, службой мониторинга программ, а также рядом других средств.
Эксперименты с тестовыми программами в рамках стандартного варианта легли в основу материала, излагаемого в книге. Испытывая систему, я переложил на C++ Builder несколько приложений, ранее написанных на Borland C++ версии 4.5. Благодаря визуальным компонентам, из программ исчезла "кодовая шелуха" обработки сообщений Windows и ресурсных файлов, и остался только содержательный код. Пользовательский интерфейс приложений приобрел законченный профессиональный облик.
Хотя C++ Builder
представляется весьма надежной системой, корпорации еще предстоит опровергнуть расхожее утверждение, что в каждой отлаженной программе (в том числе и в коммерческой) есть по меньшей мере одна ошибка. Видимо, именно этим стремлением объясняется излишняя, на мой взгляд, поспешность с рекламированием "улучшенной и расширенной" версии Borland C++ версии 5.02.
C++ Builder поддерживает связь с различными базами данных 3-х видов:
dBASE и Paradox: Sybase, Oracle, InterBase и Informix; Excel, Access, FoxPro и Btrieve. Механизм BDE (Borland Database Engine)
придает обслуживанию связей с базами данных удивительную простоту и прозрачность. Проводник Database Explorer
позволяет изображать связи и объекты баз данных графически. Используя компоненты баз данных, я построил электронную записную книжку по таблице dBASE за полчаса работы на компьютере. Наследование готовых форм и их "подгонка" под специфические требования заметно сокращают временные затраты на решение подобных задач.
Справочная служба C++ Builder оказывала мне помощь в этой и многих других подобных ситуациях. Имеется полное описание каждой управляющей компоненты, включая списки свойств и методов, а также многочисленные примеры. Изложение материала в книге было значительно улучшено и систематизировано благодаря сведениям, почерпнутым мною из справочной службы.
Благодаря средствам управления проектами, двусторонней интеграции приложения и синхронизации между средствами визуального и текстового редактирования, а также встроенному отладчику (с ассемблерным окном прокрутки, пошаговым исполнением, точками останова, трассировкой и т.п.) - C++ Builder корпорации Borland предоставляет собой впечатляющую среду разработки, которая, видимо, выдержит конкурентную борьбу с такими модными продуктами как Developer Studio фирмы Microsoft.
TDBImage
4.6.5.6 TDBImage
Создает, по аналогии с компонентой
TImage, контейнер для представления графического изображения, которое хранится в текущей записи набора данных в виде Большого Бинарного Объекта (BLOB). Установка значения
false свойства Readonly разрешает модификацию изображения.
Вы должны связать контейнер изображения с набором данных посредством компоненты источника, который определяется значением свойства DataSource. Свойство DataField содержит поле в наборе данных, к которому вы хотите обратиться.
Свойство AutoDisplay управляет автоматическим масштабированием контейнера. Чтобы исходное изображение меньшего размера растянулось на весь контейнер, установите значение true свойства Stretch. Если вы передумали и хотите вернуть изображение в исходное состояние, нажмите клавишу Esc - конечно, до того, как перейти к другой записи. Во время выполнения программы вы можете вырезать, копировать и восстанавливать изображение из базы данных, нажимая типовые комбинации клавиш (Ctrl+X, Ctrl+C и Ctrl+V).
4.6.5.7 TDBListBox
Создает, по аналогии с компонентой
TListBox, список, выбранный элемент которого становится новым значением поля текущей записи в наборе данных.
Вы должны связать список с набором данных посредством компоненты источника, который идентифицируется значением свойства
DataSource. Свойство DataField содержит поле в наборе данных, к которому вы хотите обратиться.
Элементы списка, которые может выбирать пользователь, содержатся в свойстве
Items, а номер выбранного элемента - в свойстве Itemlndex. Вы можете динамически добавлять, вычеркивать и вставлять элементы списка с помощью методов Add, Append, Delete и Insert объекта Items, например:
DBListBoxl->Items.Delete(3);
4.6.5.8 TDBComboBox
Создает, по аналогии с компонентой
TComboBox, комбинацию области редактирования и выпадающего списка текстовых вариантов для выбора. Текст, введенный в область редактирования или выбранный из списка, становится новым значением поля текущей записи в наборе данных, при условии, что свойство Readonly имеет значение false.
Вы должны связать комбинированный список с набором данных посредством компоненты источника, который идентифицируется значением свойства DataSource. Свойство DataField содержит поле в наборе данных, к которому вы хотите обратиться.
Элементы списка, которые может выбирать пользователь, содержатся в свойстве
Items, номер выбранного элемента - в свойстве Itemlndex, а сам выбранный текст - в свойстве SelText. Свойства SelStart и SelLength позволяют установить выборку части текста или обнаружить, какая часть текста выбрана. Вы можете динамически добавлять, вычеркивать и вставлять элементы списка с помощью методов Add, Append, Delete и Insert объекта Items, например:
DBComboBoxl->Items->Insert(4, "Пятый элемент списка");
Правила сортировки списка задаются свойством Sorted. Вид и поведение компоненты TDBComboBox можно выбрать в свойстве Style.
4.6.5.9 TDBCheckBox
Предоставляет по аналогии с компонентой TCheckBox чек-бокс выбора состояния, связанного с конкретной записью в наборе данных.
Вы должны связать чек-бокс с набором данных посредством компоненты источника, который определяется значением свойства
DataSource. Свойство DataField содержит поле в наборе данных, к которому вы хотите обратиться.
Если содержимое поля текущей записи совпадает со значением свойства ValueChecked, бокс переходит в начальное состояние "checked". Если содержимое поля текущей записи совпадает со значением свойства ValueUnchecked, бокс переходит в начальное состояние
"unchecked". В зависимости от установленного пользователем состояния бокса, текущая запись приобретает значение свойства ValueChecked или ValueUnchecked, при условии, что свойство Readonly имеет значение false и набор данных находится в режиме редактирования. Если вы хотите запретить пользователю изменять поля записей, установите значение true
свойства ReadOnly.
4.6.5.10 TDBRadioGroup
Предоставляет по аналогии с компонентой TRadioGroup контейнер для группы логически взаимоисключающих радио-кнопок, связанных с конкретными полями записей в наборе данных.
Радио-кнопки "группируются" при помещении их в один и тот же контейнер. Только одна кнопка из данной группы может быть выбрана. Когда пользователь выбирает кнопку, ее "значение" становится содержимым поля текущей записи в наборе данных. Значения кнопок можно редактировать в свойстве Values: первой строке Values соответствует первая строка свойства Items
и т.д.
Группу радио-кнопок можно использовать для обеспечения единственного выбора из альтернатив, представленных полями записей, а также - для отображения данных поля, имеющего несколько возможных значений.
Вы должны связать группу радио-кнопок с набором данных посредством компоненты источника, который идентифицируется значением свойства DataSource. Свойство DataField содержит поле в наборе данных, к которому вы хотите обратиться.
Добавление кнопок к компоненте
TDBRadioGroup выполняется редактированием свойства Items. Присвоение названия очередной строке свойства Items приводит к появлению этой кнопки в группирующей рамке. Значение свойства Itemlndex определяет, какая радио-кнопка выбрана в настоящий момент. Вы можете группировать радио-кнопки в несколько столбцов, устанавливая соответствующее значение свойства Columns.
4.6.5.11 TDBLookupListBox
Создает таблицу ссылок для заполнения полей информацией из другого набора данных.
Небольшая по объему таблица ссылок (lookup table) содержит коды разрешенных значений, которые может принимать некоторое поле. Например, в базе данных электронной записной книжки обязательно имеются записи почтовых адресов. Название города и улицы из конкретного адреса можно закодировать в таблицах ссылок "Города" и "Улицы". Компоненты будут эффективно обращаться с короткими кодами полей (например, "МК" вместо "Москва" или "ЛНП" вместо "Ленинградский проспект"), структура базы данных станет равномерной, а пользователь по-прежнему будет оперировать с полями полных названий.
Вы должны связать компоненту TDBLookupListBox с набором данных посредством компоненты источника TDataSource, который идентифицируется значением свойства
DataSource. Свойство DataField указывает либо на поле в наборе данных, либо на поле в таблице ссылок, к которому вы хотите обратиться. В последнем случае вам не надо определять прочие свойства - компонента сделает это самостоятельно.
Не забудьте поместить на форму еще пару компонент TTable и TDataSource, чтобы получать информацию из таблицы ссылок. Свойство ListSource идентифицирует источник таблицы ссылок. Свойство ListField представляет поле в таблице ссылок, содержащее код ссылки как таковой. Свойство KeyField определяет индекс поля ссылки, которое вы хотите скопировать в DataField.
4.6.5.12 TDBLookupComboBox
Создает комбинацию области редактирования и выпадающей таблицы ссылок для заполнения полей информацией из другого набора. Смысловое содержание свойств компонент TDBLookupComboBox и TDBLookupListBox полностью совпадает.
4.6.6 Компоненты Win 3.1
Компоненты вкладки Win 3.1 палитры компонент
представляют 7 интерфейсных элементов Windows 3.1 для включения в вашу программу.
4.6.6.1 TDBLookupList
Эта компонента поддерживает совместимость с системой Windows
3.1; ее аналогом является компонента TDBLookupListBox из вкладки Data Controls.
4.6.6.2 TDBLookupCombo
Эта компонента поддерживает совместимость с системой Windows
3.1; ее аналогом является компонента TDBLookupComboBox из вкладки Data Controls.
4.6.6.3 [TTabSet
Отображает ряд горизонтальных вкладок, нажатие на которые инициирует некоторые действия в вашей программе. Эта компонента поддерживает совместимость с Windows 3.1; ее аналогом является компонента TTabControl из вкладки Win95.
Названиявкладок вводятся в список свойства Tabs кнопкой в графе значений этого свойства. TTabSet обычно используется совместно с компонентой TNotebook для представления ряда страниц блокнота в одном диалоговом окне. В этом случае обработчик события OnClick сначала соединяет все страницы блокнота с вкладками, а затем меняет текущую страницу блокнота при нажатии на новую вкладку:
TabSetl->Tabs = Notebookl->Pages;
Notebookl->PageIndex = TabSetl->TabIndex;
Номер выбранной вкладки содержится в свойстве Tablndex. Чтобы определить, какая вкладка является (или сделать вкладку) первой видимой в наборе, воспользуйтесь свойством Firstlndex.
Несколько свойств управляют видом компоненты. Значение alBottom свойства Align требует разместить вкладки внизу формы. Цветовое различие выбранной и невыбранных вкладок задают свойства SelectedColor и UnselectedColor. Свойства BackgroundColor и DitherBackground меняют цвет и яркость фона под набором вкладок. Расстояние вкладок от краев компоненты определяют свойства StartMargin и EndMargin. Чтобы появлялись кнопки прокрутки, когда не хватает места для отображения всех вкладок, установите значение true свойства AutoScroll. Выбор между надписями и графическими изображениями на вкладках осуществляет свойство Style.
4.6.6.4 TOutline
Представляет механизм построения многоуровневой древовидной структуры для определенной иерархии данных.
Отрезки линий обводки (ветви дерева) пронумерованы, начиная с 1, и содержатся в массиве свойства Items.
Одна и та же, например, первая (верхняя) ветвь некоторого дерева Outlinel может адресоваться прямо, как Outlinel[l], или косвенно, как Outlinel. Items[l]. Дерево имеет смысл строить во время выполнения программы. Добавляйте новые родительские ветви к дереву с помощью методов Add и
AddObject. Добавляйте новые ветви потомков с помощью методов AddChild и AddChildObject. Для замены существующей ветви используйте методы Insert и InserObject. Метод Delete вычеркивает указанную ветвь.
Для ускорения выполнения перечисленных действий заключите их между методами
BeginUpdate и
EndUpdate, что избавит от избыточной перенумерации ветвей в процессе создания дерева. Значение свойства
Selectedltem отражает номер текущей выбранной ветви.
Узлы дерева могут сопровождаться идентифицирующими картинками. Свойство OutlineStyle определяет очертание дерева и тип узловых картинок, а их вид конкретизируют свойства PictureLeaf, PictureMinus, PicturePlus, PictureOpen и PictureClosed.
4.6.6.5 THeader
Отображает секционированный заголовок и позволяет менять размеры его секций, манипулируя кнопками мыши. Эта компонента поддерживает совместимость с Windows 3.1; ее аналогом является компонента THeaderControl из вкладки Win95.
Секции заголовка содержатся в свойстве Sections. Можно изменять размер выбранной секции и перетаскивать ее границы в новое положение, удерживая нажатой кнопку мыши: на стадии проектирования - правую кнопку, а во время выполнения программы - левую. Размеры других секций остаются без изменения.
Если свойство AllowResize разрешает изменение размера, то в начале изменения происходит событие OnSizing, а по окончании изменения - событие OnSized. Обработчики этих событий могут, например, выровнять текст под заголовком в соответствии с новой шириной секции.
4.6.6.6 TTabbedNotebook
Представляет ряд страниц блокнота, каждая из которых содержит собственный набор элементов управления. Нажимая на закладку, которая выступает из верхней части страницы, пользователь выбирает ее. Эта компонента поддерживает совместимость с Windows 3.1; ее аналогом является компонента TPageControl из вкладки Win95.
Доступные страницы блокнота с закладками представляют собой символьные последовательности, перечисленные как значения свойства Pages.
Окно редактора блокнота (Рис. 4.20) открывается кнопкой в графе значений этого свойства. Вы можете активизировать конкретную страницу одним из следующих способов: выбрав ее из выпадающего списка свойства ActivePage, a также перелистывая закладки с помощью опций Next Page и Previous Page контекстного меню.
Рис. 4.20. Конструирование заготовки блокнота.
Если вы хотите переопределить значение указателя Pagelndex для конкретной страницы, обратитесь к методу GetIndexForPage. Свойство TabsPerRow определяет число закладок в одном ряду. Если в блокноте имеется больше страниц, чем закладок, умещающихся в ряду, автоматически достраивается следующий ряд закладок. Шрифт надписей на закладках задается свойством TabFont.
4.6.6.7 TNotebook
Представляет ряд страниц блокнота. Пользователь выбирает нужную страницу, щелкая по ней мышью. Эта компонента обычно используется совместно с компонентой TTabSet, чтобы нажатием на соответствующие вкладки выбирать страницы. Совместное использование компонент описано в п.4.6.6.3.
Доступные страницы блокнота представляют собой символьные последовательности, перечисленные как значения свойства
Pages. Обратиться к конкретной странице блокнота можно посредством свойств Pagelndex
или ActivePage.
4.6.7 Диалоговые компоненты
Компоненты вкладки Dialogs палитры компонент
осуществляют включение в вашу программу 8 диалоговых элементов Windows, являющихся прямыми производными от абстрактного класса
TCommonDialog.
4.6.7.1 TOpenDialog
Открывает в вашей программе доступ к диалогу открытия файлов. Метод Execute активизирует окно диалога во время выполнения программы.
После того, как пользователь выбрал файл нужного типа и нажал кнопку ОК, имя файла заносится в свойство FileName. С помощью свойства
Filter пользователь определяет, какие файлы сделать видимыми в списке файлов. Свойство Filterlndex определяет фильтр по умолчанию.
Манипулируя свойством Options, вы можете изменить вид и поведение компоненты, например, разрешить выборку нескольких файлов одновременно; имена этих файлов сохранятся в списке свойства Files. Если вы хотите, чтобы расширения автоматически приписывались к именам файлов, печатаемых в области редактируемого ввода, используйте свойство DefaultExt.
4.6.7.2 TSaveDialog
Открывает в вашей программе доступ к диалогу сохранения файлов. Метод Execute активизирует окно диалога во время выполнения программы.
После того, как пользователь выбрал файл нужного типа и нажал кнопку ОК, имя файла заносится в свойство FileName. С помощью свойства
Filter пользователь определяет, какие файлы сделать видимыми в списке файлов. Свойство Filterlndex определяет фильтр по умолчанию.
Манипулируя свойством Options, вы можете изменить вид и поведение компоненты. например, разрешить выборку нескольких файлов одновременно; имена этих файлов сохранятся в списке свойства Files. Если вы хотите, чтобы расширения автоматически приписывались к именам файлов, печатаемых в области редактируемого ввода, используйте свойство DefaultExt.
4.6.7.3 TFontDialog
Открывает в вашей программе доступ к диалогу выбора шрифтов и их атрибутов. Execute активизирует окно диалога во время выполнения программы.
После того, как пользователь выбрал нужный шрифт и нажал кнопку ОК, выбранный шрифт заносится в свойство Font. Свойство Device позволяет выбрать устройство, на которое повлияет сделанное изменение шрифта.
Манипулируя свойством Options, вы можете изменить вид и поведение компоненты, например, включить кнопку Help в окно диалога или разрешить появление в списке только шрифтовых файлов с расширением .ttf(True Type Fonts).
4.6.7.4 TColorDialog
Открывает в вашей программе доступ к диалогу выбора цветов. Метод Execute активизирует окно диалога во время выполнения программы.
После того, как пользователь выбрал нужный цвет и нажал кнопку ОК, диалог закрывается, и выбранный цвет заносится в свойство
Color.
4.6.7.5 TPrintDialog
Открывает в вашей программе доступ к диалогу печати. Метод Execute активизирует окно диалога во время выполнения программы.
С помощью этого диалога можно выбрать принтер, задать диапазон печатаемых страниц, число копий с подбором (collate) страниц в копиях, а также - требование печати в файл. Указанные параметры отражаются значениями соответствующих свойств данной компоненты.
При нажатии на кнопку Setup происходит вызов диалога установок принтера (TPrinterSetupDialog). Манипулируя свойством Options, вы можете изменить вид и поведение компоненты, например, отменить появление опции печати в файл.
4.6.7.6 TPrinterSetupDialog
Открывает в вашей программе доступ к диалогу предварительных установок принтера перед печатью. Метод Execute активизирует окно диалога во время выполнения программы.
4.6.7.7 TFindDialog
Открывает в вашей программе доступ к диалогу поиска текста. Метод Execute активизирует окно диалога во время выполнения программы.
Значением свойства FindText является искомый текст. Манипулируя свойством Options, вы можете изменить вид и поведение компоненты, например, отменить появление встроенных компонент TCheckBox под названиями Match Case, Whole Word и др.
Когда пользователь вводит искомый текст и нажимает кнопку Find Next, обработчик возникающего события OnFind производит поиск текста, являющегося значением свойства FindText.
4.6.7.8 TReplaceDialog
Открывает в вашей программе доступ к диалогу поиска текста с заменой. Метод Execute активизирует окно диалога во время выполнения программы. Эта компонента обладает всей функциональностью предыдущей, кроме того, позволяя заменять найденный текст новым.
4.6.7.9 Использование диалоговых компонент текстовым редактором. Приемы отладки
C++Builder поставляется вместе с примером текстового редактора файлов формата RTF.
который демонстрирует работу диалоговых компонент TOpenDialog, TSaveDialog, TFontDialog и TPrintDialog. Приложение также использует ранее описанные компоненты TMainMenu, TRichEdit, TPanel, TEdit, TSpeedButton. TComboBox, TUpDown, TLabel, TBevel и способно предоставлять контекстно-зависимую помощь из файла
RICHEDIT.HLP.
=> По команде главного меню File |
Open Project откройте диалог выбора проектов.
=> Войдите в каталог
\...\CBuilder\Examples\Apps\RichEdit.
=> Выберите проектный файл с именем
RichEdit и нажмите кнопку Open.
Рис. 4.21. Форма текстового редактора.
Рис. 4.21 показывает главную форму текстового редактора с перечнем имен основных обработчиков событий, коды которых, как правило, и составляют содержание файлов программных модулей приложений для C++Builder.
Компонента TRichEdit занимает всю свободную область формы редактора, причем вложенные свойства
RichEditl->Paragraph содержат атрибуты форматирования параграфов документа. Объект FontName
представляет название шрифта, выбранное пользователем из выпадающего списка
TComboBox. Для установки размера шрифта служит объект FontSize области редактируемого ввода TEdit, сопряженный с кнопками TUpDown. Под панелью инструментов рас-
положена измерительная линейка с тремя регуляторами форматирования (объекты типа TLabel): левого отступа параграфа Leftind, ширины красной строки Firstind и правого отступа параграфа Rightind (последний регулятор появится после запуска программы). Внизу формы расположена панель строки состояния
StatusBar, предназначенная для вывода пояснений к командам меню.
Меню редактора включает типовые команды управления, некоторым из которых поставлены в соответствие быстрые кнопки на панели инструментов.
File | Edit | Help | ||
New Open Save Save As | |
Undo | Contents Search for Help On How to Use Help | |
Cut Copy Paste | ||||
About | ||||
Font | |
|||
Exit | |
RICHEDIT.RTF из каталога \...\CBuilder\Examples\Apps\RichEdit\Help, а затем собрать новую версию RICHEDIT.HLP с помощью утилиты
BUILDHLP.BAT. Наличие действующей команды меню Help придает программе профессиональный облик и облегчает работу пользователя.
После того, как вы запустите редактор и испытаете что он умеет, придет черед разобраться в том как он это делает. Листинг 4.1 содержит полный текст файла кодового модуля Romain.cpp с необходимыми комментариями. Несмотря на большой размер, целесообразно привести текст целиком, поскольку им вполне можно руководствоваться при создании собственных стилизованных приложений профессионального уровня с меню, быстрыми кнопками, диалогами и контекстной помощью.
ttinclude <vcl.h> ttinclude <windows.hpp> ftpragma hdrstop
#include "Romain.h"
^include "RichAbt.h"
const float RulerAdj = 4.0/3.0; //
цена деления линейки
const int GutterWid = 6; //
ширина поля подшивки
//-----_-------________--________--________--_____-__--_____
#pragma resource "*.dfm" TMainForm *MainForm;
// Конструктор главной формы приложения _fastcall
TMainForm::TMainForm(TComponent *0wner)
: TFormfOwner) { SetFileName((AnsiString)"Untitled") ;
)
// Установка текущих атрибутов Форматирования текста void _fastcall TMainForm::SelectionChange(TObject*) { char sizebuf[6];
try { FUpdating = true;
FirstInd->Left = // левая граница красной строки int(RichEditl->Paragraph->FirstIndent*RulerAdj)-
4+GutterWid;
LeftInd->Left = // левый отступ параграфа int((RichEditl->Paragraph->LeftIndent+
RichEditl->Paragraph->FirstIndent)*RulerAdj)-
4+GutterWid;
RightInd->Le?t = II правый отступ параграфа •" Ruler->ClientWidth-6-
int((RichEditl->Paragraph->RightIndent+GutterWid)*
RulerAdj) ;
BoldButton->Down = //
состояние кнопки "жирный" RichEditl->SelAttributes->Style.Contains(fsBold) ;
ItalicButton->Down = //состояние кнопки "курсив" RichEditl->SelAttributes->Style.Contains(fsltalic) ;
UnderlineButton->Down = //
состояние кнопки "подчерк." RichEditl->SelAttributes->Style.Contains(fsUnderline) ;
BulletsButton->Down = //
состояние кнопки "нумерация" bool(RichEditl->Paragraph->Numbering) ;
FontSize->Text = // размер шрифта itoa(RichEditl->SelAttributes->Size, sizebuf, 10);
FontName->Text = // название шрифта RichEditl->SelAttributes->Name;
// Состояние кнопок выравнивания параграфа switch((int)RichEditl->Paragraph->Alignment) { case 0: LeftAlign->Down = true; break;
case 1: RightAlign->Down = true; break;
case 2: CenterAlign->Down = true; break;
} }
catch (...)
{ FUpdating = false; } //
ошибка (поймано исключение) FUpdating =
false;
}
// функция возвращает установленные атрибуты текущего текста TTextAttributes *_fastcall TMainForm::CurrText(void) { return
RichEditl->SelAttributes;
}
// функция добавляет указанный шрифт к списку имеющихся int
EnumFontsProc(TLogFontA &LogFont, TTextMetricA &, int,
Pointer Data) { ((TStrings *)Data)->Add((AnsiString)LogFont.IfFaceName);
return 1;
}
// функция выбирает имена имеющихся шрифтов void _fastcall
TMainForm::GetFontNames(void) { HDC hDC = GetDC(O) ;
void * cTmp = (void *)FontName->Items;
EnumFonts(hDC, NULL, (FONTENUMPROC) EnumFontsProc, (long) cTmp ) ;
ReleaseDC(0,hDC) ;
FontName->Sorted = true;
}
// Включение имени Файла в строку заголовка приложения void _fastcall TMainForm::SetFileName(const AnsiString
FileName) ( LPSTR IpBuf = new char[MAX_pATH];
sprintf(IpBuf, "%s-%s", ExtractFileName(FileName).c_str(), Application->Title.c_str()) ;
Caption = (AnsiString)IpBuf;
FFileName = FileName;
delete IpBuf;
}
// Реакция в диалоге "Сохранить изменения?" void _fastcall TMainForm::CheckFileSave(void) { if (RichEditl->Modified) {
switch(M&ssageBox(Handle, "Save Changes?", "Confimation",
MB_YESNOCANCEL I MB_ICONQUESTION)) { case ID_YES : FileSaveClick(this) ;
case ID_CANCEL : Abort() ;
};
) }
// Запись рисок измерительной линейки ширины параграфов void _fastcall TMainPorm::SetupRuler(void) { int iCtr = 1;
char sTmp[201] ;
while (iCtr < 200) (
sTmp[iCtr++] = 9; // табулятор sTmp[iCtr++] = 'I'; // риска } Ruler->Caption = (AnsiString)sTmp;
}
// Информирует Windows о текущем размере окна редактирования void _fastcall TMainForm::SetEditRect(void) ( TRect Ret = Rect(GutterWid, 0,
RichEditl->ClientWidth-GutterWid, ClientHeight) ;
SendMessage(RichEditl->Handle, EM_SETRECT, 0, long(&Rct));
}
// Инициализирует компонентные объекты формы приложения void _fastcall TMainForm::FormCreate(TObject* /*Sender*/) { Application->OnHint = &ShowHint;
OpenDialog->InitialDir = ExtractFilePath(ParamStr(0)) ;
SaveDialog->InitialDir = OpenDialog->InitialDir;
GetFontNames() ;
SetupRuler() ;
SelectionChange(this); // атрибуты форматирования
}
// Выдает пояснения к командам меню в строку состояния void_fastcall
TMainForm::ShowHint(TObject* /*Sender*/) { StatusBar->SimpleText = Application->Hint;
}
// Создание пустого безымянного Файла по команде File I New void_fastcall
TMainForm::FileNewClick(TObject* /*Sender*/) { CheckFileSavef); // сохранить изменения? SetFileName((AnsiString)"Untitled") ;
RichEditl->Lines->Clear() ;
RichEditl->Modified = false;
}
// Загрузка выбранного Файла командой File I Open или кнопкой void _fastcall TMainForm::FileOpenClick(TObject*) { CheckFileSave(); // сохранить изменения? if (OpenDialog->Execute()) {
RichEditl->Lines->LoadFromFile(OpenDialog->FileName) ;
SetFileName(OpenDialog->FileName) ;
RichEditl->SetFocus() ;
RichEditl->Modified = false;
RichEditl->ReadOnly =
OpenDialog->Options.Contains(ofReadOnly) ;
} }
// Запись Файла командой File I Save или кнопкой void?*_fastcall
TMainForm::FileSaveClick(TObject* Sender) { if (!strcmp(FFileName.c_str(), "Untitled"))
FileSaveAsClick(Sender); // нет имени else
{ RichEditl->Lines->SaveToFile(FFileName); // то же имя RichEditl->Modified = false;
} )
// Запись Файла под выбранным именем командой FilelSaveAs void _fastcall TMainForm::FileSaveAsClick(TObject*) { if (SaveDialog->Execute()) {
RichEditl->Lines->SaveToFile(SaveDialog->FileName) ;
SetFileName(SaveDialog->FileName) ;
RichEditl->Modified = false;
} }
// Диалог печати Файла по команде File I Print или кнопкой void _fastcall TMainForm::FilePrintClick(TObject*) { if (PrintDialog->Execute()) RichEditl->Print( FFileName );
}
// Выход из программы по команде File I Exit void _fastcall
TMainForm::FileExitClick(TObject*) { Close() ;
}
// Отмена редактирования командой Edit I Undo или кнопкой void _fastcall TMainForm::EditUndoClick(TObject*) ( if (RichEditl->HandleAllocated())
SendMessage(RichEditl->Handle, EM_UNDO, 0, 0) ;
}
// Вырезка выбранного текста командой Edit I Cut или кнопкой void _fastcall TMainForm::EditCutClick(TObject*) { RichEditl->CutToClipboard();
}
// Копирование текста командой Edit I Copy или кнопкой void _fastcall TMainForm::EditCopyClick(TObject*) { RichEditl->CopyToClipboard();
}
// Вставка текста командой Edit I Paste или кнопкой void _fastcall TMainForm::EditPasteClick(TObject*) { RichEditl->PasteFromClipboard() ;
}
// Вывод указателя Файла помощи по команде Help I Contents void _fastcall TMainForm::HelpContentsClick(TObject*) { Application->HelpCommand(HELP_CONTENTS, 0) ;
}
// Поиск заданной справки по команде Help I Search...
void _fastcall TMainForm::HelpSearchClick(TObject*)
{ Application->HelpCommand(HELP_PARTIALKEY, (long) "");
)
// Вывод оглавления Файла помощи по команде Help I Нои to... void _fastcall
TMainForm::HelpHowToClick(TObject*) { Application->HelpCommand(HELP_HELPONHELP, 0) ;
}
// Диалог с логотипом редактора по команде Help I About
void_fastcall TMainForm::HelpAboutClick(TObject*)
{ Porm2 = new TPorm2(Application); // создать объект формы
Form2->ShowModal(); //
активировать диалог
delete Porm2; // уничтожить объект }
// Диалог выбора шрифта и его атрибутов по команде
Edit I font void _fastcall TMainPorm::SelectFont(TObject*) { FontDialogl->Font->Assign(RichEditl->SelAttributes);
if (FontDialogl->Execute())
CurrText()->Assign(FontDialogl->Font) ;
RichEditl->SetFocus() ;
}
// Адаптация длины линейки к текущему окну редактирования void _fastcall TMainForm::RulerResize(TObject*) { RulerLine->Width =
(int)Ruler->ClientWidth-(RulerLine->Left*2) ;
}
// Реакция на изменение пользователем размеров формы
void _fastcall TMainForm::FormResize(TObject* Sender)
{ SetEditRect(); // послать сообщение Windows
SelectionChange(Sender); //
атрибуты форматирования
)
// Перерисовка формы
void _fastcall TMainForm::FormPaint(TObject* Sender) { SetEditRect(); // послать сообщение
Windows
}
// Реакция на нажатие кнопки "Стиль шрифта жирный" void _fastcall TMainForm::BoldButtonClick(TObject*) { if (iFUpdating)
{ if (BoldButton->Down) //
изменить данный стиль CurrText()->Style = CurrText()->Style “ fsBold;
else // сбросить данный стиль CurrText()->Style = CurrText()->Style ” fsBold;
} }
// Реакция на нажатие кнопки "Стиль шрифта курсив" void _fastcall TMainForm::ItalicButtonClick(TObject*) { if (iFUpdating)
{ if (ItalicButton->Down) //
изменить данный стиль CurrText()->Style = CurrText()->Style “ fsltalic;
else // сбросить данный стиль CurrText()->Style = CurrText()->Style ” fsltalic;
} }
// Реакция на нажатие кнопки "Стиль шрифта подчеркнутый" void _fastcall TMainForm::UnderlineButtonClick(TObject*) { if (iFUpdating)
{ if (UnderlineButton->Down) //
изменить данный стиль
CurrText()->Style = CurrText()->Style “ fsUnderline;
else // сбросить данный стиль CurrText()->Style = CurrText()->Style ” fsUnderline;
) }
// Изменение размера шрифта в допустимом интервале значений void _fastcall TMainForm::FontSizeChange(TObject*) {
int fontsize = atoi(FontSize->Text.c_str());
if ((iFUpdating) && (fontsize)) { if (fontsize < 1)
{ ShowMessage("Number must be between 1 and 1638.");
FontSize->Text =1; } else if (fontsize > 1638)
{ ShowMessage("Number must be between 1 and 1638");
FontSize->Text = 1638; } CurrText()->Size =
atoi(FontSize->Text.c_str()) ;
} }
// Реакция на нажатие одной из кнопок выравнивания текста void _fastcall TMainForm::AlignClick(TObject* Sender) { if (IFUpdating)
{ TControl *oAliBtn = (TControl*)(Sender);
RichEditl->Paragraph->Alignment = (TAlignment)oAliBtn->Tag;
} }
// Реакция на выбор нового названия шрифта из списка void _fastcall TMainForm::FontNameChange(TObject*) { if (iFUpdating)
{ CurrText()->Name =
FontName->Items->Strings[FontName->ItemIndex] ;
} }
// Реакция на нажатие кнопки "Нумерованный список" void _fastcall TMainForm::BulletsButtonClick(TObject*) { if (iFUpdating)
RichEditl->Paragraph->Numbering =
(TNumberingStyle)BulletsButton->Down;
}
// Типовая проверка возможности выхода из приложения void _fastcall TMainForm::FormCloseQuery(TObject*, bool & CanClose)
{ try
{ CheckFileSaveO; } // сохранить изменения? catch (...)
{ CanClose = false; } //
ошибка (поймано исключение) }
// Определение позиции регулятора линейки, выбранного мышью void_fastcall TMainForm::RulerItemMouseDown(TObject* Sender,
TMouseButton Button, TShiftState Shift, int X, int Y) { TLabel * oTmpLabel = (TLabel *)Sender;
FDragOfs = oTmpLabel->Width / 2;
oTmpLabel->Left = oTmpLabel->Left+X-FDragOfs;
FDragging = true;
}
// Перемещение мышью выбранного регулятора линейки void _fastcall TMainForm:':RulerItemMouseMove(TObject* Sender,
TShiftState Shift, int X, int /*Y*/) ( if (FDragging)
{ TLabel * oTmpLabel = (TLabel *)Sender;
oTmpLabel->Left = oTmpLabel->Left+X-FDragOfs;
) }
// Определение позиции регулятора ширины красной строки void_fastcall TMainForm::FirstIndMouseUp(TObject* Sender,
TMouseButton Button, TShiftState Shift, int X, int Y) { FDragging = false;
RichEditl->Paragraph->FirstIndent =
int((FirstInd->Left+FDragOfs-GutterWid) / RulerAdj);
LeftIndMouseUp(Sender, Button, Shift, X, Y) ;
}
// Определение позиции регулятора левого отступа параграфа void _fastcall TMainForm::LeftIndMouseUp(TObject* Sender,
TMouseButton, TShiftState, int, int) { FDragging = false;
RichEditl->Paragraph->LeftIndent =
int((LeftInd->Left+FDragOfs-GutterWid) / RulerAdj)-RichEditl->Paragraph->FirstIndent;
SelectionChange(Sender); //
атрибуты форматирования
}
// Определение позиции регулятора правого отступа параграфа
void_fastcall TMainForm::RightIndMouseUp(TObject* Sender,
TMouseButton, TShiftState, int, int) { FDragging = false;
RichEditl->Paragraph->RightIndent =
int((Ruler->ClientWidth-Rightlnd->Left+FDragOfs-2) / RulerAdj)-2*GutterWid;
SelectionChange(Sender); //
атрибуты форматирования }
// Активизация Файла помощи на Форме void _fastcall
TMainForm::FormActivate(TObject* /*Sender*/) { Application->HelpFile = "RICHEDIT.HLP";
RichEditl->SetFocus() ;
Листинг 4.1. Кодовый файл Romain.cpp модуля редактора.
Рис. 4.22 демонстрирует работу со встроенным отладчиком на примере приложения редактора. Предположим, вы хотите узнать, правильно ли занеслось имя открытого файла в переменную FFileName. В процессе отладки вы будете пользоваться опциями контекстного меню Редактора кода, активизируемого нажатием правой кнопки мыши:
=> Найдите инструкцию, за работой которой вы хотите проследить, и нажмите клавишу
F5 (или щелкните мышью слева от выбранной инструкции). Красный цвет отмечает строку останова программы. Повторное нажатие клавиши или повторный щелчок мышью снимет точку останова. => Командами меню Run [ Run (клавиша F9), Run | Run to Cursor (клавиша
F4) или одноименной опцией запустите приложение.
=> Если программа дошла до точки останова (а это должно случиться при работе отлаженных приложений), строка окрасится синим цветом, а перед ней появится символ " ^ ".
; => Командой Run | Step Over (клавиша F8) выполните выбранную инструк-; цию в пошаговом режиме.
=> Двойным щелчком мышью выберите переменную FFileName в тексте ин-|§ струкции и опцией
Inspect (клавиши Alt+F5)
проинспектируйте начальное ti4' значение -
"Untitled".
Рис. 4.22. Некоторые приемы отладки.
=> Продолжите отладку (клавиша F9) и откройте любой файл в диалоге команды File | Open меню редактора.
=> Снова выполните выбранную инструкцию в пошаговом режиме. => Проинспектируйте новое значение выбранной переменной - она должна
содержать имя открытого файла и полный путь к нему. => Продолжите работу в режиме отладки или завершите ее командой
Run | Program Reset.
4.6.8 Системные компоненты
Компоненты вкладки System палитры компонент
осуществляют включение в вашу программу 12 специализированных системных элементов управления.
4.6.8.1 TTimer
Эта компонента инкапсулирует таймерные функции Windows API: SetTimer и KillTimer и сама обрабатывает сообщения WM_TIMER.
Свойство Interval задает частоту возникновения события OnTimer. По умолчанию Interval=1000 (одна секунда). Временной интервал передается функции SetTimer в качестве параметра.
Для каждого таймера вашей программы заведите отдельную компоненту.
4.6.8.2 TPaintBox
Предоставляет вашей программе возможность рисования на форме только внутри заданной прямоугольной области, предотвращая выход за границы поля рисования.
Если компонента TPaintBox перенесена на форму, ваша программа может рисовать на поверхности поля рисования (канве) с помощью обработчика события OnPciinl. Цвет и шрифт, используемые при инициализации объектов канвы (TCanvas),
определяются свойствами Color и Font. Если вы хотите рисовать на всей форме, воспользуйтесь обработчиком события OnPaint самой формы.
Чтобы сохранить относительное положение поля рисования неизменным, даже если пользователь изменит размеры формы, установите значение true свойства Align.
4.6.8.3 TFileListBox
Отображает список файлов в текущем каталоге, доступных программе во время ее работы. Смена текущего каталога отражается значением свойства Directory.
Свойство Mask
задает типы, а свойство FileType - атрибуты файлов, которые появятся в списке файлов. Имя и расширение выбранного файла можно будет вводить в область редактируемого ввода, если свойство
FileEdit указывает на соответствующий объект класса TEdit. Чтобы снабдить имена файлов пиктограммами. установите значение
true свойства
ShowGlyphs.
TFileListBox является производной от класса TCustomListBox, причем ее функционирование определяется, в основном, компонентой
TListBox.
4.6.8.4 TDirectoryListBox
Отображает древовидную структуру каталогов текущего диска, доступных программе во время ее работы. Смена текущего каталога отражается значением свойства Directory, а смена текущего дисковода - значением свойства Drive.
TDirectoryListBox является производной от класса TCustomListBox, причем ее функционирование определяется, в основном, компонентой
TListBox.
4.6.8.5 TDriveComboBox
Отображает комбинированный редактируемый список дисков, доступных программе во время ее работы. Смена текущего дисковода отражается значением свойства Drive.
Синхронизировать работу трех компонент TDriveComboBox, TDirectoryListBox и TFileListBox можно обработчиками событий OnChange первой и второй компоненты:
TForml->DriveComboBoxl->Change(Sender: TObject);
TForml->DirectoryListBoxl->Change(Sender: TObject);
Теперь, когда пользователь меняет дисковод в списке дисков, содержимое списков каталогов и файлов также обновится. Другой способ решения проблемы синхронизации связан с установкой значения DirectoryListBoxl свойства DirList в списке дисков, а также значения TFileListBox 1 свойства FileList в списке файлов".
4.6.8.6 TFilterComboBox
Представляет комбинированный редактируемый список фильтров для выбора имен файлов с расширениями.
Свойство Filter задает фильтры, которые появятся в списке фильтров, а свойство Mask - конкретный фильтр, выбранный пользователем в поле редактируемого ввода.
Обычно, список фильтров сопровождается списком файлов. Синхронизировать работу этих компонент можно обработчиком события OnChange первой компоненты:
TForml->FilterComboBoxl->Change(Sender: TObject) ;
Теперь, когда пользователь меняет фильтр, содержимое списка файлов обновится соответственно. Другой способ решения той же проблемы связан с установкой значения FileListBoxl свойства FileList списка фильтров.
4.6.8.7 TMediaPlayer
Элемент управления устройствами мультимедиа, которые поддерживаются MCI-драйверами
(Media Control Interface)
операционной системы Windows. Данная компонента отображает стандартную панель (Рис. 4.23) с кнопками (Play, Stop, Eject и др.), позволяющими управлять такими устройствами как звуковая плата, компакт диск, видеокамера, AVI плеер, MIDI секвенсор.
Рис. 4.23. Петель управления устройствами мультимедиа.
Конкретное устройство задается свойством DeviceType (например, dtWaveAudio или dtVideodisc). Значение dtAutoSelect свойства DeviceType вызовет попытку автоматически выбрать тип устройства, а значение true
свойства AutoOpen - открыть его вызовом метода Open. Если устройство сохраняет свои данные в файле, задайте имя этого файла в свойстве FileName.
4.6.8.8 TOleContainer
Организует связь с OLE объектами (Object Linking and Embedding) или непосредственно включает их в вашу программу.
Механизм OLE,
являющийся расширением операционных систем Windows, реализует передачу данных от программы-сервера к другой программе-контейнеру. Например, ячейки электронной таблицы можно переносить в документ текстового процессора. Передача данных осуществляется через динамически отводимую память. В отличие от механизма обмена данными через доску объявлений (Clipboard), контейнеру OLE не требуются знания формата принимаемых данных. Любой сервер может выдавать свои данные любому контейнеру, который принимает их, без какой бы то ни было интерпретации формата.
Чтобы дать возможность пользователю создать OLE объект в режиме диалога, достаточно обратиться к методу InsertObjectDialog. Методы CreateObject и CreateObjectFromFile
отвечают за создание включенного объекта, а метод CreateLinkToFile - за создание связанного с файлом объекта.
TOleContainer автоматически обслуживает процесс слияния меню, т.е. объединения контейнера элемента меню вашей формы с объектом замещения, который активизируется OLE сервером.
4.6.8.9 TDdeClientConv
Устанавливает режим динамического обмена данными (Dynamic Data Exchange) для программы DDE клиента. Используйте эту компоненту совместно с TDdeClientIlem, чтобы сделать ваше приложение DDE клиентом.
Механизм DDE,
являющийся расширением операционных систем Windows, реализует асинхронный обмен элементами данных между программой сервера (которая должна быть запущена первой) и программой клиента. Элементом обмена может быть любая выбранная порция текста, например, ячейка таблицы базы данных. Клиент инициирует соединение с сервером и выдает запросы на обмены элементами данных между двумя участниками "разговора".
Для соединения с сервером и превращения вашего приложения в DDE клиента на этапе проектирования, установите имя программы сервера в свойстве
DdeService, а тему разговора (имя формы или имя текстового файла) - в свойстве DdeTopic. В окне диалога установок режима DDE Info нажмите кнопку Paste Link.
Во время работы программы эти действия реализует метод Setlink. Чтобы послать текстовую строку связанному элементу сервера, воспользуйтесь методом PokeData,
а для посылки макрокоманды на исполнения сервером обратитесь к методу ExecuteMacro.
Если значением свойства ConnectMode задан автоматический режим разговора ddeAutomatic, клиент попытается установить связь при создании компоненты в момент исполнения программы. Если значением свойства
ConnectMode задан режим разговора ddeManual, то для установки связи вы должны написать обработчик события ОпОреп, который вызывает метод OpenLink.
4.6.8.10 TDdeClientltem
Определяет элемент динамического обмена данными DDE
клиента. Укажите имя объекта компоненты TDdeClientConv в свойстве DdeConv, a элемент обмена - в свойстве Ddeltem. Если компонента TDdeClientConv установила связь с DDE сервером, он будет автоматически и последовательно обновлять данные клиента, пока разговор не завершится.
Каждый раз, когда сервер производит обновление данных клиента, новая текстовая строка автоматически заносится в свойство
Text, и возникает событие OnChange. При обмене строками длиннее 255 символов они будут передаваться через список свойства
Lines, причем первая строка Lines
переносится в Text.
4.6.8.11 TDdeServerConv
Устанавливает режим динамического обмена данными для программы DDE сервера. Используйте эту компоненту совместно с TDdeServerItem, чтобы сделать ваше приложение DDE сервером. Тема разговора является значением свойства Name.
Если клиент посылает макрокоманды серверу, вы должны написать обработчик события OnExecuteMacro, которое возникает при приеме запроса на ее исполнение.
Использование компоненты TDdeServerConv не обязательно: если вы не поместили ее на форме, клиент будет по-прежнему посылать запросы на обновление своих данных непосредственно из компоненты TDdeServerItem. В этом случае темой разговора является имя формы, на которой находится компонента TDdeServerItem.
4.6.8.12 TDdeServerltem
Определяет элемент динамического обмена данными DDE
сервера. При использовании этой компоненты вместе с TDdeServerConv имя сервера указывается значением свойства ServerConv.
Каждый раз. когда клиент посылает запрос на обновление своих данных. сервер посылает ему содержимое свойства Text, и возникает событие OnCliange. При обмене строками длиннее 255 символов они будут передаваться через список свойства
Lines, причем первая строка Lines переносится в Text.
Чтобы протестировать связь с DDE клиентом, воспользуйтесь методом CopyToClipboard, который будет копировать содержимое свойства Text (или Lines) и информацию о связи на доску объявлений. При активизации клиента вы сможете забрать DDE данные с доски объявлений в программу клиента.
4.6.9 Компоненты отчетов
Вкладка QReport палитры компонент
содержит 11 компонент для создания и манипуляций с предварительно определенными отчетами.
Эти компоненты позволяют визуально конструировать стилизованные отчеты по данным, поставляемым любым источником, включая таблицы и запросы компонент доступа к базам данных TTable и
TQuery. Отчеты могут содержать поля заголовков, колонтитулов, сносок и итогов. QReport
предоставляет мощные средства отображения отчетов в разных видах, автоматического подведения итогов и подсчета полей - на любом уровне группировки данных отчета.
4.6.9.1 TQuickReport
Представляет и распечатывает данные в виде стилизованных отчетов QuickReport. Это основная компонента используется совместно с TDataSource и одной или несколькими TQRBand. Дважды щелкнув мышью по компоненте или выбрав опцию Preview Report из контекстного меню, вы откроете окно просмотра отчета. Укажите источник данных в свойстве DataSource. Чтобы заполнить окно просмотра отчета или напечатать его, обратитесь к методам Preview или Print.
4.6.9.2 TQRBand
Каждый образец этой компоненты отвечает за представление и печать своего поля (полосы) отчета. Свойство BandType содержит выпадающий список вариантов полос отчета (заголовки, колонтитулы, сноски, вложенные поля деталировок, итоги и т.д.), из которого вы выбираете ту полосу, которую будет обсуживать данная компонента. Распечатка одних полос отчета будет происходить автоматически, а распечатка других потребует связи с компонентами TQRGroup или TQRDetailLink.
4.6.9.3 TQRGroup
Поддерживает работу с групповой полосой отчета.
4.6.9.4 TQRDetailLink
Поддерживает работу с перекрестными ссылками на вложенные полосы деталировки, осуществляя связь по принципу master-detail.
4.6.9.5 TQRLabel
Отображает текст в виде заголовков столбцов отчета. Вы можете менять статический текст заголовка в свойстве Caption в любой момент во время подготовки отчета. Если вам требуется выводить текст поля записи некоторого набора базы данных, следует воспользоваться компонентой QRDBText.
4.6.9.6 TQRMeino
Отображает, по аналогии с компонентой TDBMemo, многострочный текст поля текущей записи в наборе данных источника.
4.6.9.7 MTQRDBText
Отображает, по аналогии с компонентой TDBText, однострочный текст поля текущей записи в наборе данных источника.
4.6.9.8 TQRDBCalc
Автоматизирует процесс суммирования и подсчета полей в наборе данных источника.
4.6.9.9 TQRSysData
Включает в отчет системную информацию определенного вида, выбираемого из выпадающего списка свойства Data.
4.6.9.10TQRShape
Отображает в отчете прямые линии, рамки и простые геометрические фигуры (прямоугольник, эллипс), выбираемые из выпадающего списка свойства Shape.
4.6.9.11 TQRPreview
Облегчает процесс создания различных видов просмотра отчета, включая операции прокрутки и масштабирования. Несколько компонентных методов позволяют управлять поведением отчета во время просмотра. Например, вызов метода
ZoomToFit в обработчике события OnClick по нажатию некоторой кнопки будет масштабировать страницу в установленное поле просмотра.
4.6.9.12 Пример использования компонент отчетов
C++Builder поставляется вместе с примером, который демонстрирует следующие разновидности работы с компонентами отчетов:
• создание этикеток для почтовых отправлений;
• создание простого отчета;
• модификация оригинальных предварительных видов печати;
• разработка отчетов по принципу
master-detail;
• сохранение выборочных текстовых файлов детализации.
Рис. 4.24. Форма приложения для работы с отчетами.
Удостоверьтесь предварительно, что локальный псевдоним (alias)
демонстрационной базы данных BCDEMOS установлен с помощью утилиты конфигурации
BDE Configutation. Чтобы вызвать проект приложения, выполните следующие действия:
=? По команде главного меню File | Open Project откройте диалог выбора проектов.
=> Войдите в каталог \..
.\CBuilder\Examples\Dbtasks\Quickrpt.
=> Выберите проектный файл с именем
Qrdemo и нажмите кнопку Open. Рис. 4.24 показывает форму демонстрационного приложения Quick Report.
=> Командой главного меню Run | Run запустите процесс компиляции и сборки приложения.
=> После вызова программы поэкспериментируйте с разными опциями.
4.6.10 Компонента ActiveX
Входящие в варианты поставки C++Builder Professional и
C++Builder Client/Server Suite
компоненты обеспечивают поддержку промышленного стандарта ActiveX/OCX. Созданные вами или третьими лицами компоненты Delphi ActiveX можно интегрировать в среду так, чтобы они вошли в Палитру компонент для немедленного использования. В частности, вы можете расширить стандартный вариант Палитры новыми компонентами ActiveX, включив пакет NetManage для обучения и разработки приложений в сети Internet. В конце главы 6 вы найдете подробную инструкцию того, как это делается. В типовом варианте поставки C++Builder Standard вкладка ActiveX палитры компонент
содержит единственную компоненту
ChartFX для построения на вашей форме разнообразных диаграмм, графиков, таблиц и проверки правописания на многих языках.
4.6.10.1 ChartFX
Дважды щелкнув мышью по компоненте или выбрав опцию Properties из ее контекстного меню. вы откроете диалоговое окно установок вида и множества других характеристик диаграммы. Свойства, методы и события компоненты ChartFX обеспечивают выполнение следующих основных операций над диаграммами:
• Создание простых диаграмм.
• Передача исходных данных в новую диаграмму.
• Редактирование данных в существующей диаграмме.
• Изменение легенд, заголовков и других визуальных атрибутов диаграммы (видов, цветов, орнаментов заливки, шрифтов, координатных сеток и т.д.).
• Создание инструментов и других визуальных элементов управления диаграммой.
Подробное руководство по использованию компоненты ChartFX можно вызвать из справочной службы при нажатии клавиши CtrI+Fl.
4.7 Дизайнер меню
Дизайнер меню (Menu Designer) облегчает процесс создания меню для вашей формы. Вы можете легко добавлять, вычеркивать и переупорядочивать команды меню непосредственно в окне дизайнера.
Чтобы начать процесс создания меню: __
1. Поместите значок компоненты MainMenu или PopUpMenu из вкладки стандартных компонент на вашу форму.
2. Откройте окно дизайнера меню. Оставив значок компоненты выбранным, дважды щелкните на нем левой кнопкой мыши или выберите опцию
Menu Designer из контекстного меню компоненты. (Открыть контекстное меню дизайнера можно щелчком правой кнопкой мыши в любом месте его окна или нажатием клавиш Alt+FlO).
3. Введите имя меню и нажмите клавишу
Enter, если желаете изменить имя, установленное по умолчанию (MainMenul).
Чтобы добавить команды в меню:
1. Выберите позицию, в которую вы хотите поместить новую команду.
2. Введите название команды (или символ "-", если вы хотите добавить разделительную черту в выпадающий список команд) и нажмите клавишу Enter. Указанному названию (Caption) C++Builder автоматически поставит в соответствие некоторый идентификатор (Name) и занесет в Инспектор объектов. Так команде меню
File ставится в соответствие идентификатор Filel, а русскоязычному названию Файл - порядковый номер идентификатора с префиксом N. Если названию предшествует символ "&", то обращение к соответствующей команде меню можно выполнять совместным нажатием клавиш Alt+первая (подчеркнутая) буква названия. Можно задать любую комбинацию "горячих" клавиш или выбрать ее из списка ShortCut Инспектора объектов.
3. Нажмите клавишу Enter, чтобы перейти к добавлению следующей команды выпадающего списка или клавишу Esc, чтобы вернуться в главное меню. С помощью левой кнопки мыши или клавиш позиционирования <-, ->, Г, ^ можно перемещаться по списку и переходить в главное меню. Повторное нажатие клавиши Enter завершает процесс.
Чтобы вставить поле для новой команды в меню:
1. Выберите элемент меню, рядом с которым вы собираетесь сделать вставку.
2. Нажмите клавишу Ins или выберите опцию
Insert из контекстного меню. Поле новой команды вставляется над выбранным элементом в выпадающем списке или слева от выбранного элемента в главном меню.
Чтобы удалить команду из меню:
1. Выберите команду, которую вы собираетесь удалить.
2. Нажмите клавишу Del или выберите опцию
Delete из контекстного меню.
Чтобы создать поле для вложенного подменю:
1. Выберите элемент, для которого вы хотите создать подменю.
2. Нажмите клавиши Ctrl+-> или выберите опцию
SubMenu из контекстного меню.
Чтобы переместить элемент:
1. Выберите элемент меню и, удерживая левую кнопку мыши, перенесите его на новое место. Вид курсора поможет вам выбрать разрешенную позицию.
2. Отпустите кнопку мыши.
Вы можете конструировать меню, включая в него те элементы, которые вам требуются, или начать конструирование с шаблонов, содержащих часто употребляемые команды.
Чтобы добавить шаблон к вашему меню:
1. Выберите опцию Insert From Template из контекстного меню.
2. Выберите нужный шаблон из предложенного списка и нажмите кнопку ОК или клавишу Enter. Элементы шаблона образуют подменю элемента, на который указывал курсор в выпадающем списке команд, или создадут новый элемент в главном меню.
Чтобы сохранить ваше меню в виде шаблона:
1. Выберите опцию Save As Template из контекстного меню.
2. Введите имя шаблона и нажмите кнопку ОК или клавишу Enter.
Чтобы удалить шаблон:
1. Выберите опцию Delete Templates из контекстного меню.
2. Выберите нужный шаблон из списка и нажмите кнопку ОК или клавишу Enter. Внимание: Данный шаблон будет удален не только из списка, но и из файла шаблонов BCB.DMT
на жестком диске.
В процессе работы программы можно динамически добавлять в меню новые исполняемые или информационные команды. Операции с множественными меню позволяют динамически менять порядок расположения элементов активного (главного) меню, вводя или замещая альтернативные элементы.
Чтобы организовать слияние меню во время выполнения:
1. Выберите меню, которое вы хотите активизировать из выпадающего списка свойства Menu вашей формы. Операции слияния применимы только к активному меню. Если форма содержит несколько компонент меню, вы можете переключать активное меню во время выполнения программы, устанавливая новые значения свойства Menu, например: Forml->Menu = ВтороеМеню;
2. Определите порядок, в котором команды меню из разных форм будут выстраиваться
на разделяемой панели главного меню: чем меньше номер свойства Grouplndex, тем
левее располагается соответствующая команда. Пусть главное (активное) меню содержит только две команды: "Файл" (Grouplndex=0) и "Справка" (5). Тогда между ними разрешается вставить до четырех команд из меню другой формы-потомка с номерами 1,2,3, и 4, например, "Редактор" (1) и "Формат" (3). Далее можно заместить команду "Формат" (3) на команду "Работа" (3) из меню другой формы-потомка.
Рис. 4.25 иллюстрирует процесс проектирования меню на форме прототипа тестового приложения, предназначенного для обработки файлов. Открыв контекстное меню дизайнера, обратите внимание на опцию Insert From Resource. Судя по названию, она должна давать возможность создавать меню на базе ресурсных файлов с расширением .гс. К сожалению, автору не удалось заставить дизайнер использовать ресурсы приложений, ранее написанных на Borland C++ версии 4.5. Будем надеяться, что в широко рекламируемой версии 5.02 эта недоработка будет исправлена.
Сконструированное вами меню всегда отображается на вашей форме в том виде, в котором оно появится при запуске программы, поэтому нет необходимости компилировать и запускать программу, чтобы увидеть результат проектирования. Вы можете продолжить редактирование меню в окне Инспектора объектов, активизировав его щелчком левой кнопки мыши.
4.8 Итоги
Мы научились пользоваться основными инструментами интегрированной среды визуальной разработки C++Builder, уделив основное внимание назначению элементов Палитры компонент - "кирпичей", составляющих основу вашего приложения.
ВИЗУАЛЬНАЯ РАЗРАБОТКА ПРИЛОЖЕНИЙ БАЗ ДАННЫХ
5. ВИЗУАЛЬНАЯ РАЗРАБОТКА ПРИЛОЖЕНИЙ БАЗ ДАННЫХ
Разработка Систем Управления Базами Данных (СУБД) раньше всегда была очень трудоемким и медленным процессом, отягощенным необходимостью учитывать массу специфических деталей подсистем обслуживания различных баз данных на низком уровне. C++Builder
принимает на себя выполнение этих рутинных операций позволяя сосредоточиться на решении основной задачи.
Все приложения СУБД, создаваемые в среде C++Builder, являются клиентами в архитектуре программного взаимодействия клиент/сервер. Клиент выдает запросы к серверу базы данных на получение или передачу информации. Сервер обрабатывает запросы от множества клиентов одновременно, координируя доступ к данным и их обновление.
Все приложения СУБД, создаваемые в среде C++Builder, основаны на компонентах пользовательского интерфейса с некоторой базой данных, которые предоставляют удивительно легкие в использовании средства разработки специальных приложений. Львиная доля времени процесса разработки уходит на визуальную установку свойств выбранных компонент. Удачно спроектированное приложение всегда обеспечивает простоту просмотра и редактирования данных пользователем, независимо от сложности структуры используемой модели данных. Данная глава с очевидностью покажет, что формы приложений СУБД для типично сложной системы в архитектуре взаимодействия клиент/сервер действительно могут быть созданы в интегрированной среде C++Builder весьма быстро и с малыми усилиями.
Воздействия на компоненты многогранны: их реакция на события обеспечивается стандартными методами, а установка значений свойств может производиться во время работы приложения. Таким образом, простое вначале приложение в процессе разработки постепенно усложняется, чтобы в конце концов выглядеть совершенно профессиональным программным изделием.
5.1 Организация доступа к базам данных
C++Builder организует доступ приложения к данным таким образом, чтобы полностью отстранить разработчика от специфики обслуживания конкретной базы дан
Рис. 5.2. Основная страница Drivers утилиты конфигурации BDE.
5.1.2.1 Страница Drivers
Используется для модификации установок, применяемых драйверами BDE при создании, сортировке и обслуживании таблиц базы данных. В графе Driver Name
перечисляются типы драйверов, инсталлированных на вашей рабочей станции. Драйверы
STANDARD обеспечивают доступ к базам данных Paradox и dBASE, а прочие драйверы - соединения с серверами SQL и ODBC. В графе
Parameters перечислены все прослеженные утилитой параметры выбранного типа драйвера вместе с их текущими установками. Чтобы изменить некоторую установку, выберите имя нужного драйвера и измените его старое значение. При первой инсталляции драйвера выбираются значения по умолчанию. В графе Description кратко описывается назначение выбранного параметра.
Чтобы получить доступ к ODBC (например, к базе данных Microsoft Access), надо сначала создать соответствующий источник данных, и только потом вызвать утилиту конфигурации BDE для подключения к этому источнику. Кнопка New ODBC Driver
открывает диалог добавления соединения ODBC к имеющемуся списку (Рис. 5.3). Кнопка Delete ODBC Driver разрешает вычеркнуть выбранный драйвер.
Рис. 5.3. Диалог добавления драйвера соединения ODBC.
5.1.2.2 Страница Aliases
Используется для выполнения операций вычеркивания и модификации псевдонимов баз данных типа STANDARD, SQL или ODBC. Alias Names перечисляет все имеющиеся псевдонимы. Позже вы будете использовать псевдоним для указания имени нужной базы данных в свойстве DatabaseName компонент таблицы TTable или запроса TQuery. Графа Parameters содержит, в частности, тип сервера и полный путь к каталогу, содержащему нужные таблицы. В графе Description кратко описывается назначение выбранного параметра.
Кнопка New Alias открывает диалог добавления нового псевдонима выбранного типа к имеющемуся списку (Рис. 5.4). Кнопка Delete Alias разрешает вычеркнуть выбранный псевдоним.
Рис. 5.4. Дисиог добавления псевдониме!.
5.1.2.3 Страница System
Используется для модификации установок системных и сетевых параметров, которые BDE использует при запуске приложения. Эта информация хранится в регистрационном файле Windows. В графе Parameters перечислены все прослеженные утилитой системные и сетевые параметры вместе с их текущими значениями. Чтобы изменить установку, выберите нужный параметр и измените его старое значение. В графе Description кратко описывается назначение выбранного параметра.
5.1.2.4 Страница Date
Используется для установки формата отображения даты. В графе Parameters перечислены все прослеженные утилитой параметры даты вместе с их текущими значениями. Чтобы изменить установку, выберите нужный параметр и измените его старое значение. В графе Description кратко описывается назначение выбранного параметра.
5.1.2.5 Страница Time
Используется для установки формата отображения времени. В графе Parameters перечислены все прослеженные утилитой параметры времени вместе с их текущими значениями. Чтобы изменить установку, выберите нужный параметр и измените его старое значение. В графе Description кратко описывается назначение выбранного параметра.
5.1.2.6 Страница Number
Используется для установки формата отображения числовых величин. В графе Parameters
перечислены все прослеженные утилитой параметры числовых величин вместе с их текущими значениями. Чтобы изменить установку, выберите
нужный параметр и измените его старое значение. В графе Description кратко описывается назначение выбранного параметра.
5.1.3 "Живые данные"
Основная задача быстрой разработки приложений управления базами данных состоит в том, чтобы оперативно обнаруживать проблемы в экранном представлении базы данных и ошибки в запросах к ней.
Большинство систем программирования реляционных баз данных требуют написания специализированных процедур для выборки данных из таблиц и заполнения полей в соответствующих диалогах. Чтобы удостовериться, что ваша программа производит ожидаемые результаты, раньше приходилось многократно проходить по циклу редактирование => компиляция => сборка, непроизводительно расходуя время. ^
Иначе обстоит дело при работе в визуальной среде C++Builder,
компоненты которой поддерживают поступление "живых данных". Когда вы поместите на форму компоненты доступа и управления, определите их свойства, отвечающие за связь с базой данных, вам будут представлены данные точно так же, как их увидит пользователь. C++Builder объединяет три этапа разработки в единый производственный процесс. В результате удается строить СУБД, базирующиеся на текущих требованиях заказчика, вместе с тем гибкие настолько, чтобы быстро адаптировать их к новым запросам пользователей.
Форма приложения будет выглядеть одинаково на стадии проектирования и во время работы программы. Вы сможете производить необходимые коррекции экранного представления базы данных и запросов к ней, даже не компилируя программу. Способность интерактивного испытания запросов ставит процесс разработки СУБД на самый высокий качественный уровень.
5*2 Использование визуальных компонент
Одним из важнейших достоинств интегрированной среды C++Builder является наличие удобных средств быстрой визуальной разработки приложений СУБД -специализированных компонент баз данных. В отличие от разделяемых элементов управления VBX, C++Builder компилирует компоненты в единую исполняемую программу, что существенно повышает ее надежность и быстродействие. Только очень опытные программисты способны создать программу подобного уровня качества и гибкости, используя исключительно прямые обращения к соответствующим функциям Windows API. При таком подходе даже простое приложение требует написания непомерного по объему кода.
C++Builder предоставляет разработчикам интерфейсные элементы баз данных из Библиотеки Визуальных Компонент на следующих двух вкладках Палитры компонент:
• Компоненты управления данными
Data Control (такие как область редактирования TDBEdit или сетка TDBGrid) обеспечивают отображение и редактирования записей на форме приложения.
• Компоненты доступа к данным Data
Access (такие как таблица TTable или запрос TQuery) адресуют фактические данные, хранящиеся в файле базы данных, а компонента источника TDataSource служит как интерфейс межкомпонентной связи.
Для. работы с базами данных необходимо проанализировать и правильно установить значения ключевых свойств компонент доступа и управления. В дальнейшем будем выделять ключевые свойства, методы и события подчеркиванием.
C++Builder поддерживает "трехступенчатую" модель разработки приложения баз данных. В этой модели компонента управления связана с компонентой источника, а та, в свою очередь, получает фактические данные таблицы или запроса посредством механизма BDE. Рис. 5.5 показывает пример взаимосвязи компонент.
Рис. 5.5. Взаимосвязь компонент управления и доступа к содержимому баз данных.
Среднее звено, компонента TDataSource, допускает менять фактическую таблицу на стадии проектирования формы без какого бы то ни было перепрограммирования самого приложения - все отображаемые элементы связаны с источником, а не с питающими его таблицей или запросом. Кроме того, компоненты источников берут на себя задачу синхронизации обмена данными между парами таблиц по принципу master-detail.
5.2.1 Компоненты доступа к данным
В этом разделе мы подробно рассмотрим работу пяти компонент доступа, первые три из которых составляют основу любого приложения баз данных. Описание остальных компонент можно найти в главе 4.
5.2.1.1 Источники данных
Как уже было сказано, невидимая компонента TDataSource действует как интерфейс между некоторым объектом набора данных (таблица, запрос) и визуальной компонентой управления. С одной стороны, все наборы данных должны быть ассоциированы с некоторым источником. С другой стороны, каждая компонента управления должна быть ассоциирована с источником, чтобы получать данные для отображения и редактирования. Каждой компоненте набора данных должна соответствовать по меньшей мере одна компонента источника. Далее мы увидим, что все компоненты управления имеют свойство
DataSource, значение которого замыкает трехступенчатую связь. Рис. 5.6 показывает свойства компоненты источника в окне Инспектора объектов:
Рис. 5.6. Свойства источники.
AutoEdit
разрешает или запрещает режим редактирования записей, вводимых в поля компонент управления. Значение true включает режим редактирования по умолчанию.
DataSet
определяет имя конкретного набора данных (таблицы или запроса), который питает данный источник. Можно переключаться с одного набора данных на другой "на лету", во время выполнения программы. Следующий простейший код реализует попеременное подключение объекта источника DataSourcel к таблице заказчиков "Заказчики" или к таблице "Заказы":
DataSourcel->DataSet = "Заказы";
else
DataSourcel->DataSet = "Заказчики";
Чтобы синхронизировать работу компонент управления на двух формах, достаточно установить свойство DataSet на один и тот же набор данных:
void_fastcall TForm2::FormCreate (TObject *Sender)
{
DataSourcel->Dataset = Forml->Tablel;
Name определяет содержательное название данной компоненты, отличающее ее от других источников данных вашего приложения. Целесообразно давать источнику имя. которое отражает название ассоциированного с ним набора данных. Например, если вы собираетесь работать с таблицей клиентов
"Clients", заданной свойством DataSet, то пусть значением свойства Name будет "ClientsSource" или подобное имя.
С компонентой TDataSource связаны три события:
• OnDdlciC/iange
возникает при перемещении курсора на новую запись и используется при необходимости "ручной" синхронизации поведения компонент управления.
• OnStciteChcinse
возникает при изменении свойства State наборов данных. Например, обработчик события (Листинг 5.1) будет отслеживать изменения состояния таблицы MyTable, выводя па форму соответствующие текстовые отметки.
void_fastcall TFormI::StateChange(TObject *Sender)
{
char S[20];
switch (MyTabie->State) {
case dslnactive:
strcpy(S,"Таблица неактивна");
break;
case dsBrowse:
strcpytS, "Идет просмотр");
break;
case dsEdit:
strcpytS, "Идет редактирование");
break;
case dslnsert:
strcpy(S, "Идет вставка записи");
break;
} // Вывод текстовой строки S
}
Листинг 5.1. Обработчик события
OnStateChange источника.
• OnUpdateData возникает перед обновлением текущей записи и используется при необходимости синхронизации поведения обычных компонент с изменениями некоторого набора данных.
5.2.1.2 Таблицы
Компонента таблицы представляет собой один из самых фундаментальных и гибких компонентных классов. TTable
устанавливает прямую связь с таблицей базы данных посредством BDE, причем все записи или столбцы этой таблицы становятся доступными для приложения - как раздельно, так и внутри определенного интервала адресов. Рис. 5.7 показывает свойства компоненты таблицы в окне Инспектора объектов:
Рис. 5.7. Свойства таблицы. |
Active разрешает или запрещает режим просмотра "живых данных" таблицы на этапе проектирования. Значение true или метод Open открывают просмотр таблицы. Значение false (устанавливается по умолчанию) или метод Close закрывают просмотр. DatabaseName содержит псевдоним базы данных или полный путь к ее каталогу. Использование псевдонима всегда предпочтительнее: вы можете переназначить физический носитель данных, например, заменив локальный дисковод на сетевой. Перекомпиляция приложения не требуется - просто измените путь на вкладке Aliases в утилите конфигурации BDE. |
TableName
позволяет выбрать фактическое имя таблицы из выпадающего списка с именами всех таблиц в адресуемой базе данных.
Exclusive
разрешает или запрещает другому приложению обращаться к таблице, пока вы ее используете сами. Значение false запрещает исключительный доступ по умолчанию.
IndexFiles
открывает диалог выбора индексного файла для таблицы.
IndexName
задает правило отображаемых сортировки данных, отличное от упорядочивания по первичному ключу (primary key order).
Filter позволяет устанавливать критерий фильтрации, в соответствии с которым адресуется некоторое подмножество записей таблицы.
ReadOnly управляет правами доступа в процессе выполнения программы. Значение
false разрешает запись по умолчанию.
MasterFields и MasterSource
участвуют в образовании связи двух таблиц (ведущей и ведомой) по принципу master-detail.
Следующая процедура иллюстрирует процесс создания простой формы для демонстрационной базы данных BCDEMOS, в которой пользователь может прокручивать записи таблицы заказчиков CUSTOMER с отображением всех заказов таблицы ORDERS, сделанных текущим заказчиком:дексного файла для таблицы.
IndexName задает правило отображаемых сортировки данных, отличное от упорядочивания по первичному ключу
(primary key order).
Filter позволяет устанавливать критерий фильтрации, в соответствии с которым адресуется некоторое подмножество записей таблицы.
ReadOnly управляет правами доступа в процессе выполнения программы. Значение false
разрешает запись по умолчанию.
MasterFields и
MasterSource участвуют в образовании связи двух таблиц (ведущей и ведомой) по принципу master-detail. Следующая процедура иллюстрирует процесс создания простой формы для демонстрационной базы данных BCDEMOS, в которой пользователь может прокручивать записи таблицы заказчиков CUSTOMER с отображением всех заказов таблицы
ORDERS, сделанных текущим заказчиком:
1. Выполните команду главного меню File | New Data Module, чтобы открыть контейнер нового модуля данных DataModule2. В этот модуль поместите две пары компонент доступа к базам данных TTable и TDataSource (Рис. 5.8). 2. Установите свойства объекта ведущей таблицы Tablet DatabaseName = BCDEMOS TableName = CUSTOMER.DB Name = CustomersTable |
Рис. 5.8. Модуль данных. |
DatabaseName = BCDEMOS TableName = ORDERS.DB Name = OrdersTable
4. Установите свойства объекта источника DataSourcel DataSet = CustomersTable Name = CustomersSource
5. Установите свойства объекта источника DataSource2 DataSet = OrdersTable Name = OrdersSource
6. Поместите на форму пару компонент управления сеткой TDBGrid.
7. Выполните команду File | Include Unit Hdr, чтобы указать, что данная форма должна использовать созданный модуль данных.
8. Установите свойство объекта первой сетки DBGridI
DataSource = DataModule2->CustomersSource и свойство объекта второй сетки
DBGrid2
DataSource = DataModule2->OrdersSource
9. Активизируйте таблицу OrdersTable модуля данных и установите свойство
MasterSource = CustomersSource, чтобы связать ведущую таблицу CUSTOMER с ведомой таблицей
ORDERS.
10. Дважды щелкните мышью в графе значений свойства MasterFields, и в открывшемся окне дизайнера связи полей
- выберите номер заказчика CustNo (связующее поле таблиц) из выпадающего списка Available Indexes;
- задайте CustNo в списках Detail Fields и Master Fields;
- нажмите кнопку Add, чтобы добавить в список Joined Fields соединение CustNo -> CustNo;
- нажмите кнопку OK, подтверждая сделанный выбор.
11. Установите свойство Active = true для таблиц CustomersTable и OrdersTable с тем, чтобы сразу же отобразить живые данные в сетках на форме.
12. Скомпилируйте и запустите приложение. Рис. 5.9 показывает работу связанных таблиц: при прокрутке записей таблицы заказчиков вы увидите только те записи в таблице заказов, которые относятся к текущему заказчику.
Рис. 5.9. Связь таблич по принципу
muster-detail.
Методы Locate
и Lookup
используются для поиска указанных записей как в индексных таблицах, так в таблицах с ключами: эти методы реализуют самый быстрый из возможных способов поиска в данной таблице. Если столбцы для поиска индексированы и индекс совместим с указанными опциями, используется способ индексного поиска. В противном случае методы создают для BDE соответствующий фильтр.
Locate производит поиск специфической записи и позиционирует курсор на нее. В простейшем варианте вы передаете методу название столбца для поиска, искомое значение ключа записи и флаг опций. Листинг 5.2 содержит фрагмент кода, обеспечивающего поиск в столбце "Имя" таблицы MyTable первой записи со значением "Иван". Если поиск завершился успешно. Locate возвращает значение true,
и найденная запись становится текущей. Если искомая запись не найдена. Locate возвращает значение false, и позиция курсора не меняется.
( boot Success;
TLocateOptions Options;
Options “ loPartialKey;
Success = MyTable->Locate("Имя", "Иван", Options);
}
Листинг 5.2. Простейший пример использования метода Locate.
Возможности метода проявляются при поиске вариантных значений записи в нескольких столбцах таблицы. Обобщенный синтаксис описания метода имеет следующий вид:
bool _fastcall Locate(const AnsiString KeyFields, const Variant SKeyValues, TLocateOptions Options);
В главе 3 отмечалось, что различные типы искомых значений объявляются в шаблонном классе Variant (с помощью которого C++Builder реализует одноименный встроенный тип Delphi). Названия столбцов для поиска разделяются в текстовой строке параметра KeyFields символом точка с запятой.
Lookup выбирает значения столбца той записи, которая удовлетворяет заданным значениям поиска. Позиция курсора не меняется. В простейшем варианте вы передаете методу название столбца для поиска, искомое значение ключа записи и возвращаемые поля этой записи. Листинг 5.3 содержит фрагмент кода, обеспечивающего поиск в таблице MyTable
первой записи, у которой в столбце "Фирма" имеется значение "Borland". Если поиск завершился успешно. Lookup возвращает в массиве типа Variant название фирмы, фамилию ее представителя и номер телефона. Если искомая запись не найдена. Lookup возвращает значение Null.
{
Variant Results;
Results = MyTable->Lookup("Фирма", "Borland",
"Фирма;Представитель;Телефон") ;
}
Листинг 5.3. Простейший пример использования метода Lookup.
Возможности метода проявляются при поиске вариантных значений полей записи в нескольких столбцах таблицы. Обобщенный синтаксис описания метода имеет следующий вид:
Variant _fastcall Lookup(const AnsiString KeyFields, const Variant sKeyValues, const AnsiString ResultFields);
Названия столбцов для поиска разделяются в текстовой строке параметра KeyFields символом точка с запятой.
С компонентой TTable также связаны следующие методы:
• GotoCurrent
синхронизирует перемещения курсора по нескольким табличным компонентам, ассоциированным с одной и той же фактической таблицей базы данных.
• First, Next, Prior, Last и MoveBy используются
для навигации по данным таблицы.
• SetKey, FindKey, FindNearest, GotoKey
и GotoNearest
используются для поиска по специфическим значениям ключей.
• Append, Insert, AppendRecord и InsertRecord добавляют новую запись к таблице. Delete вычеркивает текущую запись. Edit разрешает приложению модифицировать записи, a Post вызывает фактическое изменение содержимого базы данных.
• EditRangeStart, EditRangeEnd, SetRangeStart, SetRangeEnd, ApplyRange и SetRange устанавливают границы интервала адресов записей, возвращаемых приложению при групповом доступе.
5.2.1.3 Запросы
Компоненты таблиц являются полноправными, гибкими и легкими в использовании компонентами доступа, достаточными для многих приложении СУБД. TTable
возвращает все строки и столбцы единственной таблицы, если доступ не ограничивается установкой интервалов и фильтров. Компоненты запросов предоставляют разработчикам альтернативные возможности. TQuery обеспечивает доступ к нескольким таблицам одновременно и способна адресовать некоторое подмножество записей. Вид возвращаемого набора данных (result set) зависит от формы запроса, который может быть либо статическим, когда все параметры запроса задаются на стадии проектирования, или динамическим, когда параметры определяются во время выполнения программы.
Указанные действия записываются и реализуются на стандартизованном языке структурированных запросов SQL (Structured Query Language), принятом большинством удаленных серверов реляционных баз данных, таких как Sybase, Oracle, InterBase и SQL Server. Ha SQL можно сформулировать весьма изощренные запросы к базам данных. C++Builder передает запросы серверу, который интерпретирует их и возвращает результаты вашему приложению.
Рис. 5.10 показывает свойства компоненты запроса в окне Инспектора объектов:
Рис. 5.10. Свойства запроса. |
Active разрешает или запрещает режим просмотра "живых данных", возвращаемых запросом на этапе проектирования. Значение false устанавливается по умолчанию. DatabaseName содержит псевдоним базы данных или полный путь к ее каталогу, необходимые для разрешения запроса. RequestLive разрешает или запрещает BDE сделать попытку возвратить "живой" результирующий набор. Значение false (устанавливается по умолчанию) указывает, что результаты запроса нельзя модифицировать. Значение true гарантирует возврат результирующего набора при условии, что синтаксис команды SELECT согласуется с требованиями запрашиваемых данных. SQL используется для ввода команды SQL посредством строчного редактора списка, который открывается двойным щелчком мышью в графе значений этого свойства. Локальные и удаленные серверы баз данных обеспечивают выполнение четырех команд SQL: SELECT - для выбора существующих данных из таблиц; INSERT - для добавления |
новых данных в таблицы; UPDATE - для модификации данных таблиц; DELETE -для удаления данных из таблиц. Результаты обработки запроса возвращаются приложению клиента. Следующая процедура иллюстрирует процесс создания формы со статическим запросом к таблице
EMPLOYEE всей информации о служащих, зарплата которых превышает заданную величину:
1. Поместите компоненту TQuery на форму.
2. Установите псевдоним адресуемой базы данных сервера в свойстве DatabaseName. В примере используется псевдоним BCDEMOS локальной демонстрационный базы данных, содержащей, в частности, таблицу служащих некоторого предприятия.
3. Откройте строчный редактор списка, введите команду SQL
SELECT * FROM EMPLOYEE WHERE Salary>40000 и нажмите кнопку ОК.
4. Поместите на форму компоненту
TDataSource и установите ее свойство DataSet = Queryl.
5. Поместите на форму компоненту управления сеткой TDBGrid и установите ее свойство DataSource = DataSourcel.
6. Установите свойство Active = true для запроса Queryl с тем, чтобы сразу же отобразить живые данные в сетке (Рис. 5.11).
Рис. 5.] ]. Форма приложения со статическим запросом к таблице служащих.
Свойство SQL
имеет объектный тип TStrings и заключает список текстовых строк наподобие массива. Листинг 5.4 показывает обработчик события ButtonlClick, реализующий ввод запроса пользователем при нажатии кнопки на форме. Введенная команда SQL записывается в строчный массив (того же типа TStrings) свойства Memol->Lines компоненты редактирования TMemo. Результаты запроса можно, как и в предыдущем примере, отобразить на сетке.
void_fastcall TFormI::ButtonlClick(TObject *Sender) {
// Проверить, введена ли какая-то строка в Memol if
(strcmp(Memol->Lines->Strings[0].c_str(), "") == 0) (
MessageBox(0, "No SQL Statement Entered", "Error", MB_OK) ;
return;
} else
(
// Деактивировать предыдущий запрос, если он имел место Queryl->Close ();
// Очистить свойство SQL от предыдущего запроса Queryl->SQL->Clear () ;
// Присвоить введенный в Memol текст свойству SQL Queryl->SQL->Add(Memol->Lines->Strings[0].c_str()) ;
try
{
Queryl->0pen(); // выполнить команду SQI }
catch(EDBEngineError* dbError) .//
обработка ошибок BDE {
for (int i=0; i<dbError->ErrorCount; i++) MessageBox (0, dbError[i].Message.c_str(), "SQL Error", MB_OK) ;
} ) }
Листинг 5.4. Ввод и исполнение команды SQL.
Params позволяет специфицировать имена, типы и начальные значения параметров запроса. C++Builder дает возможность конструировать команду SQL динамического запроса с параметрами. Чтобы указать нужный параметр динамического запроса, используйте символ двоеточия перед именем этого параметра. Например, параметр номера служащего в таблице employee идентифицируется следующей командой SQL: SELECT * FROM employee WHERE EmpNo = :EmpNo.
Увидеть или поменять атрибуты выбранного параметра можно посредством диалогового редактора (Рис. 5.12), который открывается двойным щелчком мышью в графе значений этого свойства. Нажатие кнопки ОК подготавливает SQL сервер к запросу и вызывает попытку его выполнения на стадии проектирования приложения.
Рис. 5.12. Редактор параметров запроса.
Свойство Params содержи-. указатель на объект типа TParams. Поэтому изменить значение параметра во время выполнения программы можно по ^ндексу в массиве I terns объекта типа TParams: ;
Queryl->Params->Items[0]->AsInteger = 4;
или по имени параметра, посредством метода ParamByName:
Queryl->ParamFjyName ("FirstName") ->AsString = "John";
Листинг 5.5 дает законченный пример использования метода ParamByName во время исполнения программы. Параметр имени служащего FirstName идентифицируется следующей командой SQL:
SELECT * FROM EMPLOYEE WHERE FirstName = :FirstName
Заметим, что обработчик события первым делом обращается к методу подготовки запроса Prepare, посылая команду SQL
серверу. Сервер выделяет ресурсы и оптимизирует динамический запрос только однажды, до первого исполнения. Все, что необходимо теперь, - это подставлять новые значения параметров и выполнять команду запроса с помощью метода Open.
void_fastcall TFormI::ButtonlClick(TObject *Sender) {
// Деактивировать предыдущий запрос, если он имел место Queryl->Close () ;
if (!Queryl->Prepared)
Queryl->Prepare(); //
подготовить запрос // Заменить значение параметра на введенное пользователем Queryl->ParamByName("FirstName")->AsString =
Editl->Text.c_str() ;
try
{
Query 1 ->0pen(); // выполнить команду SQL }
catch(EDBEngineError* dbError) //
обработка ошибок BDE {
for (int i=0; i<dbError->ErrorCount; i++)
MessageBox(0, dbError[i].Message.c_str(), "SQL Error", MB_OK) ;
} }
Листинг 5.5. Изменение значения параметра динамического запроса.
Рис. 5.13 показывает работу предыдущего примера: пользователь вводит значение параметра FirstName и в результате выполнения запроса получает список всех служащих с указанным именем.
Рис. 5.13. Результат выполнения динамического запроса с параметром.
DataSource указывает на источник другого набора данных, отличный от источника данного запроса, из которого вам надо выбрать значения текущего поля. Объект запроса будет сравнивать имя параметра в команде SQL с именами полей дочернего набора данных. Когда имена совпадают, параметр автоматически приобретает значение соответствующего поля. Следующая процедура иллюстрирует процесс создания формы, в которой запрос к таблице заказчиков CUSTOMER соединяется с таблицей заказов ORDERS по номеру заказчика CustNo:
1. Поместите компоненты TQuery и TTable на форму и установите псевдоним
BCDEMOS в их свойствах DatabaseName.
void _fastcall TPormI::DataSource2DataChange(TObject
*Sender, TField *Field) {
// Связь запроса с таблицей ORDERS
установлена? if (Queryl->DataSource != NULL)
return; // да, возврат // Деактивировать предыдущий запрос, если он имел место Queryl->Close() ;
if (!Queryl->Prepared)
Queryl->Prepare(); //
подготовить запрос // Выбрать значение параметра запроса из поля таблицы Queryl->ParamByName("CustNo")->AsInteger =
Tablel->Fields[1]->AsInteger;
try
{
Query 1 ->0pen(); // выполнить команду SQL }
catch(EDBEngineError* dbError) //
обработка ошибок BDE (
for (int i=0; i<dbError->ErrorCount; i++)
MessageBox(0, dbError[i].Message.c_str(), "SQL Error", MB_OK) ;
} }
Листинг 5.6. Соединение запроса с таблицей по событию источника.
Для динамического формирования текста командной строки SQL во время исполнения программы удобно использовать стандартную функцию Windows sprintf. Эта функция замещает параметры форматирования (%s, %d, %n и т.д.) передаваемыми значениями, например, в результате подстановки значений параметров форматирования:
tbIName = "EMPLOYEE";
fIdName = "EmpNo";
fIdValue = 3;
sprintf(sqls, "SELECT * FROM %s WHERE %s = %d", tbIName, fIdName, fIdValue)
символьный массив sqls будет содержать следующую команду:
"SELECT * FROM EMPLOYEE WHERE EmpNo =3"
Листинг 5.7 иллюстрирует применение функции sprintf для
формирования команды SELECT динамического запроса к таблице EMPLOYEE.
Методы Clear и Add используются для занесения этой команды в свойство SQL. Поскольку подготовленный запрос использует ресурсы сервера и нет никакой гарантии, что новый запрос будет работать с данными одной и той же таблицы, C++Builder снимает готовность при любом изменении свойства SQL (т.е. устанавливает значение false свойства Prepared). При очередном исполнении запроса готовность автоматически восстанавливается.
void _fastcall TFormI::ButtonlClick(TObject *Sender)
{
char sqls[250]; // массив для хранения команды SQL
char fmts[50]; // массив для значения зарплаты
// Присвоить
fmts значение, введенное пользователем
if ( i (strcmp(SalaryEdit->Text .c_str () , " ") ==0)) strcpytfmts, SalaryEdit->Text.c_str()) ;
else
strcpy(fmts, "100000"); //
максимальная зарплата
// Деактивировать предыдущий запрос, если он имел место
Queryl->Close() ;
// Очистить свойство SQL от предыдущего запроса
Queryl->SQL->Clear() ;
// Построить команду SELECT с помощью функции sprintf
sprintf(sqls, "SELECT * FROM EMPLOYEE WHERE Salary<%s", fmts);
// Присвоить сформированную команду SELECT свойству SQL
Queryl->SOL->Add(sqls);
try
{
Query 1 ->0pen(); // выполнить команду SELECT )
catch(EDBEngineError* dbError) //
обработка ошибок BDE {
for (int i=0; i<dbError->ErrorCount; i++) MessageBox(0, dbError[i].Message.c_str(), "SQL Error",
MB_OK);, ) 1
Листинг 5.7. Формирование команды SQL с помощью функции
sprintf.
Рис. 5.15 показывает работу предыдущего примера: в результате выполнения сформированного запроса пользователь получает список всех служащих, зарплата которых (выбранная из столбца Salary таблицы) оказывается меньше введенного значения.
Рис. 5.15. Результат выполнения сформированного динамического запроса.
Методы Open и ExecSQL предназначены для передачи серверу команды SQL для исполнения. В предыдущих примерах все запросы выдавали единственную команду SELECT. Результат запроса по команде select рассматривается как набор данных, точно так же, как при работе с таблицей. Существуют другие команды
SQL, например, команда UPDATE, которая обновляет содержимое некоторой записи, но не возвращает какой бы то ни было результат. Для исполнения сервером таких запросов следует использовать метод ExecSQL вместо метода Open.
Листинг 5.8 представляет собой некоторое обобщение всех рассмотренных ранее операций с динамическими запросами и их параметрами.
Внимание: это приложение предназначено для управляемой модификации столбца Salary таблицы служащих
EMPLOYEE из демонстрационной базы данных BCDEMOS. Изменения, внесенные командой UPDATE, необратимы, поэтому перед запуском собранного приложения позаботьтесь о том, чтобы сохранить копию оригинальной таблицы. По умолчанию С-н-Builder помещает все таблицы в каталоге инсталляции \.. .\CBuilder\Examples\Data.
void_fastcall TFormI::ButtonlClick(TObject *Sender) { •
char sqls[250]; // массив для хранения команды SELECT char fmts[50]; // массив для зарплаты и надбавки int mins, adds; //
десятичные эквиваленты
// Присвоить
fmts зарплату, введенную пользователем
strcpy(fmts, SalaryEdit->Text.c_str());
mins = atoi(fmts);
// Деактивировать предыдущий запрос и очистить SQL
Queryl->Close О
;
Queryl->SQL->Clear () ;
sprintf(sqls, "SELECT * FROM EMPLOYEE WHERE Salary<%s", fmts);
// Присвоить сформированную команду SELECT свойству SQL
Queryl->SQL->Add(sqls) ;
try
{
Queryl->0pen (); // выполнить команду SELECT
} catch(EDBEngineError* dbError) //
обработка ошибок BDE
{
for (int i=0; i<dbErro-r->ErrorCount; i++) MessageBox(0, dbError[i].Message.c_str(), "SQL Error", MB_OK) ;
}
// Присвоить
fmts надбавку [в %], введенную пользователем
strcpy(fmts, AddEdit->Text.c_str());
adds = atoi(fmts);
// Деактивировать предыдущий запрос и очистить SQL Queryl->Close () ;
Queryl->SQL->Clear() ;
// Присвоить команду UPDATE свойству SQL Queryl->SQL->Add("UPDATE EMPLOYEE set Salary =
(Salary+((Salary*:adds))/100) WHERE (Salary < :mins)");
Queryl->ParamByName("mins")->AsInteger = mins;
Queryl->ParamByName("adds")->AsInteger = adds;
try
{ Queryl->ExecSQL(); //
выполнить команду UPDATE
}
catch(EDBEngineError* dbError) //
обработка ошибок BDE
{
for (int i=0; i<dbError->ErrorCount; i++) MessageBox(0, dbError[i].Message.c_str(), "SQL Error", •MB_OK) ;
) // Деактивировать предыдущий запрос и очистить SQL
Queryl->Close ();
Queryl->SQL->Clear();
// Восстановить команду SELECT Queryl->SQL->Add(sqls) ;
try
{
Queryl->0pen (); // выполнить команду SELECT }
catch(EDBEngineError* dbError) //
обработка ошибок BDE {
for (int i=0; i<dbError->ErrorCount; i++)
MessageBox(0, dbError[i].Message.c_str(), "SQL Error", MB_OK) ;
} }
Листинг 5.8. Использование метода
ExecSQL с командой UPDATE.
Рис. 5.16 показывает пример работы приложения, вызывающего повышение заданной параметром mins минимальной зарплаты на величину процентной надбавки, определяемой параметром adds. Приложение представляет пользователю список служащих, зарплата которых осталась меньше установленного минимума. Сравните последний столбец таблицы с оригинальными значениями (Рис. 5.15).
Рис. 5.16. Результат выполнения динамического запроса с модификацией данных.
Аналогично таблице, компонента запроса также инкапсулирует следующие методы:
• First, Next, Prior, Last и MoveBy используются для навигации по результатам динамического запроса.
• Append, Insert, AppendRecord и InsertRecord добавляют новую запись к таблице. Delete вычеркивает текущую запись. Edit разрешает приложению модифицировать записи, a
Post вызывает фактическое изменение содержимого базы данных.
5.2.1.4 Хранимые процедуры
Хранимая процедура представляет собой инкапсуляцию команд и данных (таблиц, индексов, областей значений) в некотором мета-объекте базы данных удаленного сервера. Компонента TStoredProc позволяет выполнить часто повторяющуюся процедуру, хранимую на сервере, и передать результаты приложению клиента.
Операции над большими группами строк в таблице базы данных, агрегатные или математические функции — подходящие кандидаты для хранимых процедур. Перемещая на мощный сервер такие повторяющиеся задачи с интенсивными вычислениями, можно заметно улучшить производительность вашего приложения. Общая загруженность сети при этом снижается, поскольку обработка происходит там же, где находятся сами данные.
Для примера рассмотрим приложение, задачей которого является вычисление единственной величины - среднеквадратичного отклонения значений по большой выборке записей. Для реализации этой функции ваше приложение должно получить по сети от сервера все значения, участвующие в вычислении, а затем произвести подсчет СКО. Цель вашего приложения - конечный результат в виде единственного числа - можно было бы достичь гораздо более эффективно с помощью хранимой на сервере процедуры, которая считывает данные "на месте" и передает именно то конечное значение, которое требовалось вашим приложением.
Рис. 5.17 показывает свойства компоненты хранимой процедуры в окне Инспектора объектов:
Рис. 5.17. Свойства хранимой процедуры. |
Active разрешает или запрещает режим просмотра "живых данных", возвращаемых процедурой на этапе проектирования. Значение false устанавливается по умолчанию. DatabaseName содержит псевдоним адресуемого сервера базы данных. StoredProcName позволяет выбрать имя нужной процедуры из выпадающего списка имен процедур, хранимых на данном сервере. ParamBindMode задает метод, по которому фактические параметры ставятся в соответствие формальным параметрам в описании хранимой процедуры. Значение pbByName (по умолчанию) определяет соответствие по именам, а значение pbByNumber - по порядку перечисления в процедуре. Params используется для ввода параметров хранимой процедуры (если таковые имеются). Редактор параметров, который открывается двойным щелчком мышью в графе значений этого свойства, обращается к серверу для выборки информации о входных и выходных параметрах. Для некоторых серверов |
полная информация, необходимая для запуска хранимой процедуры, может оказаться недоступной. В таком случае вам придется самому определить тип каждого параметра (Input, Output, Result), тип его данных и, возможно, значения входных параметров. Редактор параметров отображает параметры в том поряд-
ке, в котором они перечислены в описании данной хранимой процедуры (Рис. 5.18). Нажатие кнопки ОК подготавливает сервер и вызывает запуск хранимой процедуры на стадии проектирования приложения. Только база данных Oracle позволяет добавлять, вычеркивать или удалять все параметры в определении хранимой на сервере процедуры, производя таким образом ее перегрузку. Отвечающие за такие действия кнопки редактора параметров Add, Delete и Clear обычно запрещены, поскольку ваше приложение клиента не может модифицировать хранимые процедуры других серверов.
Рис. 5.18. Редактор параметров хранимой процедуры.
Аналогично запросу, свойство Params содержит указатель на массив объектного типа TParams. Поэтому изменить значение параметра во время выполнения программы можно по индексу в массиве Items объектов типа TParams:
StoredProcl->Params[0]->Items[1]->AsString = Editl->Text;
или по имени параметра, посредством метода ParamByName:
StoredProcl->ParamByName("ЧИСЛО_СЛУЖАЩИХ")->AsInteger++;
а затем подготовить сервер методом
Prepare и выполнить процедуру методом ЕхесРгос:
StoredProcl->Prepare () ;
StoredProcl->ExecProc ();
Хранимая процедура возвращает результаты через выходные параметры или через результирующий набор. Доступ к выходным параметрам во время выполнения программы (как и модификация значений входных параметров перед запуском хранимой процедуры) осуществляется по индексам в массиве Items объектов типа TParams:
Editl->Text = StoredProcl->Params[0]->Items[0]->AsString;
или по имени параметра, посредством метода ParamByName:
Edit2->Text =
StoredProcl->ParamByName("ЧИСЛО_СЛУЖАЩИХ")->AsInteger;
Хранимые процедуры некоторых серверов (например, Sybase) возвращают, подобно запросу, результирующий набор, для отображения которого надо использовать подходящую компоненту управления - чаще всего сетку. Процедура проектирования формы приложения с компонентой TStoredProc аналогична той, которую мы использовали для отображения результатов запроса:
1. Установите псевдоним адресуемой базы данных сервера в свойстве DatabaseName.
2. Поместите на форму компоненту
TDataSource и установите ее свойство DataSet = StoredProcl.'
3. Поместите на форму компоненту управления сеткой TDBGrid и установите ее свойство DataSource = DataSourcel.
4. Поместите компоненту TStoredProc на форму.
5. Укажите имя процедуры в свойстве
StoredProcName.
6. Установите свойство Active = true для процедуры
StoredProcl с тем, чтобы сразу же отобразить результаты в сетке.
7. Откройте редактор параметров, введите (если надо) их значения и нажмите кнопку ОК.
5.2.1.5 Соединения с базой данных и транзакции
Компонента TDatabase позволяет создавать в приложении локальный BDE псевдоним базы данных, таким образом не требуя его наличия в конфигурационном файле BDE. Этот локальный псевдоним может использоваться другими компонентами доступа. Кроме того, с помощью TDatabase можно разработать оригинальный процесс первого соединения с сервером (login), подавляя некоторые подсказки и автоматически подставляя значения необходимых параметров. Наконец, и что наиболее важно, TDatabase способна поддерживать одиночное соединение с базой данных, концентрируя в себе все необходимые операции для поддержания транзакции.
Классическим примером транзакции является перевод денежных средств банковских счетов. Такая транзакция обычно состоит в добавлении определенной суммы перевода к новому счету и вычитании этой суммы из исходящего счета. Если выполнение любой из этих операций терпит неудачу, весь трансферт считается незавершенным. SQL серверы дают возможность "прокручивать назад" команды при возникновении ошибки, не производя никаких изменений в базе данных. Именно управление транзакциями является функцией компоненты TDatabase. Как правило, транзакция содержит несколько команд, поэтому начало транзакции надо отметить методом StartTransaction. Как только транзакция началась, все ее исполняемые команды находятся во временном состоянии, до тех пор, пока один из методов Commit или Rollback () не отметят конец транзакции. Вызов Commit
фактически модифицирует данные, а вызов Rollback отменяет всякие изменения.
Рис. 5.19 показывает свойства компоненты соединения с базой данных в окне Инспектора объектов:
Рис. 5.19. Свойства соединения с базой дачных. |
AliasName содержит псевдоним существующей базы данных, определенный утилитой конфигурации BDE. Указание этого свойства является альтернативой значения DriverName. DatabaseName позволяет создать локальный псевдоним базы данных в дополнение к значениям AliasName или DriverName. DriverName содержит имя драйвера BDE при создании локального псевдонима по значению DatabaseName. Указание этого свойства является альтернативой значения AliasName. Params содержит строчный массив параметров одиночного соединения. |
дактирования EditOld, заменяется на новый, введенный в область редактирования EditNew. В этом примере компонентный объект Databasel
использовался для одиночного соединения с базой данных, поддерживающего выполнение одиночной транзакции. Этот объект необходимо каким-то образом связать с псевдонимом базы данных - или установкой соответствующих свойств компоненты, или определив параметры соединения (такие как тип драйвера, имя сервера, имя пользователя, пароль) во время выполнения программы. Сначала опробуем первый способ соединения на стадии проектирования формы приложения, установив значения свойств компоненты, как показано на Рис. 5.19.
void_fastcall TFormI::ButtonlClick(TObject *Sender) {
char sqls[250]; // массив для хранения команды SQL try
{
Databasel->StartTransaction ();
Queryl->SQL->Clear () ;
// Изменить
EditOld на EditNew в таблице CUSTOMER sprintf(sqls, "UPDATE CUSTOMER set Addrl = \"%s\" WHERE (Addrl = \"%s\")", EditNew->Text.c_str(), Edit01d->Text.c_str());
Queryl->SQL->Add(sqls) ;
Queryl->ExecSQL ();
Queryl->SQL->Clear () ;
// Изменить
EditOld на EditNew в таблице ORDERS sprintf(sqls, "UPDATE ORDERS set ShipToAddrl = \"%s\" WHERE (ShipToAddrl = \"%s\")", EditNew->Text.c_str(), Edit01d->Text.c_str()) ;
Queryl->SQL->Add(sqls) ;
Queryl->ExecSQL();
// Внести все изменения, сделанные до этого момента Databasel->Commit();
Tablel->Refresh();
Table2->Refresh() ;
}
catch(EDBEngineError* dbError) //
обработка ошибок BDE {
for (int i=0; i<dbError->ErrorCount; i++) MessageBox (0, dbError[i].Message.c_str(), "SQL Error", MB_OK) ;
Databasel->Rollback() ;
return;
} catch (Exception* exception) //
обработка исключений
{
MessageBox (0, exception->Message.c_str (), "Error", MB_OK) ;
Databasel->Rollback() ;
return;
) }
Листинг 5.9. Транзакция, реализующая смену адреса фирмы.
Конечно, показанный на Рис. 5.20 пример прохождения транзакции в соединении с локальным сервером не имеет особой практической ценности и приводится здесь только с целью проиллюстрировать работу компоненты
TDatabase.
Рис. 5.20. Результат прохождения транзакции.
Листинг 5.10 показывает, как соединиться с сервером базы данных во время выполнения программы, не создавая ее псевдонима. Ключевые моменты заключаются в том, чтобы указать DriverName и заполнить массив параметров информацией, необходимой для первого соединения (в нашем примере пользователь вводит свое имя и пароль в объекты компонент редактирования Editi и Edit2). Мы определили только те параметры соединения, которые не установлены для данного драйвера в утилите конфигурации BDE. Значение параметра SQLPASSTHRU MODE определяет один из трех возможных способов взаимодействия табличных методов Add, Append и Insert с компонентами запросов, которые соединены с той же базой данных. Использованное в примере значение NOT SHARED означает, что табличные методы и запросы используют два раздельных соединения с сервером. Сервер рассматривает их как соединения с двумя разными пользователями. До тех пор, пока транзакция не завершится, табличные методы не применяются, хотя результаты выполнения запросов могут менять содержимое базы данных, раздельно от действий активной транзакции. Два других значения SHARED NOAUTOCOMMIT и SHARED AUTOCOMMIT указывают, что табличные методы и запросы разделяют одно общее соединение с сервером. Если вам нужно включить табличные методы в транзакцию, используйте способы SHARED NOAUTOCOMMIT или NOT SHARED.
void_fasfccall TFormI::ButtonlClick(TObject *Sender) (
char name[20]; // буфер для имени пользователя char pass [20]; // буфер для пароля try
{
// Закрыть базу данных и установить параметры Databasel->Close() ;
Databasel->DriverName = "STANDARD";
Databasel->KeepConnection = true;
Databasel->LoginPrompt = false;
Databasel->Params->Add("SERVER NAME=
...\\CBuilder\\EXAMPLES\\DATA\\EMPLOYEE.DB") ;
Databasel->Params->Add("SCHEMA CACHE=8") ;
Databasel->Params->Add("OPEN MODE=READ/WRITE") ;
Databasel->Params->Add("SQLPASSTHRU MODE=NOT SHARED");
sprintffname, "USER NAME=%s", Editl->Text.c_str ());
Databasel->Params->Add(name);
sprintf(pass, "PASSWORD=%s", Edit2->Text.c_str());
Databasel->Params->Add(pass);
// Снова открыть базу данных и указанную таблицу Databasel->0pen() ;
Tablel->0pen() ;
}
catch(EDBEngineError* dbError) //
обработка ошибок BDE
{
for (int i=0; i<dbError->ErrorCount; i++) MessageBox(0, dbError[i].Message.c_str(), "SQL Error", MB_OK) ;
} )
Листинг 5.10. Соединение с сервером без псевдонима.
Конечно, первое вхождение в локальную демонстрационную базу данных BCDEMOS не требует задания имени пользователя и пароля, однако, используя такую методику, вы сможете стандартизовать процесс соединения с обычно защищенными базами данных удаленных серверов. Рис. 5.21 иллюстрирует работу прототипа подобного приложения.
Рис. 5.21. Первое соединение с "защищенной" базой дачных.
5.2.2 Компоненты управления данными
Компоненты управления служат для отображения и редактирования наборов данных на форме в удобном для пользователя виде. Свойство DataSource замыкает трехступенчатую связь любой из компонент управления с компонентагли доступа к содержимому базы данных. Вы должны связать выбранную компоненту управления с набором данных посредством компоненты источника TDataSource, который определяется значением свойства DataSource.
Устройство и работа большинства из этих интерфейсных элементов довольно очевидны, их эквиваленты знакомы нам по вкладке
Standard стандартных Windows Палитры компонент, а глава 4 содержит подробное их описание.
Остановимся на особенностях использования исключительно важной и мощной компоненты навигатора базы данных TDBNavigator. Эта компонента придает приложениям СУБД новый стандартизованный облик с панелью управления как у видеомагнитофона (Рис. 5.22).
Рчс. 5.22. Панель навигатора.
Нажимая на кнопки First, Prior, Next и Last, пользователь перемещается от записи к записи, а с помощью кнопок
Insert, Delete, Edit, Post, Cancel и Refresh про-
изводит редактирование записей. Именно навигатор обеспечивает ту степень функциональности, в которой нуждается любое современное приложение баз данных. Все кнопки навигатора видимы по умолчанию.
Рис. 5.23 показывает свойства компоненты навигатора базы данных в окне Инспектора объектов:
Рис. 5.23. Свойства навигатора базы данных. |
DataSource соединяет кнопки управления панели навигатора с компонентами доступа к наборам данных через компоненту источника. Изменяя значение этого свойства во время выполнения программы, можно использовать одну и ту же компоненту для навигации по разным таблицам. Разместите на форме две компоненты редактируемого ввода DBEditI и DBEdit2, связанные с таблицами CustomersTable и OrdersTable через источники данных CustomersSource и OrdersSource, соответственно. Когда пользователь выбирает название фирмы (поле Company в DBEditI), навигатор тоже должен соединяться с источником CustomersSource, а когда активизируется номер заказа (поле OrderNo в DBEdit2), навигатор должен переключаться на источник OrdersSource. Чтобы реализовать подобную схему работы навигатора, необходимо написать обработчик события OnEnter для одного из объектов компоненты редактирования, а затем разделить это событие с другим объектом (Листинг 5.11). VisibleButtons позволяет убрать ненужные кнопки, например, все кнопки редактирования на форме, предназначенной исключительно для просмотра данных. В приложениях клиент/сервер рекомендуется запретитькнопки First и Last, так как их нажатие может проявляться в длительном прохождении запросов. |
Во время выполнения программы можно динамически прятать или вновь показывать кнопки навигатора - в ответ на определенные действия пользователя или на изменения состояния приложения. Предположим, вы предусмотрели единый навигатор для редактирования таблицы
CustomersTable и для просмотра таблицы OrdersTable. Когда навигатор подключается ко второй таблице, желательно спрятать кнопки редактирования Insert, Delete, Edit, Post, Cancel и Refresh, а при подключении к первой таблице - снова показать их. Листинг 5.11 показывает законченный текст обработчика события OnEnter с добавлениями кода для манипуляций кнопками панели навигатора.
void_fastcall TFormI:: DBEditlEnter(TObject *Sender) {
if (Sender == DBEditI)
{
DBNavigator->DataSource = CustomersSource;
Set<TNavigateBtn, 0, 9> btnShow;
btnShow “ nbFirst“nbPrior“ nbNext“nbLast“nbInsert“ nbDelete“nbEdit“nbPost“nbCancel“nbRefresh;
DBNavigator->VisibleButtons = btnShow;
}
else
{
DBNavigator->DataSource = OrdersSource;
Set<TNavigateBtn, 0, 9> btnShow;
btnShow “ nbFirst“nbPrior“nbNext“nbLast;
DBNavigator->VisibleButtons = btnShow;
} }
Листинг 5.11. Переключение значения свойства DataSource навигатора и манипуляции со свойством VisibleButtons.
ShowHint разрешает или запрещает высвечивать подсказку с названием кнопки навигатора, когда на нее наведен курсор. Значение false
(устанавливается по умолчанию) запрещает подсказки для всех кнопок.
Hints содержит массив текстовых подсказок для каждой кнопки навигатора. Используя редактор списка, который открывается двойным щелчком мышью в графе значений этого свойства, можно дать оригинальные названия кнопкам.
Рис. 5.24 показывает работу приложения, использующего все описанные возможности навигатора.
Рис. 5.24. Разделение навигатора между двумя таблицами.
5.3 Итоги
C++Builder - это система программирования общего назначения, которая может использоваться для быстрой разработки любых приложений, в том числе одних из самых сложных — Систем Управления Базами Данных.
Визуальное построение простого интерфейса пользователя с таблицей некоторой базы данных оказывается делом нескольких часов, а иногда и нескольких минут. Не намного больше времени потребуется для организации SQL запросов к локальному или удаленному серверу базы данных, даже с переменным числом параметров или кэшируемыми обновлениями.
Итак, проектирование формы приложения СУБД в среде C++Builder в простейшем случае требует выполнения всего лишь трех или четырех действий:
1. Перенесите на форму компоненту набора данных (TTable или
TQuery) из вкладки Палитры Data Access и установите ее свойства в соответствии со специфическими требованиями выбранной базы данных.
2. Перенесите на форму компоненту источника данных[ТОа1а5оигсе и в свойстве DataSet укажите ссылку на объект набора данны^ (например. Table 1 или Query 1).
3. Перенесите на форму нужные компоненты отображения и редактирования данных из вкладки Data Controls и в их свойстве DataSource задайте источник данных (например, DataSourcel). Определите отображаемое поле набора данных в свойстве DataField. '
4. Если на предыдущем шаге вы выбрали компоненту сетки TDBGrid,
то используйте ее совместно с компонентой навигатора TDBNavigator.