Обложка 1
Открытие и сохранение 17
Титульный
Содержание
Перегрузка методов 32
Введение
1. Первый проект
Запуск С++ - Builder
Знакомство с системой С++ - Builder
Нажимаем кнопку — и «Привет!»
Окно сообщений
Сохранение проекта
Завершение работы C++ Builder
Выводы
2. Типы данных и операторы
Как снова открыть проект
Новая кнопка
Плохо или хорошо
Сохранение и проверка
События и методы
Совершенствование проекта
Еще один проект
Случайные числа и переменные
Строка
Необходимость объявления
Комментарии
Плюс или минус, умножить или разделить
Формат чисел
Выводы
3. Условия и операторы передачи управления
От 1 до 5
Структура if
Функции try и catch
Балл за баллом
«И» и «или»
Простая игра
Отгадывание числа
Компьютер считает вместе с тобой
Структура if-else
Новая игра или конец игры
Выводы
4. Управляющие структуры
На пути к миллиону
Структура while
Варианты циклов
Парад кнопок
Кнопка нажимается
Выбор из списка
С ветви на ветвь
Все в одном окне
Выводы
5. Библиотека компонентов
От точек
...до флажков
Последние штрихи
Ответы для переключателей
Кнопка «Готово»
Массивы переменных и начальные значения
Правильный выбор
Структура for
Разнообразие типов строк
Выводы
6. Проект «Камертон настроения»
Две кнопки и несколько групповых окон
Окно редактирования, панель и полоса прокрутки
Подготовка перед запуском программы
Ответ «Камертона настроения»
Полоса прокрутки
Новые ответы
Строки из списка
Приема нет
Протокол сеанса терапии
Выводы
7. Меню и системные диалоговые окна
Меню «Камертона настроения»
Два диалоговых окна
Курс «похудания»
Вывод ответов на печать
Опрос на всякий случай
На все случаи жизни
Выводы
8. Графические объекты
О точках и координатах
Первая картинка
На очереди цвет
Прямоугольники и эллипсы
А теперь текст
Кисть
Размерность
И все-таки она вертится
Новый класс
Несколько методов и конструктор
Объявление и инициализация
Появление, перемещение и исчезновение
Функция или процедура
Выводы
9. Инкапсуляция и наследование
Под одной крышей
Проект и модуль
Заголовочный файл
Получение наследства
Новорожденный
Методы Set и Calc
Тяжелое наследие
Расчет процентов
Наследник Tlmage
Директива #include
Наконец-то рисунок
Выводы
10. Собственные компоненты
TMovie умеет больше, чем Tlmage
Появление и исчезновение объектов
Кролик «оживает»
Бег и вращение
Мультфильм
ЗначокдляТМоу1е
Установка компонента
Выводы
11. Приложение MDI
Приложение для работы с несколькими документами
Подходящая основа
Работа с текстами
Работа с графическими изображениями
А как быть с таблицами
Из списка в поле таблицы
Все три компонента
Скромное приложение
Выводы
12. Виртуальные методы
TObject, TControl и кое-что еще
Круглая кнопка
Мечтасбывается
MouseDown и MouseUp
Открытые события
О старых и новых методах
Небольшое семейство монстров
Много слов, мало дел
Полиморфизм
Выводы
13. Полиморфные классы
Шоу монстров
Еже один вид полиморфизма
Деструкторы
Конструктивно или деструктивно
Множественное наследование
Кто следующий
Чистый и абстрактный
Выводы
14. Всякая всячина
Парад монстров
Компонент Timer
Перегрузка операторов
Доступ и выбор
Значение или ссылка
Шаблоны
Выводы
Заключение
Приложение А
Приложение В
Приложение С
Приложение D
Глоссарий
Алфавитный указатель
Аннотация
Выходные данные
Обложка 2
Текст
                    Ханс-Георг  Шуманн
 от  8  до  88
 Объектно-ориентированное
 программирование
 для  начинающих
 С++  для  детей
 Ханс-Георг  Шуманн


С++ для детей
Hans-Georg Schumann C++ fUr Kids Objektorientierte Programmierung furEinsteiger mitp
Ханс-Георг Шуманн с++ Издательство «Интерэксперт» Москва 2002 для детей Объектно-ориентированное программирование для начинающих
УДК 087.5:004.434 ББК 32.973.26 = 081.1 Ш 96 Перевод О. В. Варламовой Все права, в том числе и на переведенную версию, сохраняются. Ни одна часть книги в любом виде (будь то издание, фотокопия, микрофильм или иная форма) не может быть воспроизведена либо обработана, размножена или распространена без письменного на то разрешения издательства. Употребление товарных, торговых и фирменных наименований в этой книге без воспроизводства товарных знаков и торговых марок может в соответствии с законом о защите товарных знаков и торговых марок не рассматриваться как употребление товарных знаков и торговых марок, и потому объявлено свободным для использования. ISBN 3-8266-0495-4 (Г ермания) ©verlag moderne industrie, Landsberg/Lech, 2000 ISBN 5-85523-064-3 (Россия) ©Перевод на русский язык и оформление АО «Интерэксперт», 2002
Содержание Введение 11 1. Первый проект 17 ЗапускС+H-Builder 18 ЗнакомствоссистемойС+H-Builder 20 Нажимаемкнопку — и«Привет!» 21 Окно сообщений 30 Сохранение проекта 33 Завершение работы C++Builder 36 Выводы 36 2. Типыданныхиоператоры 39 Как снова открыть п роект 40 Новаякнопка 41 Плохо или хорошо? 44 Сохранение и проверка 45 События и методы 47 Совершенствование проекта 49 Ещеодинпроект 52 Случайные числа и переменные 56 Строка 59 Необходимость объявления 60 Комментарии 62 Плюс или минус, умножить или разделить 63 Формат чисел 68 Выводы 70 3. Условия и операторы передачи управления 71 От 1 до 5 72 Если...то... 74 Содержание
CrpyKTypaif 77 OyHKLiHHtryMcatch 79 Балл за баллом 82 «И» и «или» 83 Простая игра 85 Отгадываниечисла 87 Компьютер считает вместе с тобой 89 Структура if-else 91 Новая игра или конец игры? 93 Выводы 96 4. Управляющиеструктуры 99 На пути к миллиону 100 CrpyKTypawhile 101 Вариантыциклов 104 Парадкнопок 107 Кнопка нажимается... 110 Выборизсписка 111 С ветви на ветвь 115 Все в одном окне 117 Выводы 121 5. Библиотекакомпонентов 123 Отточек... 124 ...дофлажков 126 Последние штрихи 127 Ответы для переключателей 129 Кнопка «Готово» 130 Массивы переменных и начальные значения 132 Правильный выбор 134 Структура for 135 Разнообразиетиповстрок 138 Выводы 142 6. Проект«Камертоннастроения» 144 Две кнопки и несколько групповыхокон 145 Содержание
Окно редактирования, панель и полоса прокрутки 147 Подготовка перед запуском программы 150 Ответ «Камертона настроения» 151 Полоса прокрутки 153 Новые ответы 154 Строки из списка 156 Приеманет 159 Протокол сеанса терапии 160 Выводы 163 7. Менюисистемныедиалоговыеокна 165 Меню «Камертона настроения» 166 Двадиалоговыхокна 169 Открытие и сохранение 17 2 Курс «похудания» 175 Выводответовнапечать 176 Опрос на всякий случай 180 Навсеслучаижизни 182 Выводы 184 8. Графическиеобъекты 186 О точках и координатах 187 Перваякартинка 188 На очереди цвет 191 Прямоугольники и эллипсы 193 Атеперьтекст 195 Кисть 196 Размерность 198 И все-таки она вертится 200 Новый класс 203 Несколько методов и конструктор 205 Объявление и инициализация 208 Появление, перемещение и исчезновение 209 Функция или процедура 212 Выводы 214 Содержание
9. Инкапсуляцияинаследование 217 Под одной крышей 218 Проект и модуль 221 Заголовочныйфайл 224 Получение наследства 226 Новорожденный 228 Методы Set и Calc 231 Тяжелоенаследие 233 Расчет процентов 235 НаследникЛтаде 238 Директива#тсЫе 242 Наконец-то рисунок 244 Выводы 247 10. Собственныекомпоненты 249 ТМоу1еумеетбольше,чемТ1таде 250 Появление и исчезновение объектов 252 Кролик «оживает» 255 Бег и вращение 258 Мультфильм 261 ЗначокдляТМоу1е 264 Установка компонента 269 Выводы 273 11. Приложение MDI 275 Приложениедля работы с несколькими документами 276 Подходящая основа 278 Работастекстами 281 Работа с графическими изображениями 286 Акакбытьстаблицами? 290 Изспискавполетаблицы 293 Все три компонента 295 Скромное приложение 298 Выводы 299 Содержаниё
12. Виртуальныеметоды 301 TObject, TControl и кое-что еще? 302 Круглая кнопка 306 Мечтасбывается 309 MouseDown и MouseUp 311 Открытые события 314 О старых и новых методах 316 Небольшое семейство монстров 318 Много слов, мало дел 321 Полиморфизм 324 Перегрузка методов 32 7 Выводы 328 13. Полиморфныеклассы 331 Шоу монстров 332 Еже один вид полиморфизма 334 Деструкторы 337 Конструктивно или деструктивно? 340 Множественное наследование 342 Кто следующий? 345 Чистый и абстрактный 347 Выводы 349 14. Всякаявсячина 351 Парад монстров 352 KoMnoHeHTTimer 353 Перегрузкаоператоров 356 Доступ и выбор 358 Значение или ссылка 361 Шаблоны 364 Выводы 367 Заключение 368 Содержание
ПриложениеА 371 Приложение В 373 ПриложениеС 377 Приложение D 380 Глоссарий 387 Алфавитныйуказатель 399 Содержание
Введение 0 чем книга «С++ для детей»? Чтобы ответить на этот вопрос, я рас¬ скажу тебе одну историю. Жили-были два приятеля, звали их Денис и Брайан. Как-то раз забавы ради они решили разработать абсолютно новый язык программирова¬ ния, дав ему самое короткое название. — А что, если мы назовем этот язык >4? — предложил Брайан. — Сказав А, нам придется говорить и Б, — возразил Денис. — И друзья назвали новый язык С (произносится как «си»). Время шло, многие люди научились программировать на языке С. С его помощью было разработано немало крупных проектов, в том числе и операционная система Windows, на основе которой работает большин¬ ство компьютеров. Десять лет спустя парень по имени Бьерн взялся за усовершенствование языка С. Бьерн не стал называть его D. Поскольку созданный им язык появился на основе прежнего, он добавил к С два плюса, в результате чего появилось название С+ + (произносится как «си-плюс-плюс»). Почему он назвал его С++? Ты найдешь ответ на этот вопрос, прочи¬ тав книгу. Что такое программирование? Программирование — это подробное описание действий, которые дол¬ жен выполнить компьютер. Самое привлекательное в этом процессе то, что ты сам определяешь, какие действия будут произведены. Когда ты запускаешь собственную программу, компьютер выполняет придуман¬ ные тобой команды. Разумеется, он не сможет убрать комнату и прине¬ сти чашечку кофе в постель, но если ты научишься программировать, компьютер станет послушно выполнять твои команды. Что такое программирование?
Однако бывают случаи, когда компьютер отказывается следовать ука¬ заниям. Чаще всего это происходит из-за ошибок в программе. Ошибку может сделать и компьютер, и операционная система, которая подчас так ловко прячется, что программист с трудом находит ее. Надеюсь, что твое желание научиться программировать достаточно ве¬ лико. Для освоения материала книги тебе потребуется соответствую¬ щая система разработки. Что такое система разработки? Сочиняя письмо, ты записываешь его. Для создания программы необхо¬ димо напечатать ее текст. Для этого используется программа ввода тек¬ ста, называемая редактором. Компьютер не сможет прочитать и выполнить указанные в тексте ко¬ манды, если тот не переведен на язык, доступный вычислительной машине. Следовательно, необходимо воспользоваться услугами пере¬ водчика. Ты пишешь программу, а переводчик транслирует ее на язык, понятный компьютеру. Такая программа-переводчик называется компилятором. Далее программа переделывается, улучшается, снова проверяется. Для удобства существуют разные вспомогательные средства. Вся совокупность программ-помощников называется системой (или средой) разработки. Особенности языка С++ При создании языка программирования следует исходить из того, что¬ бы люди в разных странах могли работать на нем и понимать друг друга. Поскольку наибольшее распространение в разных уголках мира полу¬ чил английский язык, все языки программирования используютанглийс- кие слова. Очевидно также и то, что лучше всего пользоваться тем язы¬ ком, которому легче обучиться. Итак, ты познакомишься с языком программирования С++, который признан среди профессионалов наиболее совершенным, поскольку ни один другой язык не дает столько же возможностей. На нем написаны многие компьютерные программы и игры. В то же время С++ считается одним из самых сложных языков, поэтому было бы хорошо, если, при¬ ступая к его изучению, ты уже знал бы другой язык программирования. Введение
Однако если для первого знакомства с программированием ты выбрал С++, ты сможешь научиться им пользоваться с помощью этой книги. Чтобы стать хорошим программистом, придется съесть не один пуд соли. Нередко начинающие программисты теряют интерес к обучению, когда что-нибудь не получается, приложение делает совсем не то, чтб требу¬ ется, и ошибку найти невозможно. Они поневоле задаются вопросом о том, стоит ли им продолжать учебу, когда в мире существует множество программ. Сегодня хорошие программисты нужны всем. В большинстве предлага¬ ющих работу объявлений одно из требований к кандидатам состоит в обязательном знании С++. Работа профессиональных программистов оплачивается очень высоко, поэтому научиться языку С++ не такая уж и плохая затея. Система разработки C++Builder Для освоения материала книги тебе придется приобрести компакт-диск с полной системой разработки C++Builder. Она является широко рас¬ пространенной системой, работающей со всеми версиями Windows, на¬ чиная с Windows 95. Система разработки C++Builder
Прочитав эту книгу, ты научишься: • основам программирования на С++; • работать с системой C++Builder в среде Windows; • работать с компонентами («кирпичиками», которые помогают сэко¬ номить много времени); • секретам объектно-ориентированного программирования (ООП). В приложениях находится полезная информация не только для родите¬ лей и учителей, но и для тебя! Как работать с киигой? Книга имеет большое количество иллюстраций. Автор постарался так скомпоновать излагаемый материал, чтобы он легко усваивался. Для этого также были введены некоторые постоянно используемые симво¬ лы. Ниже дано их подробное описание. Последовательность шагов *> Эта стрелка указывает на определенное действие, которое тебе пред¬ стоит выполнить в процессе разработки того или иного проекта. Аварийные ситуации Если ты нажал не ту клавишу или запутался, ответ на вопрос о том, что делать, ты найдешь под этим вопросительным знаком. Верное решение можно также найти в приложении С. Важная информация Иногда на страницах книги встречается изображение морковки, с помощью которого отмечена наиболее важная информация. Введение
0 том, как быстрее и легче выполнить то или иное действие, сооб¬ щает волшебный кролик, раскрывающий сундучок с секретами. Задачи В конце каждой главы помещены вопросы и задачи. Эти упражнения не всегда просты, но они помогут тебе быстрее освоить учебный материал и научиться программировать. Что понадобится при чтении книги? Компакт-диск Тебе понадобится компакт-диск с полной системой разработки C++Builder (фирмы lnprise/Borland), работающей под управлением Windows. С помощью программы SETUP системаС++Вш№егустанавли- вается в любую выбранную папку, например: C:\CPP или C:\CBUILDER. Операционная система Большинство современных компьютеров работает на основе несколь¬ ких версий операционной системы Windows. C++Builder не работает под управлением версии Windows 3.1, поэтому тебе потребуется опера¬ ционная система Windows, начиная с версии Windows 95! Дискеты для упражнений Тебе понадобится, по меньшей мере, одна дискета, даже если ты будешь сохранять копии программ на жестком диске. На ней результаты работы будут в большей безо¬ пасности. Что понадобится при чтении книги?
Родители или учителя смогут помочь тебе разобраться с возник¬ шими вопросами, прочитав в конце книги приложения А и В. Хорошо ли ты знаком с компьютером? Для того чтобы программировать на С++, вовсе не обязательно в совершен¬ стве уметь работать на компьютере. Однако следует уметь запускать систему C++Builder и завершать ее работу. 06 этом я расскажу тебе в главе 1. Ниже ты найдешь пояснения к тому, как работать с мышью: Переместйтьуказатель мыши: переместитьмышь Указать мышью: переместить курсор мыши в нужное место Щелкнуть мышью: указать курсором мыши нужный объект, затем нажать ее левую кнопку Дважды щелкнуть мышью: указать курсором мыши нужный объект, затем быстро нажать два раза подряд ее левую кнопку Перетащить мышью объект; указать курсором мыщи нужный объект, и, удерживая нажатой ее левую кнопку, переместить мышь Найди следующие клавиши на клавиатуре, — тебе придется часто пользо¬ ваться ими: Ctrl, Shift, Alt, Del, Ins, Enter, Tab Знаешь ли ты: • как вставить компакт-диск в компьютер? • как вставить дискету в компьютер? • как включить принтер? Если тебе трудно ответить на поставленные вопросы, рекомендую прочесть книгу о компьютере для начинающих пользователей. Если же ты хорошо знаком с компьютером и работой Windows, давай начнем наше путешествие в страну С++. Введение
1. Первый проект Пожапуй, можно начать! Включи компьютер и дождись, когда на экране появится рабочий стол Windows. Отсюда начнется наше путешествие. В этой главе ты научишься: • запускать систему разработки C++Builder; • создавать и запускать проекты по разработке программы; • работать с формой; • работать с компонентом ВиМоп.(Кнопка); • пользоваться окном сообщений; • сохранять проект; • завершать работу C++Builder.
Запуск C++Builder Прежде чем мы приступим к программированию, надо установить сис¬ тему разработки C++Builder. Инструмент, с помощью которого мы со¬ здаем программы, работает только под управлением Windows, начиная с версии Windows 95. Выполняемая программой SETUP установка подробно описывается в приложении В. Если тебе не удастся самостоятельно установить C++Builder, обратись за чьей-нибудь помощью. Чтобы запустить C++Builder, выполни следующие действия: r> Нажми кнопку Пуск и в раскрывшемся меню укажи команду Прогрдммы. ^ В раскрывшихся подменю последовательно выбери команды C++BuiLDER И C++BUILDER. Или: •^ Нажми кнопку Пуск и укажи команду Выполнить. 1. Первый проект
*^ Введи текст bcb.exe и нажми кнопку ОК. Если в результате програм¬ ма не запустится, введи путь, последовательно указав все папки. На¬ пример: c:\cpp\bin\bcb.exe или c:\programs\borland\cbuilder\bin\bcb.exe или найди файл bcb.exe, нажав кнопку Овзор. В зависимости от того, на каком компьютере ты работаешь, на загрузку C++Builder требуется разное время. Если на экране по¬ явится изображенное на рисунке окно сообщений, нажми кнопку YES (Дд). Вас приветствует система разработки C++Builder В этом случае тебе придется еще немного подождать, поскольку C++Builder станет выполнять некоторые дополнительные дей¬ ствия. Новые файлы займут добавочный объем памяти, но тебе будет удобнее работать с системой C++Builder. По окончании создания вспомогательных файлов на экране появляется следу¬ ющее сообщение: Нажми кнопку OK для завершения установки. O*Bufldet Запуск C++Builder
Знакомство с системой C++Builder Вид открывающихся на экране окон показан на следующем рисунке: Окна системы разработки C++Builder Сразу трудно разобраться, что к чему, — окна заслоняют друг друга. В самом верху находится строка меню, под ней располагается множество значков, которые в процессе работы щелкают мышью. Этими четырьмя меню, изображенными на рисунке, ты будешь пользо¬ ваться наиболее часто. Они имеют следующее назначение: • меню FiLE (Фдйл) позволяет сохранять, загружать (открывать), распе¬ чатывать и создавать файлы, а также завершать работу C++Builder; • меню EDiT (ПрдвкА) позволяет редактировать тексты программ и но¬ вые модули программы, а также возвращаться к предыдущим шагам и восстанавливать их; • меню RuN (Здпуск) позволяет запускать проекты; • меню HELP (СпРАВкд) предоставляет справочную информацию о системе. t. Первый проект
Некоторые наиболее часто используемые команды объединены в так называемом всплывающем меню. Когда ты нажимаешь пра¬ вую кнопку мыши, оно появляется на экране в том месте, где в момент нажатия находился ее указатель. На экране не видно окна редактирования. Основным рабочим про¬ странством является окно, похожее на рамку для выкладывания мо¬ заики. |^Form1 Это так называемое окно формы, или форма, где мы будем создавать интерфейс нашей программы. Необходимые составляющие расположе¬ ны сверху на панели пиктограмм в палитре компонентов. С помощью элементов этой палитры, или компонентов, создаются, а затем включаются в программу кнопки, диалоговые окна и меню. Уже в первой программе на языке С++ мы воспользуемся палитрой компонентов. Нажимаем кнопку - и «Привет!» Наверное, ты знаешь, как работать с меню и диалоговыми окнами. По¬ пробуем создать небольшой проект, который будет запускаться нажа¬ тием одной кнопки. Для этого нам необходимы компоненты, которые в С++ называются Button (Кнопка). Созданные с их помощью кнопки активируются мышью. Нажимаем кнопку — и «Привет!»
^ Пройдись указателем мыши по пиктограммам, обозначающим компо¬ ненты. Ты увидишь окошки с названием компонента. Добравшись до значка Button, щелкни его мышью. ^ Переведи указатель мыши на форму. ■+Удерживая нажатой левую кнопку мыши, перемести ее указатель по ди¬ агонали вниз. Рамка для кнопки ■+Отпусти кнопку мыши, когда компонент в форме приобретет требуе¬ мый размер. Кнопка почти готова 1. Первый проект
Оставим кнопку в центре формы. Чтобы ускорить процесс работы, выполни следующие действия. • Нажми правую кнопку мыши, предварительно рас¬ положив ее указатель на компоненте. Откроется меню. Его называют контекстно-ориен¬ тированным, так как в нем отображаются только за¬ писи, имеющие отношение к текущему объекту или к текущему положению. Укажи в этом меню команду AuGN (Выровнять). Небольшое диалоговое окно ото¬ бражает возможности, которые предлагает С++Вш№егдля выравни¬ вания положения объекта. В списках HORIZONTAL (ПО ГОРИЗОНТАЛИ) И VERTICAL (По вЕРтикдли) щелкни мышью пере¬ ключатели CENTER IN WINDOW (ПОСЕРЕ- динЕ ОКНА). При этом в поле переклю¬ чателей появляются точки. Теперь у тебя есть кнопка с надписью BuTTONl. Если тебе не нравится надпись, измени ее, войдя в окно инспектора Object Inspector (инспек¬ тор объектов). В нем находятся все свойства (properties), характеризу¬ ющие объект или компонент: размер, расположение, имя. Нажимаем кнопку — и «Привет!»
Что такое объект (object)? Все вещи и живые существа, которые нас повсюду окружают — дома, деревья, машины, люди, — называются объектами. И в языке С++ имеются объекты. Например, форма, создаваемая тобой кнопка или все компоненты — объекты. Так же, как и в обычной жизни, в языке С++ встречаются объекты одного типа. В С++ их относят к одному классу (class), или типу объектов (object type). Кроме того, объект в С++ называют эк¬ земпляром (instance). Следуя этой логике, ты являешься экземп¬ ляром класса «Человек». Обычно в С++ имя класса начинается с прописной буквы «Т» (со¬ кращение от слова «тип»), например: TForm или TButton. При при¬ своении имени экземпляру «Т» опускается, например: Form или Button. ->Щелкни мышью текст в текстовом поле свойства CAPTioN (Нддпись) и удали его. Затем введи текст Нд- ЖМИ МЕНЯ! В результате этот текст появляется в виде надписи на кнопке. Кнопка 1. Первый проект
Ты не можешь найти поля, где вводятся свойства? Вместо них на экране странные имена, начинающиеся на On? Это значит, что ты находишься не на той странице инспектора объектов. Щелкни мышью закладку PROPERTiES, расположен¬ ную прямо под надписью Виттоы1:ТВиттоы. Попробуем сделать так, чтобы после нажатия кнопки программа посы¬ лала «привет». ^Дважды щелкни мышью кнопку с надписью Нджми МЕНя! На экране появляется новое окно, являющееся окном редактора кода C++Builder. До сих пор оно было спрятано под окном формы. В окне указано название метода, отвечающего за действия компьютера после нажатия кнопки Нджми МЕНя: void fastcall TForml::ButtonlClick(TObject *Sender) Постарайся понять значение стоящих в середине строки и выделенных полужирным шрифтом слов: TForm — класс, к которому принадлежит форма. Цифра 1 означает, что классов может быть создано много. Нажимаем кнопку — и «Привет!»
Forml — «форма для выпечки», в которую помещается программа. На¬ звание метода ButtonlClick в переводе означает «нажатие кнопки №1». Двойное двоеточие (::) в языке С++ связывает класс с методом. Этот символ называют оператором разрешения области действия. Как тебе уже известно, объекты обладают свойствами. Однако объект не может совершать действий, если ему не приписан метод. Например, машина*не только обладает определенными свойства¬ ми, но и может совершать некоторые действия: двигаться, увели¬ чивать скорость или тормозить, разворачиваться, стартовать и ос¬ танавливаться. Описание действий в С++ называется методом. Для того чтобы кнопка реагировала на щелчок мыши, необходимо оп¬ ределить хотя бы один метод. Он определяется и для всех остальных компонентов, а также для большинства объектов. Бывает, некоторым объектам приписываются десять-двадцать методов (а то и больше). Между фигурными скобками ({}) нам следует вставить инструкцию для C++Builder, описывающую то, что должно происходить после нажатия клавиши. void fastcall TForml::ButtonlClick(TObject *Sender) { Application->MessageBox ("Привет!", '"', 0); } ■+Дополни метод одной строкой. Стрелка составлена из знака «минус» (—) и знака «больше» (>). Не забудь поставить точку с запятой (;)! С прописной или строчной буквы? В отличие от таких языков программирования, как Паскаль и Бей¬ сик, в С++ важно, с какой буквы начинается слово: с прописной или строчной. Необходимо строго следовать тому, что предлагает С++. При вводе текста следи за прописными буквами, появляю¬ щимися между строчными.“Нельзя вместо MessageBox писать messagebox или Messagebox. Пока не спрашивай, что означает команда Application->MessageBox, — давай, сначала запустим программу. 1. Первый проект
~^ Для запуска программы в меню RuN укажи команду RUN. Еще быстрее можно запустить программу, нажав клавишу F9 или кнопку с зеленой стрелкой, расположенную на панели инструмен- jJ тов в левом верхнем углу экрана. На этот раз на экране появляется форма (без «сетки») с надписью, пред¬ лагающей нажать кнопку. Твоя программа не запускает¬ ся? Вместо этого в нижней час¬ ти экрана редактора кода появ¬ ляется следующее сообщение об ошибке: Нажимаем кнопку — и «Привет!»
Место, где, по мнению C++Builder, допущена ошибка, выделено красным. Возможно, сделанаопечатка. В нашем случае вместо тек¬ ста MessageBox введено Messagebox, то есть написание слова было нарушено. Часто забывают также поставить точку с запятой или запятую, скоб¬ ки или кавычки. Исправь ошибки и запусти программу еще раз. После нажатия кнопки появляется еще одно небольшое окно. Изображение невелико, однако слово «Привет!» можно лрочитать.’Та- кие окна называют окнами сообщений. Процесс создания программы отличается от ее выполнения не толь¬ ко тем, что в одном случае форма покрыта растровой сеткой, а в другом — нет. • При создании программы можно менять размер формы и кноп¬ ки. Кроме того, с помощью инспектора объектов можно при¬ сваивать компонентам другие свойства, а также вводить текст программы в окне редактора кода. Подобные действия назы¬ ваются также процессом разработки. 1. Первый проект ^ Нажми кнопку ОК. Окно сообщений закрывается.
При выполнении программы компоненты начинают работать. При нажатии кнопки активируется действие, например, откры¬ вается окно сообщений с приветствием. Работа программы дол¬ жна завершаться соответствующим образом: щелчком значка мышью или нажатием функциональной клавиши. Этап выполне¬ ния называют также процессом работы. Ты случайно открыл слишком много окон? Исчез¬ ла форма? Или окно редактора кода? Исчезнув¬ шие окна можно вернуть следующим образом: • в меню ViEW (Вид) укажи команду FoRMs (Формы); Нажимаем кнопку — и «Привет!» Заверши работу программы, нажав в форме кнопку с кре¬ стиком в правом верхнем углу или комбинацию клавиш Alt+F4.
• щелкни мышью в списке форм имя открыва¬ емой формы; • нажми кнопку ОК. Снова открывается фор¬ ма; • для того чтобы открыть окно редактора кода, в меню ViEW выбери команду UNiTS (Модули). Окно сообщений Рассмотрим следующую строку программы метода ButtonClick: Application->MessageBox ("Привет!", "'', 0) ; Команда вызывает метод под названием MessageBox. Application — это объект, к которому относится данный метод. Под объектом Application подразумевается составляемая тобой программа. В операционной сис¬ теме Windows программу называют также приложением. Стрелка (->) в С++ объединяет объект и метод. Этот символ называют оператором доступа. В скобках указаны параметры, или аргументы: ("Привет!”, "", 0) 1. Первый проект
Первый параметр метода MessageBox — текст, который появляется в ка¬ честве сообщения. В нашем примере это текст «Привет!». Затем указа¬ но название окна. Поскольку мы ничего не написали в кавычках, у окна нет названия. Кавычки, в которых ничего не заключено, называют пус¬ той строкой. Последний параметр состоит из одного числа. В таблице представлена расшифровка значений этого параметра. Поскольку данному параметру я присвоил нулевое значение, в окне по¬ явилась только одна кнопка ОК. Для удобства использования проекта введи в программу следующую команду: void fastcall TForml::ButtonlClick(TObject *Sender) { Application->i4essageBox ("Привет!", "Большой привет:'', 1+64); ^ Измени в редакторе кода текст программы. Войди в окно редактора кода, дважды щелкнув мышью кнопку Нджми МЕНя. Может случиться так, что в тексте появится красная полоса, а в на¬ чале строки — небольшой символ в кружке.
? Это так называемая точка прерывания. Таким способом отмеча¬ ется место, где прерывается выполнение программы. Точки пре¬ рывания используют в том случае, когда возникает предположе¬ ние, что в определенном месте программы допущена ошибка (см. приложение С). Нам эти отметки не нужны. Как их убрать? Чтобы красная полоса исчезла с экрана, щелкни мышью кружок. Запусти программу, указав в меню RuN команду RuN, или нажми клави¬ шу F9. Нажми кнопку Нджми МЕНЯ. Для завершения работы программы нажми любую кнопку в окне сооб¬ щений, а затем в правом верхнем углу формы кнопку с изображением крестика, или нажми дважды комбинацию клавиш Alt+F4. Попробуй запустить программу с разными значениями последнего па¬ раметра функции MessageBox и понаблюдай, какие символы и кнопки по¬ являются в окне сообщений. Поменяй также и первые два параметра. Если ты любопытен и хочешь узнать, какую информацию предлага¬ ет справочная система С++ по указанному слову или по некоторой теме, нажми клавишу F1 или вызови справку с помощью меню HELP. • Если щелкнуть мышью форму или компонент, а затем нажать клавишу F1, появятся сведения о выбранном объекте. • Если в окне редактора кода подвести курсор к какому-либо вхо¬ дящему в словарный запас языка С++ слову и нажать клавишу F1, появятся относящиеся к нему сведения. 1. Первый проект
Сохранение проекта Наш первый проект небольшой, однако стоит сохранить все, что мы сделали. ^ В меню FiLE укажи команду SAVE Рмшст As (Co- ХРАНИТЬ ПРОЕКТ КАК). Сохранение проекта • Если курсор находится в свободном месте окна, то нажатие кла¬ виши F1 позволит тебе обратиться к справочной системе С++. Теперь ты умеешь ориентироваться в справочной системе и зна¬ ешь, какую информацию она может тебе предоставить. Выйти из окна справки можно, нажав клавишу Esc или кнопку с изображени¬ ем крестика в правом верхнем углу окна.
Открывается окно сообщений. В нем выделяется папка, в которой по¬ мещена система C++Builder. В этой папке можно сохранять и проекты. Для копирования файлов создана вложенная папка Тест. Ты можешь ука¬ зать другую папку для сохранения проектов. Для этого выбери в поле спис¬ ка ПдпкА соответствующее имя и дважды щелкни его мышью. Выбор папки *^Если ты не хочешь пользоваться предложенным именем файла UNIT1.CPP, установи курсрр в текстовое поле Имя ФАйлд и введи дру¬ гое название, например HALL01. Для сохранения созданных проектов на дискете вставь ее в дисковод и введи следующий текст: A:HALL01 ■^ Нажми кнопку ОК. 1. Первый проект
Достаточно ввести только имя файла HALL01 без расширения, так как C++Builder автоматически добавляет точку и расширение CPP. В файлах с этим расширением (сокращенное название C-PlusPlus) хранятся тексты программы. В данном случае не имеет значения, какие буквы — прописные или строчные, — используются для обо¬ значения файла. Процедура сохранения еще не закончена. Как только ты подтвердишь выбор имени, нажав кнопку OK, на экране появляется следующее окно: Необходимо сохранить сам проект, так как он не сохраняется в файле тек¬ ста программы. Используй предложенное название проекта PROJECT1.MAK или выбери имя по своему вкусу. ^ Подтверди введенное имя проекта, нажав кнопку ОК. Теперь ты знаешь, что для сохранения созданного в C++Builder проекта используется несколько файлов. • Один из них содержит текст программы, называемый исход¬ ным текстом. Он сохраняется в файле с расширением CPP (для С++). Сохранение проекта
• Главный файл проекта имеет расширение МАК (сокращение от английского слова make — делать). Он сохраняется под другим именем. • Файл с данными о форме имеет расширение DFM — Delphi ForMular (формат был заимствован у системы разработки Delphi, также созданной фирмой Borland). Этот файл создается C++Builder автоматически и получает то же имя, что и файл с исходным текстом. • Кроме того, C++Builder управляет некоторыми дополнитель¬ ными файлами, которые тоже создаются автоматически. Завершение работы C++Builder Твой самый первый проект сохранен на дискете или на жестком диске. Теперь самое время немного отдохнуть. Для того чтобы выйти из при¬ ложения C++Builder, выполни следующие действия: Или одновременно нажми клавиши Alt+F4. Выводы Создав один проект, ты, конечно, не стал членом гильдии программис¬ тов С++» но сделал важный первый шаг. Посмотрим, чему мы научи¬ лись. Сначала повторим новые слова из лексикона языка С++: 1. Первый проект ■+ В меню FiLE укажи команду Ехи (Выход).
Запуск системы C++Builder Дважды щелкни мышью значок C++Builder или укажи следующую последовательность команд в меню Пуск: ПРОГРАММЫ/C++BUILDER/ C++BuitDER Щелкни мышью компонент в палитре компонентов Дважды щелкни компонент мышью Выбор компонента Редактирование исходного текста метода Определение свойства объекта Сохранение проекта под новым именем Запуск программы Завершение работы программы Вызов справочной системы Завершение работы системы C++Builder Щелкни мышью поле, находящееся в инспекторе объектов рядом с наименованием свойства В меню FiLE укажи команду SAVE PROJECT As В меню Пуск укажи команду Выполнить Нажми кнопку с изображением крестика или одновременно нажми клавиши Alt и F4 Щелкни мышью значок HELP или нажми клавишу F1 В меню FiLE укажи команду Exrr Исчезнувшие окна возвращаются на экран следующим образом: Форма Окно редактирования В меню ViEw укажи команду FoRMS В меню ViEW укажи команду UNiTS Компоненты выравниваются таким образом: Компонент Щелкни компонент правой кнопкой мыши Меню Укажи команду AuGN Диалоговое окно Выбери параметры, затем нажми кнопку OK Несколько понятий, с которыми мы познакомились, работая в C++Builder: Form Форма (тип TForrn), в которой размещаются все компоненты программы. Сама форма является главным компонентом 3utton Кнопка (тип TButton), которую щелкают мышью ButtonClick Эта функция активируется щелчком кнопки мышью Выводы
Caption Свойство, которым обладают многие компоненты, — надпись MessageBox Окно сообщения с собственным именем, различными кнопками и символами Application Приложение, главный модуль программы :: Оператор разрешения области действия -> и оператор-указатель для соединения класса и объекта сфункциями Работая над проектом, мы используем следующие три окна: • окно формы, или форму, где создаются компоненты (например, кнопки); • окно редактора кода, в которое вводится исходный текст программы (например, описание метода); • окно инспектора объектов, в котором определяются свойства компонентов. Весь проект сохраняется, по меньшей мере, в следующих трех файлах: Данные проекта Расширение МАК Данные формы Расширение DFM Текст программы Расширение CPP Вопросы... 1. Как завершить работу программы в системе C++Builder? 2. Почему при работе с C++Builder работают не с программами, а с проектами? 3. Чем отличается форма (окно формы) от окна редактора кода? 1. Первый проект
2. Типы данных и операторы Итак, в предыдущей главе мы создали небольшой проект — програм¬ му, обслуживающую только одну кнопку. Давай, попробуем расши¬ рить возможности нашей программы и добавим несколько новых ком¬ понентов. В этой главе мы также поговорим об арифметических действиях. Ты уз¬ наешь, как С++ работает с числами и символами. В этой главе ты узнаешь: • как (снова) открыть уже созданный проект; • как создать новый проект; • что такое компонент Label (Метка); • кое-что о событиях и методах; • как в языке С++ описываются переменные; • новые типы данных и операторы; • как в языке С++ создаются случайные числа; • как числа преобразуются в строки.
Как снова открыть проект Для разработки следующего проекта мы можем использовать уже со¬ зданный в предыдущей главе. В нем определена одна кнопка. Мы изме¬ ним ее и добавим еще одну. • Если система С++ Builder была закрыта, снова запусти ее. Tы уже забыл, как она запускается? Тогда выполни следующие дей¬ ствия: • в меню Пуск укажи команду Прогрлммы. • укажи последовательно команды C++BuiLDER и C++BuiLDER. Если тебе не удастся запустить систему таким образом, попробуй сделать это, выбрав в меню Пуск команду Выполнить и затем на¬ жав кнопку Овзор. А сам проект мы «берем» с жесткого диска или дискеты следующим образом. Открывается диалоговое окно, в котором представлена рабочая папка системы. Если проект PROJECT1.MAK был сохранен на жестком диске, то это имя ты найдешь в текстовом поле ПдпкА. Поскольку данный проект был первым, в списке имеется только одна запись. *^Дважды щелкнув мышью название PROJECT1.MAK, введи его в текстовое поле Имя ФАйлд. 2. Типы данных и операторы ► В меню FiLE выбери команду OPEN PROJECT (Открыть про- ЕКТ).
Это твой проект Если ты сохранил свой проект на дискете, то в вызванном списке его не будет. 4 Вставь дискету в дисковод и введи в поле Имя ФАйлд следующий текст: A:PROJECT1.MAK 4 Нажми кнопку Открыть. На экране появится окно редактора кода, а поверх него — форма с со¬ зданным в предыдущей главе проектом. Новая кнопка H> Подведи курсор мыши к нижнему правому углу кнопки с надписью Нд- жми МЕня так, чтобы курсор превратился в двунаправленную стрелку. Изменение размера кнопки 4 Удерживая нажатой левую кнопку мыши, перемести ее влево так, чтобы прямоугольник начал уменьшаться. Новая кнопка
H> Когда кнопка на форме приобретет форму квадрата, отпусти кнопку мыши. ^ Щелкни квадрат мышью и, удерживая нажатой ее кнопку, перемести его немного влево. Перемещаем кнопку Щелкни мышью значок Button на панели инструментов. ■+ Создай новую кнопку на форме. Вторая кнопка 2. Типы данных и операторы
В инспекторе объектов определяются размеры всех компонентов: • Высота определяется свойством Height (Высота). • Ширина определяется свойством Width (Ширина). Инспектор объектов также позволяет задать точное расположе¬ ние компонента. • Расстояние от левой границы формы задается свойством Left (Слева). • Расстояние от верхней границы формы задается свойством Тор (Сверху). Эти четыре свойства полностью определяют расположение и раз¬ меры компонента, так как значение координаты правой границы компонента (Right) однозначно определяется суммой значений свойств Left и Width, а значение координаты нижней границы (Bottom) — суммой Тор и Height. Прежде чем сделать надпись на второй кнопке, необходимо определить, ка¬ кие же действия будет выполнять программа Пусть при нажатии новой кноп¬ ки она не просто отвечает «Привет!», а делает что-нибудь более сложное. Но для того чтобы можно было понять, какие функции выполняет про¬ грамма, необходимо их где-то указать. В этом нам поможет полоса за¬ головка формы. Новая кнопка
4 Установи курсор мыши на рабочей области формы, затем введи в тек¬ стовое поле свойства Слртюы фразу ПривЕТ, кдк ДЕЛА? Новое название формы Плохо или хорошо? Итак, давай изменим надписи обеих кнопок. Мы воспользуемся для это¬ го двумя наиболее часто встречающимися ответами на вопрос «Как дела?». Если мы напишем на кнопках Хорошо и Плохо, надписи превра¬ тятся в ответы. 4 Щелкни мышью левую кнопку и в текстовое поле инспектора объектов CAPTION введи СЛОВО ХОРОШО. ^ Нажми правую кнопку мыши и в текстовое поле инспектора объектов CAPTION введи слово Плохо. Итак, кнопки готовы. Для того чтобы при их нажатии появлялись соот¬ ветствующие ответы, для кнопок необходимо определить методы ButtonClick. На этот раз необходимо описать два метода. 2. Типы данных и операторы
4 Щелкни дважды квадрат с надписью Хорошо. На экране снова появляет¬ ся окно редактора кода. Измени текст программы TForml: :ButtonlClick следующим образом: void fastcall TForml: :ButtonlClick(TObject *Sender) { Application->MessageBox ("Радостнослышать", '"', 4+48); } 4 Щелкни мышью рабочую область формы. *^Дважды щелкни мышью квадрат с надписью Плохо. Поменяй в окне ре¬ дактора кода текст программы метода TForml:: Button2Cl ick следующим образом: void fastcall TForml::Button2Click(T0bject *Sender) { Application->MessageBox ("Очень жаль!", ''", 1+32) ; } Сохранение и проверка Прежде чем запустить новый проект, его необходимо сохранить под новым именем. Тексту программы также следует присвоить новое имя: ■^ В меню FiLE укажи команду SAVE As (Сохрднить кдк). Сохранение и проверка
т^ В диалоговом окне SAVE HALLo2 As введи в тектовое поле Имя ФАйлд следующее название: HALL02.CPP. ^ В меню FiLE укажи команду SAVE PROJECT As. 4 В текстовое поле Имя ФАйлд диалогового окна SAVE PR0JECT1 As введи имя файла PROJECT2.MAK. А теперь посмотрим, что же умеет делать наша программа. 2. Типы данных и операторы
■+ Нажми кнопку RuN основного меню C++Builder и укажи команду RuN. Поскольку на экране одновременно не могут появиться два диалоговых окна, этот рисунок является монтажом. Однако при выполнении програм¬ мы ты увидишь, что нажатые кнопки реагируют следующим образом: Кнопка Диалоговое окно Хорошо Радостно слышать! Плохо Оченьжаль! События и методы Созданный нами проект работает следующим образом: • если нажать кнопку с надписью Хорошо, появляется сообщение: «Ра¬ достно слышать!»; • если нажать кнопку с надписью Плохо, появляется сообщение: «Очень жапь!». Такое действие, как например щелчок мышью, в системе Windows на¬ зывается событием. Нажатие кнопки или перемещение окна тоже явля¬ ются событиями. Обобщая, можно сказать, что все происходящие на экране Windows действия являются событиями. Существуют также не¬ видимые, совершающиеся незаметно для нас события. Но здесь речь пойдет не о них. Оба происходящие в придуманной нами игре события называются в C++Builder OnClick, то есть событиями, которые совершаются после щелчка мышью. События и методы
Результат = щелчок кнопки Действие = выполнение метода Событие активирует метод, которому присвоено соответствующее имя. В нашем примере используются методы 3uttonlClick и Button2Click. В результате нажатия первой или второй кнопки начинает выполняться то или иное действие, то есть осуществляется один из двух методов. Их нельзя использовать отдельно, так как они связаны с определенной формой. Методы активируются только щелчком мыши в рабочей обла¬ сти определенной формы. Поэтому их полные имена выглядят следую- щимобразом: ?Forml: :ButtonlClickHTForml: :3utton2Click. Форма тоже является компонентом, точнее главным компонентом про¬ граммы. Если кнопка имеет тип TButton, то тип формы обозначается TForm. В инспекторе объектов отображаются связи меж¬ ду событием и методом. Если щелкнуть мышью закладку EvENTS (Совытия), появится список всех событий, которые могут про¬ исходить с активным компонентом, а также все свя¬ занные с ними методы. Дважды щелкнув мышью одно из имен методов, можно войти в окно редак¬ тора кода и выбрать один из методов. 2. Типы данных и операторы
Совершенствование проекта Вопрос «Привет, как дела?» можно разместить непосредственно в ра¬ бочей области формы. Для этого нам необходимо использовать еще один элемент палитры компонентов: ■+ Найди значок Label и щелкни его мышью. Label — это метка для отображения текста. Тип данного компонента обозначается как TLabel. Именно он нужен нам для размещения при¬ ветствия в рабочей области формы. *> Размести рамку для метки над двумя кнопками. Рамка компонента Label Не волнуйся, если после того как ты отпустишь кнопку мыши, рамка уменьшится. Если текст окажется длиннее заданного окна, поверхность для ввода текста соответственно увеличится. Object lnspectoi Надпись сразу же появляется на форме, но расположена она не очень удачно и выглядит несколько бледновато. Это легко поправить. Совершенствование проекта ► Введи в текстовое поле Сдртюы предложение При- BET, КАКДЕЛА?
^ Нажми кнопку с многоточием, расположенную в тек¬ стовом поле свойства FoNT (Шриот). Открывается диалоговое окно ШриФТ, в котором вы¬ бирается тип шрифта. Однако прежде всего зададим ему размер 12 или 14, чтобы вопрос был заметен. т> Выбери шрифт и его размер, затем нажми кнопку ОК. ^ Измени также размер шрифта надписей двух кнопок. При желании можно выбрать новый шрифт. Теперь мы займемся расположением метки на форме. ^ Смести метку таким образом, чтобы вопрос располагался над двумя кнопками посередине. Как дела? 2. Типы данных и операторы
Указанное в заголовке формы имя несет избыточную информацию. Его можно либо удалить, либо изменить. *+ Щелкни мышью форму и удали в инспекторе объектов текст в поле свойства CAPTiON или введи новый, напри¬ мер ПРИВЕТСТВИЕ. Object lnspec<oi Итак, форма получила новое имя. Имя программы Запусти программу в последний раз. Но сначала еще раз сохрани проект: Все имеющие отношение к проекту файлы сохраняются под присвоен¬ ными им ранее именами. ^ Нажми кнопку RuN и выбери команду RuN. Совершенствование проекта ■^ В меню FiLE укажи команду SAVE Au. (Сохрднить всЕ).
Программа выполняет те же действия, что и прежде, может быть, только выглядит несколько элегантнее. Для окрытия и сохранения проекта можнотакже пользоваться дву¬ мя значками, расположенными на панели инструментов под поло¬ сой главного меню FiLE. Если щелкнуть мышью этот значок, все относящиеся к данному проекту файлы сохранятся на жестком диске или дискете: Еще один проект Отложим теперь проект «Привет!» и займемся новым. Теперь твой ком¬ пьютер должен доказать, что он способен выполнять действия обычно¬ го калькулятора. А ты будешь делать то, чем обычно занимается учи¬ тель по математике: задавать примеры и проверять их выполнение, с той лишь разницей, что тебе придется экзаменовать компьютер. Но сначала создадим проект. Прежде всего откроем новую форму. Мы создадим также все осталь¬ ные составные элементы проекта. В меню FiLE укажи команду №w AppLicATioN (НовоЕ при- ЛОЖЕНИЕ). 2. Типы данных и операторы Нажав эту кнопку, можно открыть проект, записанный на жестком диске или на дискете:
Итак, необходимо создать три компонента Label и четыре компонента Button. 0 том, что они будут делать, ты узнаешь позже. На рисунке по¬ казано, как компоненты должны расположиться на форме: Кнопочный квартет и трио меток *> Щелкни мышью значок LABEL и начерти на форме рамку для метки. По¬ втори это действие два раза. + Щелкни мышью значок Виттоы и начерти наформе кнопку. Повтори это действие три раза. Тебе нужна помощь? • Новые компоненты создаются с помощью палитры компонен¬ тов (значки BuTTON или LABEL): щелкни мышью соответствующий значок и начерти на форме кнопку или рамку для метки. • Размеры компонентов изменяются следующим образом: пере¬ мещай курсор мыши, удерживая нажатой ее правую кнопку, от правого нижнего угла прямоугольника. • Чтобы изменить положение компонента, щелкни его мышью и перемести курсор, удерживая нажатой ее правую кнопку, в то место, где должен разместиться компонент. • Размещаемая на компоненте надпись вводится в инспекторе объектов в текстовое поле свойства CAPTiON. • В этой же строке изменяются и свойства компонента (например, положение или размер).
Программе C++Builder можно поручить даже задачу по выравни¬ ванию компонентов. • Начерти с помощью мыши рамку, охватывающую все кнопки. Таким образом выделяются кнопки. • Щелкни правой кнопкой мыши выделенную область. • Откроется контекстное меню, содержание которого зависит от текущей ситуации. Укажи в нем команду AuGN (Выровнять). • В диалоговом окне ALIGNMENT (ВЫРАВНИВАНИЕ) В списке HORIZONTAL щелкни мышью переключатель SPACE Еоилшг (РдвныЕ интЕРВАЛы), а В списке VERTICAL — кнопку CENTER IN WINDOW (РАЗМЕСТИТЬ ПО ЦЕНТРУ OKHA). • Щелкни мышью форму и введи в инспекторе объектов в поле CAPTION ИМЯ АРИФМЕТИЧЕСКИЕДЕЙСТВИЯ. Теперь ты понимаешь, что было задумано? Поскольку существует четы¬ ре основных арифметических действия, для выполнения вычислений нам потребуются четыре кнопки. Две метки предназначаются для двух уча¬ ствующих в этих операциях чисел. Третья из них служит для отображе¬ ния указания. 2. Типы данных и операторы
Мы создадим надписи тояько для четырех кнопок и верхней метки в соответствии с указанными в таблице размерами шрифта и заголовками: 4 Щелкай по очереди мышью компоненты и меняй соответственно текст в поле CAPTiON. (Для меток LabeH и Label2 текст в поле Сдртюы необхо¬ димо удалить!) ■^ Измени размеры шрифта всех компонентов. Цифры, указанные в таб¬ лице, приведены для примера. Ты, конечно же, можешь выбрать любые другие значения. 4 На всех метках необходимо расположить надпись по центру. Для этого щелкни мышью в инспекторе объек¬ тов строку AuGNMENT и, нажав кнопку с небольшим тре¬ угольником, выбери строку TACENTER. После выполнения описанных выше действий форма станет выглядеть приблизительно так: Четыре арифметических действия Еще один проект
Размеры меток почему-то уменьшаются. Например, так происхо¬ дит, когда в инспекторе объектов стирается текст в поле Сдртюы (правда, при выполнении программы они становятся больше, и их величина приводится в соответствие с размером надписи). Посколь¬ ку уменьшение величины меток может помешать созданию фор¬ мы, попробуем избежать этого. В инспекторе объектов в строке AuToSizs (Авто- подстройкд РАЗМЕРА) нажми кнопку с изображени- ем треугольника и выбери строку FALSE (ложно). В этом случае определенный тобою размер метки сохранится. Случайные числа и переменные Давай, немного поиграем. • В результате щелчка мышью надписи «Укажи два числа» на двух дру¬ гих метках должны появиться два случайных числа. • Если после появления чисел нажать одну из четырех кнопок, предназ¬ наченных для выполнения арифметических действий, над числами будет произведена одна из следующих операций: сложение, вычита¬ ние, умножение или деление. Результат отображается в окне сооб¬ щений. Однако указанные операции выполнятся только в том случае, если мы научим этому компьютер. Начнем с задания ввода двух случайных чисел. 4 Дважды щелкни мышью самую верхнюю метку, расположенную в рабо¬ чей области формы. В окне редактора кода добавь в текст программы следующие строки для метода TForml:: Label3Click: void fastcall TForml::Label3Click(TObject *Sender) { randomize (); Numberl = random (100); Number2 = random (100); Labell->Caption - String (Numberl); 2. Типы данных и операторы
Label2->Caption = String (Number2); Label3->Caption = "Выбери арифметическое цействие! "; } Как видишь, любая команда заканчивается точкой с запятой (;). Поскольку этот знак невелик, о нем частенько забывают. C++Builder воспринимает это как ошибку и выдает соответствую¬ щее сообщение. Ты не можешь охватить взглядом программу, поскольку она вся не помещается на экране? Щелкни мышью средний значок в правом верхнем углу окна редактора кода. Естественно, ты ждешь объяснений. Рассмотрим строчку за строчкой. Команда randomize запускает генератор случайных чисел. По некоторой формуле компьютер вычисляет случайное исходное значение, исполь¬ зуя при этом время и дату. Поскольку они постоянно меняются, при каждом новом обращении к команде randomize генерируется новое ис¬ ходное значение. Функция random выбирает целое число от 0 до 99. Может быть, тебе покажутся странными две стоящие после коман¬ ды randomize круглые скобки, поскольку в них ничего не указано. Эту характерную особенность С++ унаследовал у своего предше¬ ственника — языка С. Все функции, как и методы, содержат данные скобки. У некото¬ рых они пусты (как у rar.domize), у других (random) в скобках указан один параметр или даже несколько (MessageBox). Что такое функции? В языке С++ это любые команды. Строго говоря, методы тоже являются связанными с объектом функциями, в то время как такие функции, как randomize и random независимы, то есть не привязаны ни к какому объекту. Случайные числа и переменные
Если тебе сложно найти круглые скобки, напечатай их, нажав кла¬ виши Shift+9 и Shift+0. Поскольку в проекте выполняются действия над двумя числами, в методе LabelClick функция random встречается два раза. Для того чтобы компью¬ тер мог откуда-нибудь «доставать» числа, ему необходимы две «емкос¬ ти». Имена Number1 и Number2 обозначают ячейки в памяти компьюте¬ ра, где будут храниться эти числа. По той причине, что в обозначенных данными словами ячейках памяти станут храниться два случайных числа, их назвали идентификаторами или переменными. Ты еще не знаешь, что такое переменная? Из уроков математики ты, наверное, помнишь, что переменная обычно обозначается бук¬ вами x или у. Свое название она получила потому, что принимает разные значения, то есть не имеет заранее заданной величины. В С++, конечно же, есть и константы. Они имеют определенное, не изменяемое в процессе выполнения программы значение. При следующем запуске программы константа сохраняет свое значе¬ ние. Примером констант являются два отображаемых в верхней части формы предложения: "Выбери два числа! “ "Выбери арифметическое действие!" В качестве констант могут выступать и числа, например: 0,1,—1,3.14. При присваивании переменной имени желательно (но совсем необяза¬ тельно), чтобы по нему можно было определить ее назначение. Я мог бы, следуя примеру математиков, взять именахи у, но имена Number1 и Number2 более подходящие. Рассмотрим эти переменные: Numberl - random (100) ; Number2 = random (100); 2. Типы данных и операторы
(Эти строки похожи на ужасные уравнения, которыми тебя мучают в школе.) Начнем с правой стороны от знака равенства (=). Функция random (100) выбирает случайное число от 0 до 99, которое присваивается перемен- ной Number1 или Number2. Как видишь, речь идет о так называемом присваивании. Соответствен¬ но знак равенства (=) в С++ называется оператором присваивания. Строка Оставшуюся часть методаТРогпй:: Label3С1 iсk составляют еще несколь¬ ко операций присваивания, определяющих свойство Сдртюы для трех меток Labe!1, Labe!2 и Label3. Каждая метка получает свое значение, которое появляется на форме: Labell->Caption = String (Numberl); Label2->Caption = String (Number2); Label3->Caption = "Выбери арифметическое действие!"; В данном примере снова появляется стрелка ->, с которой мы уже по¬ знакомились в предыдущей главе. Это — оператор доступа, связываю¬ щий объект с методом. В то время как последнее присваивание вполне понятно, первые два не¬ обходимо объяснить подробнее. Во всех трех случаях речь идет не о числе, а о тексте или (как говорят в С++ и в других языках программи¬ рования) о строке (string). Это присвоение однозначно: Label3->Caption = "Выбери арифметическое действие!”; Предложение «Выбери арифметическое действие!», безусловно, явля¬ ется строкой и воспринимается для отображения надписи метки Label3 как текст. Меткам Label1 и Label2 нельзя непосредственно присвоить числа Number1 и Number2, поскольку компьютер воспринимает их и строки по-разному. Оба числа необходимо сначала превратить в текст. Для этого выполняются следующие действия: Labell->Caption = String (Numberl)-; Label2->Caption = String (Number2); Строка
Строка String в этом случае является не функцией, а командой, обозна¬ чающей тип данных. При выполнении команды String (Число) компью¬ тер превращает, например, число 49 в цепочку символов, состоящую из цифр 4 и 9, то есть создает строку «49». Теперь при щелчке мышью верхней метки на форме появляются два слу¬ чайных числа и новый текст. r> Нажми кнопку RuN и укажи команду RuN в открывшемся подменю (тот же результат достигается, если щелкнуть мышью зеленый треугольник [> | на основной панели инструментов C++Builder или нажать функцио¬ нальную клавишу F9). Необходимость объявления При попытке запустить программу в нижней части окна редактора кода появляются два сообщения об ошибках: Сообщение Undefined Symbol в переводе на русский язык означает «Не¬ известное обозначение» или «Неопределенный символ». При этом име¬ ются в виду две переменные — Number1 и Number2. Я действительно кое-что упустил! В языке С++ нельзя использовать пере¬ менную, предварительно не объявив ее! Только после объявления в памяти компьютера для числа или строки будет отведено соответствующее место. Подготовка места в памяти компьютера 2. Типы данных и операторы
Переменные Number1 и Number2 объявляются таким образом: int Numberl, Number2; Сначала указывается тип данных. Оператор int (сокращение от слова integer) объявляет целые числа. Для строки объявление выглядит следующим образом: String Text; Переменной Text( Текст) присваивается тип String (Строка). В нашем примере не нужно описывать переменные типа String, так как свойство CAPTiON относится к объектам, которые среда разработки C++Builder объявляет автоматически. Выше описаны не все типы данных, использующиеся в языке С++. Тип String, например, предлагается только в C++Builder, так как строки в С++ обрабатываются сложнее. Во многих оригинальных написанных на языке С++ программах можно найти переменные, относящиеся к старому типу String. К обсуждению этого вопроса мы вернемся позже. Необходимость объявления Ты, наверное, хочешь узнать, где размещаются объявления пере¬ менных. Замечу, что их нельзя вставлять в произвольный фраг¬ мент метода.
4 Пользуясь полосой прокрутки, найди в начале текста программы строку TForml *Forml; Введи следующее объявление: int Numberl, Number2 ; Если тебё не удается найти нужное место в программе (а в боль¬ ших программах текст разрастается до огромных размеров), вос- • пользуйсяпростымправилом: Объявление переменных, например, int Numberl, Number2; долж¬ но располагаться до объявления метода, в котором они исполь¬ зуются, — в нашем случае перед строкой void fastcall TForml::Label3Click(T0bject *Sender). Текст программы выглядит таким образом: int Numberl, Number2; void fastcall TForml::Label3Click(T0bject *Sender) { Numberl = random (100); Number2 = random (100); // остальные команды } + Запусти программу еще раз. Затем щелкни несколько раз мышью верхнюю метку. Как видишь, при каждом щелчке появляется новая пара чисел. А после первого щелчка мышью метки «Выбери два числа!» надпись на ней ме¬ няется на «Выбери арифметическое действие!». Ты хочешь узнать, что обозначает двойная косая черта (//)? Этот сим¬ вол сообщает системе разработки C++Builder, что следующие за ним Комментарии 2. Типы данных и операторы
строки не имеют к ней никакого отношения, то есть речь идет о коммен¬ тариях, замечаниях, объяснениях. Комментарии не влияют на процесс выполнения программы, так как C++Builder игнорирует отмеченные символом «//» строки. Комментариями стоит пользоваться, когда составляются длинные или сложные команды или формулы. Я бы, например, добавил в метод TForml: :Label3Click несколькострок в качестве комментариев: void fastcall TForml::Label3Click(T0bject *Sender) { // создать два случайных числа от 0 до 99 Numberl - random (100); Number2 = random (100); // Преобразовать числа в строки Labell->Caption = String (Numberl); Label2->Caption = String (Number2); / / отобразкть новый текст на метке Label3->Caption = "Выбери арифметическое действие!1'; } Комментарием можно снабдить выполняемые строки команды, не за¬ писывая его в отдельных строках, например: Numberl = random (100); // 1-е случайное число Number2 = random (100); // 2-е случайное число Labell->Caption = String (Numberl); // Левая метка Label2->Caption = String (Number2); // Правая метка Плюс или мииус, умножить или разделить Для того чтобы с появляющимися в форме числами происходило опре¬ деленное действие, необходимо для четырех кнопок создать методы. Они выглядят следующим образом: void fastcall TForml::ButtonlClick(TObjecL *Sender) { Result = Numberl + Number2; Плюс или минус, умножить или разделить
Label3->Capcion = "Результат сложения: " + String(Result); } // void fastcall TForml::Button2Click(TObject *Ser.der) { Result = ЫшпЬег1 - Nuraber2; Label3->Caption = "Результат вычитания: " + String(Result); } // void fastcall TForml::Button3Click(T0bject *Sender) { Result = Numberl * Number2; Label3->Caption = "Результатумножения: '' +String(Result); } // void fastcall TFoml::Button4Click(T0bject *Sender) { Result = Numberl / Number2; Label3->Caption = "Результатделения: n +String(Result); } Разделяющие методы пунктирные линии позволяют лучше увидеть, где заканчивается один метод, и начинается другой. Все строки начинаются с двойной косой черты (//) и являются комментариями. В целом эти методы похожи друг на друга и отличаются только тем, что каждый раз речь идет о новом арифметическом действии: Result = Numberl + Number2; Result = Numberl - Number2 ; Result = Numberl * Number2; Result = Numberl / Number2 ; На этот раз метка Label3 отвечает за отображение результата арифме¬ тического действия: 2. Типы данных и операторы
Label3->Caption = "Результат: " + String(Result); Ясно видно, как в этой строке отображено присваивание. Справа всегда стоит формула, по которой вычисляется результат, или составляется строка. Слева находится переменная, в которую записывается получен¬ ное после вычисления значение. Поменять местами левую и правую сто¬ рону нельзя. Операция присваивания всегда выглядит так, как изобра¬ жено на рисунке: В языке С++ используются все четыре основных типа арифметических действий. Операторы умножения и деления в С++ по виду отличаются от знаков, которыми ты пользовался на уроках математики: Итак, ты познакомился еще с одним назначением оператора сло¬ жения (+). Он может не только складывать числа, но и соединять цепочки символов. Остальные арифметические операторы для опе¬ раций со строками не используются. Оператор умножения (*) нельзя путать со звездочкой, которая при объявлении метода (TObj ect *Sender) ставится перед словом Sender. 0 значении этого символа мы поговорим позже. Если ты снова запустишь программу, то опять соберешь целый «ворох» сообщений об ошибках. Система C++Builder хотела бы познакомиться с новыми переменными, прежде чем она начнет пользоваться ими. ■> Дополни объявление переменных: int Numberl, Number2, Result; ■^Сохрани проект, выбрав в меню FiLE команду SAVE PROJECT As. При со¬ хранении присвой имена RECHEN1.CPP и MATH1.MAK. Плюс или минус, умножить или разделить Математика Калькулятор Компьютер Сложение. Вычитание Умножение Деление
^ Запусти программу и попробуй выполнить все четыре арифметических действия. Если окажется, что второе число равно нулю, то операция деления не может быть выполнена, так как на него делить нельзя. В этом случае появляется следующее сообщение: На языке C+Builder такая ситуация называется исключением (exception), то есть подразумевается, что она возникает редко. Нажми кнопку 0K, чтобы закрыть окно сообщения. Если на экра¬ не останется еще одно окно с непонятной информацией, закрой и его, нажав кнопку X. Выполнение программы прервется, но она не закроется. Завер¬ шить ее работу тебе придется самостоятельно, воспользовавшись меню RuN в C++Builder. Для того чтобы перезагрузить программу, пре¬ рванную в результате обнаружения ошибки, выбери В меню RuN команду PROGRAM RESET (ПЕ¬ РЕЗАГРУЗИТЬ прогрдмму) (или нажми одновремен¬ но клавиши Ctrl+F2). В результате закрывается и дополнительно открытое окно под на¬ званием CPU. Можно также отключить механизм обработки ошибок. Для этого в меню OpnoNS (СЕРВис) укажи команду ENVIRONMENT (СРЕДА). 2. Типы данных и операторы
Установи В диалоговом окне ENVIRONMENT OPTIONS (ПАРАМЕТРЫ СРЕДЫ) флажок BREAK ON EXCEPTION (ПРЕРЫВАТЬ ПРИ ИСКЛЮЧИТЕЛЬНЫХ СИТУАЦИЯХ). Закрой диалоговое окно, нажав кнопку ОК. Если при следующем запуске программы снова произойдет попыт¬ ка выполнить деление на ноль, появится небольшое окно с сооб¬ щением об этом, и ты сможешь продолжить работу. Отключение системы контроля над появлением исключений не все¬ гда проходит безболезненно. При возникновении сложной ошиб¬ ки программа может «зависнуть», то есть компьютер перестанет реагировать на какие-либо действия, и его необходимо будет пе¬ резагрузить. При этом все несохраненные данные проекта теря¬ ются. После установки флажка BREAK ON ЕХСЕРТюы станет легче от¬ слеживать места возникновения в программе ошибок. Плюс или минус, умножить или разделить
Формат чисел Разумеется, в программе не должны возникать ситуации деления на ноль, однако все, что мы пока можем сделать — это позаботиться о том, что¬ бы компьютер не присваивал второму числу нулевое значение. Данное условие будет действовать для всех арифметических действий. Задача решается путем изменения в программе следующих строк: // Случайное число от 1 до 99 Numberl = random (99) + 1; Number2 = random (99) + 1; В остальном наша небольшая программа не так уж плоха. Но у нас есть возможность еще улучшить ее. Отображаемый результат всегда являет¬ ся целым числом, хотя при делении получаются и дробные числа. Оператор int описывает переменные Number1 и Number2 как целые числа. Одно небольшое изменение в объявлении — и при делении будет получаться дробное число. Для этого выполни следующие действия: r> Измени строку объявления переменных: float Numberl, Number2, Result; Оператор float описывает десятичные числа, которые могут содержать десятичную точку или запятую. Так же, как и калькулятор, для отделе¬ ния целой части от дробной компьютер использует точку, в то время как в учебниках по математике для этих целей используется запятая. Кроме того, в системе C++Builder на поверхности компонентов отображается запятая. Оператор float включает также и объявление целых чисел. В отличие от целых чисел, объявленных оператором int, в случае объявления их опе¬ ратором float компьютеру придется использовать для операций с ними более сложные вычислительные действия, поскольку дроби относятся к совсем другой категории. ■^Сохрани измененную программу еще раз (можно под новым именем). Затем запусти ее и проверь, воспользовавшись кнопкой с символом «/», как она осуществляет деление. Как видишь, в результате деления получаются десятичные числа, иног¬ да с таким большим числом знаков после запятой, что результат не 2. Типы данных и операторы
помещается на метке. Это можно поправить: растяни как можно даль¬ ше левую и правую границы метки. Постарайся также увеличить форму. Однако если тебе не требуется большое число знаков после запятой, C++Builder справится и с этой задачей: FloatToStrF (число, формат, точность, количество знаков после запятой) Название оператора FloatToStrF образовано путем сокращения фразы Float То String (with) Format, которую можно перевести как «Преобра¬ зовать десятичное число в строку формата типа TFIoatFormat». Это не единственная функция преобразования в языке С++. Ниже представлены и другие важные функции: StrToInt. преобразовать строку в целое число StrToFloat преобразовать строку в десятичное число TntToStr. преобразовать целое число в строку FloatToStr преобразовать десятичное число в строку FloatToStrF преобразовать десятичноёчисло встроку формата TFIoatFormat В процессе работы мы могли бы вместо - Labell->Caption = String (Numberl) ; Label2->Caption = String (Number2); написать: Labell->Caption = IntToStr (Numberl) ; Label2->Caption = IntToStr (Number2); Так выглядит более совершенная версия метода ButtonClick для деления: void fastcall TForml::Button4Click(T0bject *Sender) { Result = Numberl / Number2; Label3->Caption = "Результат деления:" + FloatToStrF (Result,ffNumber,8,2); } ■^ Измени программу арифметических вычислений. Плюс или минус, умножить или разделить
Выводы В этой главе мы еще раз «поздоровались» и поиграли с числами. Мы узнали кое-что новое о языке С++, а также о том, как в C++Builder выполняются некоторые операции: Запустить С++ Builder Открыть проект Создать проект Сохранить проект Сохранить файл Сохранить файл как Контролировать/ изменитьсвязь с событием В меню Пуск последовательно укажи КОМаНДЫ nPOrPAMMbl/C++BuiLDEB/ C++BuiLDER (nPOrPAMMbl/C++BuiLDER/ C++BUILDER) В меню FiLE (Фдйл) укажи команду OPEN PROJECT (ОТКРЫТЬ ПРОЕКТ) В меню FiUE укажи команду №w APPLICATION (НОВОЕ ПРИЛОЖЕНИЕ> В меню FiLE укажи команду SAVE PROJECT As (СОХРАНИТЬ ПРОЕКТ KAK) В меню FiLE укажи команду SAVE . (СОХРАНИТЬ) В меню FiLE укажи команду SAVE AS (СОХРАНИТЬ КАК) Щелкни мыщью в инспекторе объектов закладку EvENTS (СоБытия), затем — расположенное напротив имени события текстовое поле В этой главе мы поработали с некоторыми уже известными нам компо¬ нентами и познакомилисьсновыми: Form Label Button QnClick LabelClick ButtonClick Height, Width Left,Top' Форма (тип TFom), в которой содержатся все . компоненты программы. Сама форма является главным компонентом Метка (тип TLabel), в которой отображается текстовое сообщение Кнопка (тип TButton) Событие, происходящее при щелчке компонента мышью Метод, активируемый щелчком метки мышью Метод, активируемый нажатием кнопки Высота и ширина видимого компонента Положение левого верхнего угла видимого компонента 2. Типы данных и операторы
Твой словарный запас пополнился следующими терминами: int Описание переменной типа «Целое число» (lnteger) float Описание переменной типа «Десятичное число» String Описание переменной типа «Строка» (Strlng) или преобразование числа в строку randomize () Запуск генератора псевдослучайных чисел random (Мах) Создание псевдослучайного числа в пределах от 0 до значения равного Мах — 1 StrTolnt, StrToFloat Преобразование строки (String) в число lntToStr, FloatToStr, Преобразование числа в строку (String) FloatToStrF ; Конец команды +, —, *, / Арифметические операторы для чисел + Соединение строк = Оператор присваивания // Комментарии 0 Заключение в скобки параметров функции или метода :: Оператор разрешения области действия метода -> Оператор доступа для метода Вопросы... 1. Какие компоненты приложения C++Builder ты уже знаешь? 2. Что такое события и методы в С++? 3. Как в процессе выполнения программы изменить надписи на кнопках и метках? ...и несколько задач 1. Доработай последний проект «Привет!», используя не менее четы¬ рех кнопок. Найди кроме Хорошо и Плохо еще несколько подходя¬ щих ответов (надписей) и запрограммируй их. 2. Составь программу Гороскоп, предлагающую отдельную кнопку для каждого названия созвездия и дающую краткий ответ при ее нажа¬ тии. Вопросы...
2. Типы данных и операторы 3. Измени математическую программу в соответствии с рисунком:
3. Условия и операторы передачи управления В этой главе мы займемся проектом, в котором компьютер будет играть роль учителя и ставить оценки. Мы составим программу игры, где компь¬ ютеру придется решать, какую оценку поставить, но руководить им будешь ты. В этой главе ты научишься: • создавать структуру передачи управления; • сравнивать значения переменных; • задаватьусловия; • пользоваться операторами if и else; • искать ошибки с помощью операторов try и catch; • а также использовать фокус.
От 1 до 5 Итак, тебя ожидает третий проект. Если, прочитав последнюю главу, ты завершил работу в системе разра¬ ботки C++Builder, запусти ееснова, последовательноуказав в меню Пуск команды Прогрдммы, C++BuiLDER и C++BuiLDER. На экране появится чи¬ стая форма. Если же ты не закрывал систему, и в ней еще открыт последний проект, сначала освободи рабочий стол. Для этого выполни следующие действия: ^ В меню FiLE системы C++Builder укажи команду NEW APPLICATION. Создаваемая программа должна поставить тебе оценку в соответствии с количеством набранных баллов, например, полученных при выполне¬ нии письменного теста. Конечно, опечатки допустимы. Начнем, пожалуй, с простейшей версии, которая каждому числу от 1 до 5 ставит в соответствие оценку. Позже мы усложним проект. Для начала создадим одну кноп¬ ку. Нам понадобится новый ком¬ понент, позволяющий вводить информацию в рабочую область формы. Форма проекта должна выглядеть примерно таким об¬ разом: 3. Условия и операторы передачи управления
■> Щелкни мышью значок LABEL и начерти на форме рамку для метки. ^ Щелкни мышью значок Виттоы и размести в нижней части формы кнопку. 4 Щелкни мышью рабочую область формы и в тескстовое поле инспекто¬ ра объектов CAPTiON введи ее заголовок 0иенка1 (или выбери другое на¬ звание по своему желанию). Какие еще компоненты нам понадобятся? Необходимо создать окно для ввода чисел. В него ты введешь свою оценку. Тип поля ввода назы¬ вается TEdit. ■> Найди в палитре компонентов значок Ешт (Окно РЕДАКТИРОВАНИЯ) и щелк¬ ни его мышью. Размести окно редактирования в центре формы. 4 Определи в инспекторе объектов следующие свойства для созданных компонентов: Caption/Text Label1 Введи оценку от 1 до 5 Button1 OK Edit1 (пусто) Font/Размер шрифта 14 14 36 Ты не можешь найти в инспекторе объектов свой¬ ство CAPTiON для окна редактирования? В списке свойств вместо него появилось новое свойство ТЕХт, в поле которого находится надпись Edit1. Удали ее, чтобы текстовое поле стало пустым.
Если... то... Пожалуй, теперь перейдем к оценкам. Но как добиться того, чтобы ком¬ пьютер понимал, что от него требуется? Давай, сначала сформулируем условия, по которым он станет выводить оценку: ЕСЛИ Оиенка = 1 ТО Метка = "очень плохо” ; ЕСЛИ Оценка = 2 ТО Метка - "плохо" ; ЕСЛИ Оценка - 3 ТО Метка = "удовлетворительно" ; ЕСЛИ Оценка = 4 ТО Метка = "хорошо"; ЕСЛИ Оценка = 5 ТО Метка = "отлично"; Эти фразы необходимо превратить в команды языка С++. Точку с за¬ пятой в конце каждой строки мы уже поставили. Перевод на язык С++ начнем с оператора if: if (Mark == 1) Labell->Caption = "очень плохо"; if (Mark == 2) Labell->Caption = "плохо"; if (Mark == 3) Labell->Caption = "удовлетворительно"; if (Mark == 4) Labell->Caption = “хорошо"; if (Mark == 5) Labell->Caption = "отлично"; Как видишь, язык С++ имеет свои особенности. Чуть позже мы рассмот¬ рим их подробнее. Сначала объявим переменную Mark: int Mark; Откуда будет извлечена выраженная словом оценка? Из окна редакти¬ рования, но только после того, как в него введется число. Поскольку свойство Editl->Text является строкой, его необходимо преобразовать в число, присваиваемое переменной Mark. Этим займется функция StrToInt, с которой ты уже познакомился в предыдущей главе: Mark = StrToInt (Editl->Text) ; Все описанные выше команды размещаются в тексте программы мето- AaButtonClick. ■+Дважды щелкни мышью расположенную наформе кнопку. Введи в окне редактора кода приведенный выше текст: сначала команду присваива¬ ния переменной Mark значения окна редактирования, затем определя¬ ющие условия строки. Не забудь описать переменную Mark\ 3. Условия и операторы передачи управления
Нужно ли каждый раз заново вводить одни и те же слова, напри¬ мер if и Labell->Caption? Нет, ты сэкономишь время, если воспользуешься функциями Сит (ВыРЕЗАТь) и lNSERT (ВсгАВИТь) из меню EoiT приложения C++Builder: \ • Сначала выдели фрагмент текста, используя клавишу Shift и кла¬ виши со стрелками или мышь. • Последовательно выбирая команды Ешт и Сит, ты вырезаешь фрагмент текста и сохраняешь его в буфере. Эту же операцию можно выполнить, одновременно нажав клавиши Ctrl+X. • Последовательно выбрав команды EoiT и CoPY (Копировдть), ты копируешь фрагмент текста и сохраняешь его в буфере. Эту же операцию можно выполнить, одновременно нажав клавиши Ctrl+C. • Последовательно выбрав команды EotT и lNSERT, ты вставляешь сохраненный в буфере фрагмент текста в нужное место. Эту же операцию можно выполнить, одновременно нажав клавиши Ctrl+V. • Последовательно выбрав команды EDiT и UNDO (ОтмЕнить), ты от¬ казываешься от последней операции редактирования. Эту же операцию можно выполнить, одновременно нажав клавиши Ctrl+Z. *^ Сохрани проект в файлах NOTENl.CPP и ZENSURl.MAK. ^ Запусти программу и попробуй ввести различные числа. Структура if Итак, мы проверили, как работает программа Оценка. Теперь подроб нее рассмотрим структуру оператора if: Структура if Операторный блок Условие
Этот схематичный рисунок раскрывает следующее назначение структуры: ЕСЛИ выполняется определенное условие, ТО компьютер должен выполнить указанное в операторном бло¬ ке действие. Таким условием является (Mark == 1) или (Mark== 5) Условие оператора if заключается в круглые скобки. Примечательно, что в этом случае для оператора сравнения в языке С++ используется двой- ной знак равенства (==), отличающий его от оператора присваивания. При определении условий необходимо особенно внимательно сле¬ дить за тем, чтобы использовался именнооператор «==» (двазна- каравенства!). В языке С++ возможно то, что недопустимо в других языках про¬ граммирования. Например, разрешено использование операто- раприсваиваниявкачествеусловия!Еслитынапишешь (Mark = 1), эта операция будет выполняться иначе, чем операция сравнения «==». В операторном блоке указываются команды (в данном случае только одна): Labell->Caption = "очень хорошо”; или Labell->Capt.ion = "удовлетворительно"; Операторный блок может содержать более одной операции. Иног¬ да компьютер должен выполнить целую серию операций. В этом случае бывает недостаточно нескольких команд. С более длинны¬ ми блоками команд ты познакомишься позже. 3. Условия и операторы передачиуправления
Описываемая нами команда в целом называется структурой пе¬ редачи управления if (если), так как в процессе ее выполне¬ ния компьютер получает указание проверить какое-либо усло¬ вие. В данном случае он должен сравнивать введенные нами оценки. В зависимости от оценки, в форме появляется та или иная надпись. В создаваемом проекте компьютер реагирует только на ввод целых чи¬ сел от 1 до 5. Если случайно вводится другое число, никакой реакции не происходит: операторный блок пропускается, поскольку указанное в нем условие не выполнено. Если ты уже программировал на каком-нибудь языке, ты не най¬ дешь знакомого оператора then: в языке С++ это слово не исполь¬ зуется. Допустим, ты случайно ввел в окно редактирования букву. Это не очень хорошо, так как в результате появляется сообщение об ошибке. Функции try и catch Система разработки C++Builder прерывает выполнение программы. Нажми кнопку OK, чтобы это сообщение исчезло.
Чтобы продолжить выполнение программы, снача¬ ла ее необходимо завершить. В меню RuN укажи команду PROGRAM RESET или нажми комбинацию кла¬ виш Ctrl+F2. Возникающая при вводе неверных символов проблема остается нере¬ шенной, так как вероятность опечатки всегда существует. У нас имеется возможность отключить механизм обработки ошибок, как мы это дела¬ ли в предыдущей главе, однако в языке С++ для такого случая предла¬ гается еще одна весьма полезная структура, позволяющая определять способ реагирования на ошибку. Некоторый фрагмент программы выполняется в испытательном режи¬ ме. Если испытания проходят неудачно, выполняется указанноедействие по обслуживанию программой ошибок. В программе Оценка метод ButtonClick может выглядеть следующим образом: void fastcall TForml::ButtonlClick(TObject *Sender) { try { Mark = StrToInt (Editl->Text); if (Mark == 1) Labell->Caption = "очень плохо"; if (Mark == 2) Labell->Caption = "плохо"; if (Mark == 3) Labell->Caption = "удовлетворительно"; if (Mark == 4) Labell->Caption = "хорошо"; if (Mark == 5) Labell->Caption = "отлично"; } catch (...) { Labell->Caption = "Ерунда!"; } } 3. Условия и операторы передачи управления
r> Измени исходныйтекстметодаТРош!: :ButtonlClick (ZENSUR1A.MAK). ^ Выбери в меню OPTiONS команду ENVIRONMENT. Сними в диалоговом окне флажок BREAK ON EXCEPTION. ^Запусти программу и подразни компьютер, введя слово «Ерунда». Давай, рассмотрим парочку try-catch поближе. Эти дваоператоратоже являются управляющей структурой: Данный схематичный рисунок поясняет назначение структуры: ПОПЫТАЙСЯ сначала выполнить первый операторный блок; если появится ошибка, ОСТАНОВИ выполнение операции и выпол¬ ни действия второго операторного блока. В операторный блок try входят все введенные ранее (заключенные в фигурныескобки) команды MeTOflaButtonClick. // в этом месте указаны все команды, // которые должны быть выполнены при проведении испытаний } Блок catch (блок-ловушка) содержит команды, выполняемые при появле¬ нии ошибки во время выполнения команд первого операторного блока: catch (...) // в этом месте указаны все команды, // которые оценивают ошибку или указывают на ее появление }
Вместо многоточия в скобках за словом catch обычно указывается тип возможной ошибки. Многоточие означает, что этот блок пропускается при возникновении любой ошибки. При использовании функций try и catch входящие в операторный блок команды всегда заключаются в фигурные скобки, даже если он состоит всего из одной команды. Следующая версия программы Оценка наконец-то позволиттебе при вво¬ де баллов узнать полученную оценку. Она определяется в соответствйи с данными, представленными в следующей таблице: Думаю, что тебе понятен принцип определения оценки. Мы не будем создавать новый проект, просто немного переделаем первую версию. Существенные изменения будут внесены только в метод ButtonClick. Во-первых, необходимо описать как целоечисло (int) переменную Points (Баллы). Начнем с наихудшей оценки: ЕСЛИ количество баллов равно 0 и 19, выведи единицу или очень плохо. Переводим команду на язык С++ и получаем следующую строку: if (Points между 0 и 19) Labell->Caption = "очень плохо"; К сожалению, в языке С++ нет оператора, с помощью которого непос¬ редственно указывался бы интервал значений («между»). Попробуем решить данную задачу несколько иначе. Количество баллов в любом случае больше или равно 0. Сформулируем это утверждение в виде сле¬ дующего условия: (Points >= 0) Балл за баллом о 20 40 60 80 19 39 59 79 100 очень плохо плохо удовлетворительно хорошо отлично 1 2 3 4 5 3. Условия и операторы передачи управления
Единицу компьютер ставит, если количество набранных баллов не боль¬ ше 19, что описывается следующим условием: (Points < 19) Нам остается лишь соединить эти два условия, в чем нам поможет опе¬ ратор, соответствующий союзу «и»: (Points >= 0) && (Points < 25) Скобки указывают на порядок выполнения условий. А теперь составим условия и для остальных пяти оценок. Метод TForml:: 3uttonlClick выглядит следующим образом: void fastcall 'TForml::ButtonlClick(TObject *Sender) { Points = StrToInt (Editl->Text)? if ((Points >= 80) && (Points <= 100)) Labell->Caption = "отлично"; if ((Points >= 60) && (Points < 79)) Labell->Caption = "хорошо"; if ((Points >= 40) && (Points < 59)) Labell->Caption = "удовлетворительно"; if ((Points >= 20) && (Points < 39)) Labell->Caption = "плохо"; if ((Points >= 0) && (Points < 19)) Labell->Caption = "очень плохо"? if ((Points>100) II {Points<0)) Labell->Caption = "Обман!"; } ^ Измени текст программы метода TForml: :ButtonlClick (ZENSUR2.MAK). При вводе текста можно экономить время, если пользоваться меню EDrr и создавать структуры if с помощью команд Сит, CoPY и lNSERT. «сИ» и «или» Тебе удалось собрать все условия в одной программе? Если ты внима¬ тельно просмотришь ее текст, то наряду с постоянно встречающимся «И» и «или»
двойным символом (&) (клавиши Shift+7) ближе к концу программы используется двойной знак (|). Оба двойных символа называются соединительными операторами. Судя по имени, они соединяют несколько условий. Эти операторы назы¬ вают также операторами «и» (and) и «или» (or). Символ Название Назначение && И (and) Должны быть выполнены все условия, для того, чтобы начал выполняться операторный блок || ИЛИ (or) Должно быть выполнено только одно из условий, для того чтобы начал выполняться операторный блок В нашем проекте для каждой оценки должны выполняться два условия. Для этого используется следующая структура if: if ((Points > 100) II (Points < 0)) Labell->Caption = "Обман!"; Данное условие введено на случай, если ты начнешь жульничать, напри¬ мер введешь более 100 баллов. А это запрещено: Points > 100 Отрицательные числа тоже не допускаются. Так как задание должно быть выполнено не хуже, чем на «очень плохо», вводится следующее условие: Points < 0 Одно число может удовлетворять только одному из вышеуказанных ус¬ ловий. (Или, может быть, ты знаешь отрицательное число больше 100?) Итак, вместо оператора «&&» мы используем оператор «||». При определении условий появились новые символы, например (<) или (>). Они так же, как и знак (==), являются операторами срав¬ нения. В процессе составления этой программы мы познакоми¬ лись почти со всеми используемыми в C++Builder операторами сравнения, которые приведены в таблице: Оператор Назначение Оператор Назначение < > равно меньше больше >= <= не равно больше или равно меньше или равно 3. Условия и операторы передачи управления
Операторы, действие которых прямо противоположно, находятся в одной строке. А теперь посмотри, действительно ли программа выполняет то, что в ней написано. Только выполни еще одно небольшое действие: ■^ Щелкни мышью метку и измени в инспекторе объектов свойство Сдртюы, введяновуюнадписьВведи полученные баллы (от 0 до 100). *^Сохрани проект Оценка в файлах NOTEN2.CPP и ZENSUR2.MAK (пос¬ ледовательно указав в меню FiLE команды SAVE As и SAVE PROJECT As). т> Запусти программу и введи несколько неверных чисел. Не используй никакие знаки, кроме цифр! Простая игра А что если нам сыграть в очень простую и интересную игру без трехмер¬ ных картинок и стереозвука? Что это за игра? Пусть компьютер загадает число, скажем, от 1 до 1000, а ты попытаешься его отгадать за наименьшее число попыток. Нам снова понадобится чистая форма. ^ Выбери в меню FiLE команду №w AppucATiON. Размести вформедве метки (Label), окно редактирования (Edit) и кнопку (Button). Форма похожа на ту, которую мы использовали в последнем проекте программы Оценка.
"> Щеякая мышью соответствующие значки палитры компонентов, размести эти компоненты в форме. Измени их свойства, ориентируясь на указанные в приведенной ниже таблице данные. Поскольку ввод числа прекращается с нажатием кнопки OK, в данном методе необходимо определить, что с ней происходит далее. •> Дважды щелкни мышью кнопку ОК. Появляется окно редактора кода с программой уже известного нам ме¬ тода 3uttonlClick. 4 Дополни текст программы следующими строками: void fastcall TForml::ButtonlClick(TObject *Ser_der) { Editl->SetFocus (); Input= StrToInt (Editl->Text); if (Input== Chance) Labell->Caption = "Отгадано верно!"; if (Input < Chance) Labell->Caption = "Число слишком маленькое! "; if (Input> Chance) Labell->Caption = "Число слижом большое!”; В этом фрагменте мы сразу решаем еще одну проблему, возникшую в программе Оценка. Прежде чем ввести число, необходимо было щелк¬ нуть мышью окно редактирования. Команда Editl->SetFocus (); устанавливает на окно редактирования фокус, активируя компонент. Если, например, присвоить фокус кнопке OK, то ее не придется нажимать, Label1 Я загадываю число от 1 до 1000 Label2 Отгадай: Button1 OK Edit1 (пусто) Form1 Игра-гадалка 12 14 14 36 3. Условия и операторы передачи управления Caption/Text Font/Размер
поскольку она будет приводиться в действие нажатием клавиши Enter. Если фокус присваивается окну редактирования, то его не нужно акти¬ вировать щелчком мыши. Однако не хватает еще одной мелочи: при запуске программы фокус уже должен быть присвоен окну редактирования в инспекторе объектов. r> Щелкни мышью рабочую область формы. Щелкни мышью в инспекторе объектов в поле свойства AcTivEC0NTR0L кнопку с изображением треугольника и выбери компонент Eorr1. 4 Не забудь описать переменные lnput (Ввод) и Chance (Случайное число/. int Input, Chance; ^ Выбери в меню RuN команду RuN (или нажми клавишу F9). При отгадывании числа появляется одно и то же сообщение о положи¬ тельном результате. Если вводится ноль, программа закрывается. Разве это настоящая игра, — компьютер не дает возможности отгадать чис¬ ло? Необходимо еще немного доработать программу. Похоже, в оди¬ ночку метод ButtonClick не справляется со всеми задачами. Отгадывание числа Зададим случайное число: randomize () ; Chance = random {1000) + 1; Но куда следует вставить эти команды? Мы запрограммировали так, что число задумывается до начала разгадывания, а не после нажатия кноп¬ ки, — иначе компьютер загадывал бы всякий раз новое число после каж¬ дой попытки его отгадать. Отгадывание числа
Итак, в задачу главного компонента — формы — входит загадывание числа. ■^ Дважды щелкни мышью рабочую область формы. В окне редактора кода появляется новый метод: void fastcall TForml::FormCreate(TObject *Sender) { } Название метода FormCreate переводится как «создание формы». При запуске программы он приводится в действие событием OnCreate. С помощью метода FormCreate инициализируются начальные значения. Они предоставляются в распоряжение проектасразу после запуска про¬ граммы. •Расширь метод Tforml: :FormCreate следующими командами создания случайных чисел: void fastcall TForml::FormCreate(?Object *Sender) { randomize (); Chance :- random (1000) + 1; } Переменная Chance не доставит хлопот, поскольку мы ее уже описали. Ты случайно щелкнул мышью надпись Я здгАДывАЮ число от 1 до 1000. В окне редактора кода появляется текст метода ?Forml::LabellClick. В нашем проекте этот метод не используется, но нам не помешает его появление, так как между двумя фигурны¬ ми скобками нет ни одной команды, и при следующем сохранении C++Builder автоматически удалит его. ’ Сохрани свой проект в файлах RATENl.CPP и ZRATENl.MAK. • Запусти программу и попробуй отгадать число! 3. Условия и операторы передачи управления
Компьютер считает вместе с тобой Постарайся отгадать число с 10-15 попыток. Предложим компьютеру подсчитывать их количество. Для этого введем новую переменную и назовем ее Try (Попытка). Опи¬ шем ее как целое число (int). При первой попытке переменной Try присваивается начальное значение 1: Try = 1; При каждой следующей попытке значение переменной увеличивается на 1. Это действие выполняет оператор присваивания Try = Try + 1; Компьютер использует текущее значение переменной Try и прибавляет к нему 1. Затем он присваивает ей результат сложения, и значение пере¬ менной увеличивается на 1. Заметь, что присваивание не является оператором равенства. (И действительно, для какого числа могла бы быть справедлива фор¬ мула Try = Try + 1?) Сначала компьютер выполняет стоящее справа от знака присваи¬ вания (=) действие. В нашем случае результат вычисляется по фор¬ муле Затем результат вычисления передается в качестве нового значе¬ ния переменной левой части. Стрелка нагляднее иллюстрирует эту операцию: Try *~ Try + 1 Допустим, начальное значение переменной 7л/равно 1. Тогдаопе- рация состоит из следующих шагов: Try + 1 Компьютер считает вместе с тобой
Присваивание Вычисление Присваивание Try<— 1 Try + 1 Try *— результат 1 1 +1 2 Старое значение переменной Try заменяется новым. При следую¬ щей попытке отгадывания число 2 превращается в 3 и т.д. Команду присваивания Try = Try + 1 мы использовать не будем, по¬ скольку имеется более простое решение: Теперь понятно, почему в названии языка С++ присутствуют два плюса: с его склонностью к удвоениям мы уже познакомились на примере та¬ ких операторов, как «==», «&&» и «||». Следующий оператор уменьшает значение переменной на 1: Try--; // соответствует: Try = Try - 1; Счет можно вести и с большим шагом, например: x+=2; // увеличение с шагом 2 x-=10; // уменьшение с шагом 10 В программу Игра-гадалка вносятся следующие изменения: int Input, Chance, Try; Editl->SetFocus (); Inpuc = StrToInt (Editl->Text); Try++; Label2->Caption = IntToStr (Try) + ". Попытки:"; if (Input == Chance) Labell->Caption = “Отгадано верно!"; if (Input < Chance) Labell->Caption = "Число слишком маленькое!"; Try++; // void fastcall TForml::ButtonlClick(TObject *Sender) 3. Условия и операторы передачи управления
Как видишь, и в метод FormCreate добавляются команды. Он устанавли¬ вает начальное значение переменной Try равное 1. *> Введи текст программы и запусти ее (RATENl.CPP и ZRATENl.MAK). Структура if-else Раз уж компьютер считает вместе с тобой, пусть он следит еще и за тем, чтобы число попыток не было слишком большим. Все должно иметь ра¬ зумный предел. Для начала определим его следующим образом: const int Max = 12; // Дюжины достаточно Оператор const описывает константу Max. Ее значение не меняется в процессе выполнения программы. Эта строка вставляется в то место программы, где объявляются переменные. При достижении заданного пограничного значения на экране появляет¬ ся соответствующее сообщение. Размести в форме новую метку или используй одну из уже созданных: Структура if-else if (Input > Chance) Labell->Caption = "Число слишком.большое!"; } // void fasccali TFoml::FomCreate(TObject *Sender) { randomize (); Chance = random (1000) + 1; Try = 1; }
if (Try <= Max) Label2->Caption = Int.ToStr (Try) + "Попытки:"; else Label2->Caption = "Достаточно!11; Какую функцию выполняет оператор else? (Это слово в переводе с анг¬ лийского языка означает «иначе».) Этот схематичный рисунок поясняет назначение структуры: ЕСЛИ выполняется определенное условие, компьютер выполняет операторный блок. ЕСЛИ оно не выполняется (= ИНАЧЕ), компьютер выполняет другой операторный блок. В этом случае условие заключается в следующем: (Try <=■ Мах) А в первом операторном блоке стоит команда присваивания: Label2->Caption = IntToStr (Try) + ". Попытки:"; Компьютер выполняет ее, если условие соблюдается. В противном слу¬ чае он переходит ко второму операторному блоку, который содержит оператор присваивания: Label2->Caption = "Достаточно!"; Так же, как и в случае с оператором if, используются разветвления. 3. Условия и операторы передачи управления
Без оператора else фрагмент выглядел бы следующим образом: if (Try <= Мах) Label2->Caption = IntToStr (Try) + ''. Попытки:"; if (Try > Мах) Label2->Caption = "Достаточно!"; Второе условие (Try > Мах) прямо противоположно первому (Try <= Мах) Фрагмент(?гу > Мах)можнозаменитьсловоме1зе. Если тебе знаком язык программирования Pascal, то ты знаешь, что в нем точка с запятой (;) перед оператором else не ставится. В языке С++ команда с оператором if должна обязательно закан¬ чиваться точкой с запятой, а блок else открывает новую команду. 4 Дополни программусоответствующими командами Игры-гадалки и попро¬ буй поиграть еще раз (ZRATEN2.MAK). Новая игра или конец игры? Скажу по правде, что в нашей игре мне не нравится ее завершение. Су¬ ществует только два способа закрыть окно программы: использовать Новая игра или конец игры?
клавиши Alt+F4 или нажать кнопку X. Попробуем сделать так, чтобы после отгадывания числа была возможность либо завершить игру, либо ее продолжить. Решить эту задачу можно только путем серьезного рас¬ ширения MeTOflaButtonClick (RATEN3.CPP): void fastcall TFoml::ButtonlClick(TObject *Sender) { Editl->SetFocus (); Input = StrToInt (Edill->Text); // Попробуй считать вместе с пользователем и предупреждать // о максимально допустимом числе попыток Мах Try++; if (Try <= Мах) Label2->Caption = IntToStr (Try) + ". Попытки:"; else Label2->Caption = "Достаточно!"; // Оценить введенное число, меньше оно или больше if (Input < Chance) Labell->Caption = "Число слижкоммаленькое!"; if (Input > Chance) Labell->Caption = "Число слишком большое! ’; / / Если число угадано верно, предложи новую игру if (Input == Chance) { Labell->Caption = "Верно отгадано!1'; Button = Application->MessageBox ('"', "Новаяигра?", 4+32); // Если новая игра, начальные значения, случайное число if (Button == IDYES) { Labell->Caption = "Я загадываю новое число! "; Label2->Caption = "Отгадай!"; Chance = random (1000) + 1; Try = 1; } 3. Условия и операторы передачи управления
// Если не новая игра, то конец программы else Close (); } } В тексте появилось слишком большое количество операторов if. Стоит обратить внимание на два обстоятельства: • операторный блок с большим числом команд необходимо заключать в фигурные скобки; • внутри одной структуры if могут содержаться другие вложенные структуры if: if (Условие1) { // Начало первого блока if if (Условие2) { // Начало второго блока if } // Конец второго блока if } // Конец первого блока if ^Дополни MeTOflTForml: :3uttonlClick (ZRATEN3.MAK). 4 Не забудь описать переменную Button как целое число (int). *> Запусти программу и попробуй отгадать число с нескольких попыток. Что изменилось? Г1осле угадывания числа компьютер спрашивает, бу¬ дешь ли ты играть новую игру. За это отвечает MeTOflMessageBox. Он пред¬ лагает тебе две кнопки: YES и No. Переменная Button принимает значе¬ ние, получаемое при нажатии кнопки в окне сообщения: Butt.on = Application->MessageBox ('"', "Новаяигра?", 4+32); Эта переменная может принимать значение либо lDYES, либо lDNO. При нажатии кнопки YES игра запускается снова. Чтобы для начала новой игры не требовалось заново запускать программу, необходимо доба¬ вить в метод несколько операторов присваивания. Позаботимся и о том, чтобы при переходе к новой игре загадывалось новое число: Новая игра или конец игры?
if (Button == IDYES) { Labell->Caption = "Я загадываю новое число!'; Label2->Caption - "Отгадай!"; Chance = random (1000) + 1; Try = 1; } else Close () ; При нажатии кнопки No (else) программа завершает работу. За это от¬ вечает метод Close, закрывающий окно программы. При решении использовать в окне сообщений другие кнопки тебе необ¬ ходимо знать, какие значения эти компоненты передают после нажатия. 1 IDOK OK 2 IDCANCEL Отмена 3 IDABORT Стоп 4 IDRETRY Повторить 5 IDIGNORE Пропустить 6 IDYES Да 7 IDNO Нет Выводы На этом пока остановимся. Посмотрим, чему ты научился: const Объявление константы If ЕСЛИ условие соблюдается, выполни команды операторного блока else ИНАЧЕ выполни команды другого операторного блока 3. Условия и операторы передачи управления Вырезать текст (и скопировать) Сит (ВыРЕЗАТь) Ctr!+X Скопировать текст (в буфер) CoPY (Копировдть) Ctrl+C Вставить текст из буфера lNSERT (Встдвить) Ctrl+V Отменить последнюю операцию UNDO (ОтмЕНИть) Ctrl+Z Ты уже много знаешь о языке С++:
try ПОПРОБУЙ выполнить операторный блок catch ОБНАРУЖЬ неудавшуюся попытку и выполни другой операторный блок = Проверка, равно ли != Проверка, не равно ли < Проверка, меньше ли <= Проверка, меньше или равно > Проверка, больше ли >= Проверка, больше или равно && Соединение условий (И): все условия должны выполняться i I Соединение условий (ИЛИ): должно выполняться одно из условий ++ Увеличение значения переменной на 1 Уменьшение значения переменной на 1 0 Определение условий или параметров {} Выделение операторных блоков Ты знаешь, что операторные блоки необходимо выделять фигурными скобками, если они состоят более чем из одной команды (для try и catch в любом случае). Ты познакомился также с некоторыми новыми компонентами, свойства¬ ми и методами приложения C++Builder: OnCreate Это событие выполняется при запуске программы, до создания формы FormCreate Этот метод инициализирует для формы начальные данные, которые предоставляются в процессе выполнения программы Close Этот метод закрывает формы, закрывая таким образом программу Edit Окно редактирования (тип TEdit), в которое вводится текст SetFocus Этот метод устанавливает фокус на активируемый таким образом компонент Вопросы... 1. Чем отличается операторы (=) от (==)? 2. Как ты думаешь, можно ли в программе Игра-гадалка использовать такую ветвь else: Вопросы..
if (Input < Chance) Labell->Caption = "Число слишком маленькое! “; else Labell->Caption = "Число слишком большое!"; 3. Что получается в результате выполнения следующего оператора при¬ сваивания: Mark = random (6) + 1; ... и несколько задач 1. ИзменипрограммуАрифметические действия изпредыдущейглавытак, чтобы перехватить операцию деления на ноль. 2. Вставь во вторую версию программы Оценка структуру try-catch. 3. Создай в программе Игра-гадалка метод LabellClick. Затем попробуй добиться того, чтобы при щелчке мышью надписи Я ЗАГАДывдю число... выдавалось сообщение Нджми эту кнопку!. 3. Условия и операторы передачи управления
4. Управляющие структуры Надеюсь, что тебе понравилась игра-гадалка, хоть она и была создана не по последнему слову техники, и победителям в ней не выдаются при¬ зы. Не каждый после первого выигрыша становится миллионером. Но если деньги положить в банк, то когда-нибудь ты им станешь. Компью¬ тер поможет определить, когда ты получишь свой первый миллион. Для новой игры мы воспользуемся программой Привет, возможности кото¬ рой будут расширены. Мы рассмотрим новые интересные способы пе¬ редачи управления в программе. В этой главе ты научишься: • работать с компонентами LlstBox и ComboBox; • работать с новым оператором передачи управления switch; • повторять выполнение фрагментов программы; • использовать операторы do и while.
На пути к миллиону Конечно, нельзя стать миллионером, зная лишь только азы программи¬ рования, но представим себе, что это возможно! Приступим к созданию нового проекта. ^ В меню FiLE открытого приложения C++Builder укажи команду №w APPLICATION. Остановимся на уже знакомой нам форме с меткой (LABEL), окном ре¬ дактирования (EDiT) и кнопкой (BurroN). Займемся разработкой проек¬ та, представленного на рисунке: *i> Найди соответствующие компоненты и размести их на форме. Затем определи их свойства, как указано в таблице. Label1 Какую сумму ты хочешь вложить? 12 Button1 OK 14 Edit1 (пусто) 36 Form1 Миллионер т> Введи в поле свойства AcTivEC0NTR0L значение Edit1. В этом проекте ты вкладываешь определенную сумму денег, а программа определяет время, через сколько лет эта сумма превратится в миллион. Основную работу снова будет выполнять метод TForm::ButtonlClick. Правда, потрудиться придется и методу FormCreate. Начнем со следую¬ щих описаний: float Principal, Interest, Percentage; int Nr, Period; 4. Управляющие структуры
Необходимо описать сразу несколько переменных. Поскольку речь пой¬ дет о деньгах, переменные Principal (Капитал), Interest (Проценты) и Percentage (Процент) необходимо описать как десятичные числа. А для описания переменной Period{Срок вклада) воспользуемся оператором int, поскольку программа будет подсчитывать целые года. Мы описали еще одну переменную — Nr (номер). Она служит для под¬ счета капитала. Сначала вводится сумма начального капитала (PrincipaQ. Компьютеру необходимо знать также процентную ставку (Percentage). Имея только эти исходные данные, можно вычислить сумму процента по вкладу. Для расчета необходима определенная формула. Если ты не проходил эту тему на уроках математики (или забыл, как вычисляются проценты), не стоит беспокоиться. Поверь, что приведенная ниже фор¬ мула верна. Что же должна делать программа? Сначала она должна рассчитать начисляемые в течение года проценты по следующей формуле: Interest = Principal * Percentage / 100; Полученная величина прибавляется к сумме капитала: Principal = Principal + Interest; Эти команды компьютер выполняет, пока капитал не станет равным мил¬ лиону. Поскольку подсчет процентов совершается по прошествии одно¬ го года, срок вклада тоже должен увеличиваться на 1 при каждом новом вычислении: Period++; Структура while Для составления программы необходима новая конструкция, так как старыми способами мы не сможем решить поставленную задачу. Повто¬ рение действия в языке С++ записывается следующим образом: while (Principal < 1000000) { Interest = Principal * Percentage / 100; Principal = Principal + Interest; Структура while
Period++; Рассмотрим эту структуру подробнее: Операторный блок Этот схематичный рисунок поясняет следующее назначение структуры: ПОКА не выполнено определенное условие, компьютер ПОВТОРЯЕТ команды операторного блока. Задано условие: (Principal < 1000000) Операторный блок содержит команды для вычисления процентов и ка¬ питала. Поскольку блок состоит из нескольких команд, нам необходи¬ мо сообщить, где он начинается и где заканчивается: { Interest = Principal * Percentage / 100; Principal = Principal + Interest; Period++; Оператор while сообщает компьютеру, где начинается цикл, а не где он заканчивается. По умолчанию предполагается, что к циклу while относится только следующая за оператором while команда, что похоже на принцип построения структуры if. Мы определяем, где начинается и заканчивается блок команд, зак¬ лючая его в фигурные скобки. Вся описанная выше конструкция называется структурой while. Ниже представлена еще одна управляющая структура: 4. Управляющие структуры
{ Principal = Principal + Interest; Period++; } while (Principal < 1000000); Она организует циклическое выполнение команд. На первый взгляд, структура выглядит так же, как и while. Этот схематичный рисунок поясняет следующее назначение структуры: Компьютер ПОВТОРЯЕТ команды операторного блока, ПОКА не выполнится определенное условие. В чем же отличие? Внимательно изучи таблицу. while do-while| Условие ставится В начале петли В конце петли Условие выполнено Команды операторного Команды операторного блока выполняются блока выполняются снова снова Поскольку условие структуры while стоит в начале цикла, выполнение образующих тело цикла команд может быть пропущено. В первой редакции нашего проекта программы Миллионер это условие соответствует случаю, когда вводится число, равное миллиону или боль¬ ше. Тогда вообще никакие действия не выполняются, поскольку в них нет необходимости. Иначе обстоит дело во второй версии, использую¬ щей структуру do-while. Ты увидишь это при тестировании программы. Для проведения испытания необходимо записать весь ее текст. Структура while
Варианты циклов Ниже представлены исходные тексты методов TForml: :ButtonlClick и TForml: :FormCreate (MILLION1.CPP): void fastcall TForml: :ButtonlClick(TObject *Sender) { Nr+ + ; if (Nr == 1) { Principal = StrToFloat (Editl->Text); Labell->Caption = "Какова будет процентная ставка?"; Editl->Text = ""; Editl->SetFocus (); } if (Nr == 2) { Percentage = StrToFloat (Editl->Text); Period = 0; while (Principal < 1000000) Interest = Principal * Percentage / 100; Principal = Principal + Interest; Period+*; } if (Period > 0) Labell->Caption = "Твои деньги должны" + IntToStr (Period) + " лет лежать в банке"; else Labell->Caption = "Добро пожаловать в Клуб миллионеров!''; } } // void fastcall TForml::FormCreate(TObject *Sender) { Nr = 0; 4. Управляющие структуры
■> Введи текст программы в окне редактора кода. Не забудь описать переменные Nr, Period, Principal, Percentagev\ /nterest\ r> Проверь реакцию программы на вводдля переменной Яг/яс/ра/значений 1,1000 и 1000000 (процентная ставка может быть любой). Как видишь, если начальный капитал невелик, а процентная ставка не очень высока, потребуется некоторое время нато, чтобы он вырос. Если же ты вложишь один миллион, ты сразу попадешь в Клуб миллионеров. Вступление в него регистрирует структура if-else: if (Period > 0) Labell->Caption = "Твои деньги должны" + IntToStr (Period) + " лет лежать в банке"; else Labell->Caption = "Добро пожаловать в Клуб миллионеров! "; ■> Поменяй цикл while наструктуру do-while (MILLION2.MAK). Во время проверки работы программы при начальном капитале равном одному миллиону на экране появляется следующее изображение: Непонятно, зачем ждать целый год, когдаты и так уже миллионер? Ошиб¬ ка кроется в структуре do-while, поскольку она должна быть выполнена хотя бы один раз. При выполнении команд структуры срок вклада уве¬ личивается на единицу. Этого можно избежать, включив оператор if до начала цикла: if (Principal < 1000000) Period++; После внесения этих изменений новая версия проекта будет работать безупречно. Вообще говоря, crpyKTypawhile для нашего случая лучше Варианты циклов
подходит, чем do и while. Но благодаря этому эксперименту мы узнали, чем они отличаются друг от друга. Моя программа постоянно отказывается работать, и система C++Builder выдает мне следующие сообщения: Эта «неисправимая ошибка компоновщика» свидетельствует о том, что C++Builder не может создать вспомогательный файл, необхо¬ димый для проверки на ошибки. Чтобы не заниматься решением этой проблемы, следует изменить соответствующие настройки. Для этого в меню OPTiONS укажи команду Рмшст (ПРОЕКТ). Pifjject SWt+Drt+F11 В диалоговом окне PROJECT OPTIONS (ПАРАМЕТРЫ ПРОЕКТА) щелкни мышью закладку LiNKER (Компоновщик). 4. Управляющие структуры
Сними флажки USE INCREMENTAL LINKER (ИСПОЛЬЗОВАТЬ ПОШАГОВЫЙ KOM- ПОНОВЩИК) И lNCLUDE DEBUG INFORMATION (ВКЛЮЧИТЬ ДАННЫЕ ОБ ОТЛАДКЕ). Нажми кнопку ОК. Парад кнопок Давай, спустимся с облаков на землю и выйдем из игры Миллионер. Вер¬ немся к последней версии проекта Привет (см. главу 2) и посмотрим, как можно расширить его возможности. 4 Выбери в меню FiLE команду OPEN PROJECT. *Э Найди в списке файлов имя PROJECT2.MAK и щелкни его мышью. ^ Нажми кнопку ОК. •^ Запусти программу еще раз, прежде чем ты начнешь расширять ее возможности. У тебя запускается другая программа, созданная во время после¬ днего сеанса работы с C++Builder? В этом случае снова скомпилируй текущий проект. Для этого вы¬ полни следующие действия: • выбери в меню pROJECT команду BUILD ALL (Скомпоновдть). • В меню RuN выбери команду RuN (или нажми клавишу F9).
Для расширения проекта нам понадобятся шесть кнопок. Используй либо две уже имеющиеся кнопки и добавь к ним еще четыре, либо удали две старые и заново создай шесть новых. В таблице представлены названия кнопок и соответствующие им ответы. I Кнопка _____ Отлично Чудесно! Хорошо Радостно слышать! Нормально Еще ничего! Плохо Сожалею! Отвратительно Это плохо! Не скажу Ну что ж Ты уже, наверное, догадался, что наш проект превращается в средство для определения настроения, поэтому я назову его Камертон настроения. Как лучше расположить эти шесть кнопок на форме? Есть несколько возможностей, из которых я выбираю следующий вариант: *> Измени размер двух имеющихся кнопок и добавь еще четыре. Или со¬ здай, выбрав в меню FiLE команду №w Аррисдтюы, шесть новых кнопок и расположи их одну под другой. Может быть, тебе придется увеличить размер формы. Ширину и высоту видимого объекта можно определить в инспекто¬ ре объектов, используя свойства НЕЮнт и WiDTH. А положение объекта определяется свойствами LEFT и Top. 4. Управляющие структуры
Для изменения размера формы существуют дополнительные свой¬ ства CLIENTWlDTH (ШИРИНА ФОРМЫ) И CuENTHEIGHT (BblCOTA ФОРМЫ), определяющие его ширину и высоту. -> Присвой новым кнопкам надписи и размести их так, как указано на рисунке выше (или по своему усмотрению). Выравнивание кнопок в форме ты можешь поручить системе C++Builder. • Выдели все кнопки, обведя их с помощью мыши рамкой. • Щелкни правой кнопкой мыши выделенную область. • В открывшемся контекстном меню укажи коман- ду AuGN. Парад кнопок
• В небольшом диалоговом окне указаны предлагаемые вариан¬ ты выравнивания. На этот раз в списке HoRizoNTAL установи пе¬ реключатели CENTERS (ЦЕНТРИРОВАТЬ), а B СПИСКе VERTICAL — SPACE EQUALLY. Кнопка нажимается... Итак, для окна сообщений нам не хватает соответствующих ответов. Дважды щелкни мышью каждую кнопку и добавь в метод ButtonClick соответствующие команды. Ниже представлен весь фрагмент текста программы (HALL03.CPP): void fastcall TForml::ButtonlClick(TObject *Sender) Application->MessageBox ("Чудесно!'', "", 4+48) ; } // void fastcall TForml: :Button2Click(T0bject *Ser.der) { Application->MessageBox ("Радостнослышать!", "", 4+48); } // void fastcall TFoml::Button3Click(TObject *Sender) { Application->MessageBox ("Еще ничего!", ''", 4+32) ; } // void fastcall TForml::Button4Click(T0bject *Sender) { 4. Управляющие структуры
Application->MessageBox ("Сожалею!", '"', 4+48); } // void fastcall TForml::Button5Click(T0bject *Sender) { Application->MessageBox ("Этоплохо!”, ”", 4+48); } // void fastcall TForml::Button6Click(T0bject *Sender) { Application->MessageBox ("Нучтож", "", 1+16); 4 Введи фрагмент текста метода MessageBox в окно редактора кода. Но сначала щелкни мышью каждую кнопку и проследи затем, чтобы надпись соответствовала появляющемуся в окне сообщений ответу. Нумерация кнопок (Buttonl — Button6) начинается сверху вниз. Если ты станешь создавать кнопки в другом порядке, нумерация тоже изменится. Ориентируйся натаблицу. •> Наконец, присвой форме имя Камертон настроения. Щелкни форму и введи ее название в поле инспектора объектов Сдртюы. Давай, посмотрим, что способна делать программа Камертон настроения. Но сначала нужно сохранить все, что мы сделали. ■> Сохрани исходный текст под именем HALL03.CPP, указав в меню FiLE команду SAVE As. ^ Присвой проекту имя KLEMP1.MAK, выбрав в меню FiLE команду SAVE PROJECT As. 4 Запусти программу и проверь, как она работает. Выбор из списка В поисках другого варианта покопаемся в палитре компонентов — не все же время заниматься только кнопками. Наша программа может ра¬ ботать и без кнопок, если мы воспользуемся списком, в котором будут содержаться все присваивающиеся им имена. Выбор из списка
К сожалению, для его создания нам придется удалить все кнопки. По¬ скольку мы не будем больше пользоваться методами ButtonClick, лучше всего создать новый проект. ■^ Выбери в меню FiLE команду №w AppucATiON. *^ Найди значок окна списка LiSTBox и щелкни его мышью. Окно списка Компонент, который мы будем использовать, называется окном спис¬ ка. В нем должны расположиться все шесть прежде находившихся на кнопках надписей. Снова откроем инспектор объектов. Для этого вы¬ полни следующие действия. ■> Щелкни мышью окно списка и в инспекторе объектов НаЙДИ СТрОКу lTEMS (ЭЛЕМЕНТЫ). 4. Управляющие структуры *+ Начерти новый компонент в центре формы.
■^ Нажми небольшую кнопку с многоточием (...), расположенную справа ОТ НаДПИСИ TSTRING. Открывшееся ОКНО называется STRING LIST EDITOR (РЕДАКТОР СПИСКА СТРОК). Текст в нем вводится так же, как и в окне обычного редактора кода. 4 Напечатай шесть строк и закончи ввод текста, нажав кнопку ОК. ^ Сохрани новый проект и исходный текст под именами KLEMP2.MAK и HALL04.CPP. 4 Запусти программу и просмотри список. Затем заверши работу тестовой программы. Чтобы соответствующий метод заработал, в него следует записать команды. ■> Дважды щелкни мышью окно списка. В методе L:stBoxlClick записаны все команды, которые он должен вы¬ полнять при щелчке мышью окна списка, так как все надписи принадле¬ жат одному списку. String list edrtoi Выбор из списка
*^Вставь следующие строки в метод List3oxClick (HALL04.CPP, KLEMP2.MAK): void fastcall TForml::ListBoxlClick(TObject *Sender) { if (LiscBoxl->ItemIndex == 0) Application->MessageBox ("Чудесно!", "", 4^48); if (ListBoxl->ItemIndex == 1) Application->MessageBox ("Радостнослшать!", "”, 4+48); if (LiscBoxl->ItemIndex == 2) Application->MessageBox ("Ещеничего!", "", 4+32); if (ListBoxl->ItemIndex == 3) Application->MessageBox ("Сожалею!", '"', 4*48); if (ListBoxl->ItemIndex == 4) Application->MessageBox ("Этоплохо!", ’"’, 4+48); if (LiscBoxl->ItemIndex == 5) Application->MessageBox ("Кучтож", "", 1+16); ? Itenlndex является свойством компонента UstBox1, который отсутству¬ ет в инспекторе объектов. Во время выполнения программы создается номер активируемой строки списка. При этом необходимо учитывать, что нумерация начинается не с 1, а с 0. Что нужно сделать для того, чтобы каждый раз заново не вводить один и тот же текст? • Выдели текст с помощью курсора мыши или клавиши Shift и клавиш со стрелками. •\ Чтобы вырезать фрагмент текста, укажи в меню EDiT команду Сит (текст при этом помещается в буфер). • Чтобы скопировать фрагмент текста, выбери в меню EDiT ко¬ манду CoPY (текст помещается в буфер). • Вставь фрагмент из буфера в нужное место текста, указав в меню EDIT КОМаНДу lNSERT. • При необходимости можно отменить последнюю операцию, ука¬ зав в меню EDiT команду UNDO. ■^ Попробуй запустить программу еще раз. 4. Управляющие структуры
С ветви иа ветвь В зависимости от того, какую строку списка ты щелкиешь мышью, ком¬ пьютер выдаст более-менее подходящий ответ на вопрос «Привет, как дела?». Оператор if утомительно использовать в тех случаях, когда постоянно повторяется такое длинное имя, как ListBoxl->ItemIndex. Хорошо, что язык С++ предлагает еще один вариант: void fastcall TForml: :ListBoxlClick(TObject *Sender) { switch (ListBoxl->ItemIndex) { case 0: Application->MessageBox ("Чудесно!", "'', 4+48); case 1: Application->MessageBox ("Радостнослышать!", ''", 4+48); case 2: Application->MessageBox ("Ещеничего!", "", 4+32); case 3: Application->MessageBox ("Сожалею!", "", 4+48); case 4: Application->MessageBox ("Этоплохо!", "", 4+48); case 5: Application->MessageBox ("Нучтож", '"', 1+16); } } Текст вводится словом switch, а перед каждым блоком команд ставится оператор case. Очень важно, чтобы весь блок был заключен в фигурные скобки. Структура switch является структурой передачи управления. Ее назы¬ вают также оператором множественного выбора. Оператор switch — переключатель, принимающий значение некоторой переменной. В зависимости от ее значения (case) выполняется та или иная команда. С ветви на ветвь
Переменная, значение которой присваивается оператору case, называ¬ ется ListBoxl->ItemIndex: switch (ListBoxl->ItemIndex) За ней в программе следует список всех возможных значений, причем каждому из них предшествует оператор case, который отделен от соот¬ ветствующего операторного блокадвоеточием (:). После оператора case может стоять несколько команд, но в нашем случае все блоки состоят из одной команды: lcase Application->MessageBox| 0: «Чудесно!» 1: «Радостно слышать!» 2: «Еще ничего!» 3: «Сожалею!» 4: «Это плохо!» «Ну что ж» ■> Измени программу, заменив структуру if на switch. Затем запусти программу и проверь, как она работает. Что-то не в порядке! При щелчке мышью строки Ну что ж программа выполняется. Но при активации строки Отлично приходится просматри¬ вать все окна сообщений, каждый раз закрывая их с помощью мыши. Помочь выполнить только один оператор case могут только целенап¬ равленные остановки (HALL04A.CPP): 4. Управляющие структуры
void fastcall TForml::ListBoxlClick(TObjecr *Sender) { switch (ListBoxl->ItemIndex) { case 0: Application->MessageBox ("Чудесно!", '"', 4-48); break; case 1: Application->MessageBox ("Радостнослышать!", '"', 4+48); break; case 2: Application->MessageBox (“Ещеничего!", "", 4+32); break; case 3: Application->MessageBox ("Сожалею!", '"', 4-48); break; case 4: Application->MessageBox ("Этоплохо!", "", 4+48); break; case 5: Application->MessageBox ("Ну, чтож", ''", 1+16); } } Оператор break позволяет остановиться на нужной ветви программного кода, то есть не продолжать путешествие по остальным операторным блокам. Теперь программа должна работать нормально. + Введи в программу команды break. Затем проверь, работает ли она так, как мы задумали (KLEMP2A.MAK). Все в одном окне Рядом со значком LiSTBox находится еще один компонент — СомвоВох. Он сочетает функции окна списка и окна редактирования, поэтому его называют редактируемым списком. Заменим окно списка редактиру¬ емым списком. ^ Щелкни мышью в форме поле списка. Нажми клавишу Del. Все в одном окне
4 Найди значок СомвоВох и щелкни его мышью. Размести в форме редак¬ тируемый список. Нам нужно подумать, как вырезатьтекст методаТРогш!:: List3oxlClick и ПГ |F|^lA|ai|HlM и g МмНР^Н вставить его в новый метод TForml:: ComboBoxlChange, не создав при этом новый проект. Почему метод редактируемого списка не называется ComboBoxCliak? Метод с таким названием тоже существует, но в рассматриваемой ситуа¬ ции соответствующее сообщение появляется при изменении (Change) строки, поэтомуприменяется методСотЬоВохС'папде. ^ Выдели в окне редактора кода всю исполняемую (заключенную в фи- гурныескобки) частьметодаТРогяй: :ListBoxlClick. Выберивменю Етт команду Сит (или нажми клавиши Ctrl+X). ■> В форме дважды щелкни мышью редактируемый список. Откроется текст метода TForml:: ComboBoxlChange. *?► Установи курсор мыши в область между фигурными скобками и выбери в меню EDiT команду lNSERT (или нажми клавиши Ctrl+V). 4. Управляющие структуры
Поскольку метод ?Forml:: ListBoxlClick не содержит команд, приложе¬ ние C++Builder удалит его самостоятельно. Не хватает самого малого — в методе TFoml: :ComboBoxlChange необхо¬ димо поменять имя List3oxl на ComboBoxl, поскольку теперь ItemIndex работает с редактируемым списком. ^Измени соответствующим образом исходный текст программы (KLEMP3.MAK). Метод выглядит следующим образом (HALL05.CPP): Все в одном окне void fastcall TForml::ComboBoxlChange(TObject *Sender) { switch (ComboBoxl->ItemIndex) { case 0: Application->MessageBox ("Чудесно!", '"', 4+48); break; case 1; Application->MessageBox ("Радостнослшать!", "", 4+48); break; case 2: Application->MessageBox ("Ещеничего!", "", 4+32); break;
4 В инспекторе объектов удали запись TEXT для редак¬ тируемого списка. Теперь редактируемый список пуст. Если ввести в него какое-нибудь слово, оно будет появляться в нем при выполнении программы как на¬ чальное значение. Но редактируемый список пока пуст. Редактор спис¬ ка необходимо на этот раз вызвать для компонента ComboBox1. 4 Найди в инспекторе объектов строку lTEMS. *> Нажми кнопку с многоточием. 4 Введи в редакторе списка те же шесть строчек, что и для окна списка (или используй при желании другой текст). Закончи ввод, нажав кнопку ОК. ■^ Прежде чем запустить новую версию проекта Камертон настроения, со¬ храни его в файлах HALL05.CPP и KLEMP3.MAK. Редактируемый список открывается нажатием кнопки с изображением треугольника. При выборе одной из строк она появляется в верхней части 4. Управляющие структуры case 3: Application->MessageBox ("Сожалею!", ””, 4+48); break; case 4: Application->MessageBox ("Этоплохо!", '"', 4+48); break; case 5: Application->MessageBox ("Нучтож", "", 1+16); } }
списка. При этом выдается соответству¬ ющее сообщение. (Ты можешь ввести в небольшое окно и собственный текст). Выводы Мы не закончили проект Камертон настроения. К этой теме мы еще вернем¬ ся. Но сейчас ненадолго прервемся, поскольку мы уже познакомились с двумя новыми компонентами, несколькими свойствами и методами: ListBox ComboBox Items ItemIndex ListBoxClick ComboBoxChange Left, Тор, Width, Height ClientWidth, ClientHeight Поле списка (тип TList3ox), в котором можно активировать несколько записей Сочетание поля ввода и поля списка (тип ?ComboBox), в котором можно вводить или активировать несколько записей Свойство компонентов TListBox и TComboBox: содержит записи* которые отображаются в поле списка или в редактируемом списке Свойство компонентов TListBox и TComboBox: содержит номер выбранной из списка записи Этот метод активируется щелчком мышью строки списка Этот метод активируется, если меняется запись в поле ввода редактируемого списка Положение (елева, вверху) и размер (ширина, высота) видимого компонента Соответственно ширина и высота формы Выводы
Мы узнали несколько новых слов языка С++: switch ПЕРЕКЛЮЧИТЬСЯ на один из операторных блоков case ЕСЛИ переменная имеет определенное значение break ПРЕРВАТЬ, чтобы переменная не «путешествовала» no другим ветвям программы while ПОКА не соблюдается условие, выполняй операторный блок do while Выполняй операторный блок, ПОКА соблюдается одно условие Вопросы... 1. Что происходит в программе Миллионер при задании значения 0 для переменных Principal или Percentage? 2. Как C++Builder узнает о том, какая запись выбрана в окне списка и в редактируемом списке? ... и несколько задач 1. Создай программу с единственной кнопкой Нажми меня, которая каждый раз при нажатии «перескакивает» в новое место. 2. Замени во второй версии программы Гороскоп (см. главу 2) все кнопки (Button)HaoKHacnncKa(ListBox). 4. Управляющие структуры
5. Библиотека компонентов Мы уже познакомилисьсважнейшими компонентами системы разработ¬ ки C++Builder, но в ней предлагается еще и много других. Ты, наверное, уже заметил их в палитре компонентов. Попробуем поиграть с новыми элементами, продолжая работать над созданием проекта Камертон на¬ строения (см. главу 4). В этой главе ты узнаешь: • чем отличается компонент RadioButton (ПЕРЕКЛЮЧАТЕЛЬ) от CheckBox (Флджок); • что такое GroupBox (ГрупповоЕ окно); • как работает оператор передачи управления for; • в чем различие между операторами объявления char и String; • новый тип переменных — bool.
От точек Даже современная западная медицина признала, что наряду с телом у человека существуют еще душа и дух. Учтем эти сведения в нашей про¬ грамме Камертон настроения. Используй старый проект, созданный в предыдущей главе, если ты го¬ тов расстаться с окном списка и с редактируемым списком. Если нет, создай новый проект. ^ Выбери в меню Fiu команду NEW AppLiCATiON (или щелкни мышью рас¬ положенное в форме окно списка или редактируемый список и нажми клавишу Del). В результате компонент исчезает. В окне редактора кода необходимо удалить расположенный между фигурными скобками текст метода ListBoxClick или метода ComboBoxClick: т> Выдели текст (например, используя клавишу Shift и клавиши со стрел¬ ками). Нажми клавишу Del для удаления текста. ■* В палитре компо¬ нентов найди зна¬ чок RADIOGROUP (ГРУППА ПЕРЕКЛЮЧА¬ ТЕЛЕЙ) и щелкни его мышью. Новый компонент сочетает в себе свойства нескольких элементов оди¬ накового типа и представялет собой группу переключателей. Переклю¬ чатель изображается кружком с точкой внутри. Нам понадобится шесть элементов, которые мы разместим в левой части формы. 4 Размести группу переключателей в форме. (Следи за тем, чтобы справа оставалось достаточно места: позже мы разместим на этом участке остальные компо¬ ненты.) 5. Библиотека компонентов
4 В инспекторе объектов для компонента RadioGroup1 в свойстве Сдртюы введи слово Самочувствие. Теперь каждый переключатель должен получить соответствующую над¬ пись. Процедура выполняется так же, как и для окна списка и редакти¬ руемого списка. 4 Щелкни мышью панель группы переключателей. Используя в инспекто¬ ре объектов свойство lTEMS, открой редактор списка и введи шесть отве¬ тов. Заверши ввод, нажав кнопку ОК. Когдаты проделаешь всеуказан- ные операции, в форме появятся переключатели. ГЛ5^1 :: Привет, как дела?:: * Radi0Gr0Up1’ "; ; ; ; ; • ; ; ; j ; ; ; ; ; ; ; ; ; • ; ; ; • ; ; ; ; ; ; ; ; С ОТЛИЧНО Г хорошо ; ; ; ; ; ; ; J ; ; • ;■ ; ♦ ; ; ; * ; ; ; • ; ; ; ; ;.; ; ; С нормально •• Г ПЛОХО • • ^ ^ • • • • • * • • • • • • • • i • • ^ • ^ ’• • ’ • • • i • - г ужасно ' : : :: ‘': <^ не скажу : : ::::: Можно создать и один переключатель. Для этого в палитре компо¬ нентов щелкни мышью значок RADi0BuTT0N. £] lyl^lA|ai|illca| щ fg^ifilfl|-^l^ |~=!| i| Шесть переключателей размещаются в форме в том же порядке, в каком находились кнопки в последнем проекте, с той лишь разни¬ цей, что переключатели следует расположить как можно ближе к левому краю окна. Надпись для переключателя присваиваивается вводом со¬ ответствующего текста в тек¬ стовое поле свойства CAPTiON в инспекторе объектов. : Привет.какдела?: rRadioGroupT- j С отлично С хорошо I ^ |.« нормально С плохо С ужасно ■ ■ *~ не скажу ■ От точек..
... до флажков Методом для группы переключателей мы займемся позже. Для начала в правой части формы я хотел бы разместить несколько новых компонен¬ тов. Они будут отвечать за разные ипостаси человека: душу, дух и тело. ■^ Найди в палитре компонентов зна¬ чок СнЕСкВох и щелкни его мышью. Новый компонент имеет вид квадрата с крестиком внутри и называется управляющим индикатором с флажком. Нам понадобятся три таких индикатора. Они располагаются в правой части формы. ^ Размести в форме первый инди¬ катор с флажком. Повтори эту операцию еще два раза. (Следи за тем, чтобы под индикаторами осталось место для кнопки.) *> Щелкни мышью каждый индикатор с флажком. В инспекторе объектов в свойстве CAPTiON введи слова Душа, Дух и Тело. Чем же отличается переключатель от индикатора с флажком? Пе¬ реключатель представляет собой кружок, в который можно поста¬ вить точку. Индикатор с флажком — это квадратное поле, в кото¬ ром при щелчке мышью появляется флажок (галочка). Разобраться в них тебе поможет следующая таблица: 5. Библиотека компонентов
Наименование Функция Другие Символ названия Переключатель (RadioButton) Индикатор с флажком (CheckBox) Среди компонентов, объединенных в одну группу, активирован может быть только один Среди компонентов, объединенных в одну группу, автивировано может быть любое их количество Зависимый переключатель, селективная кнопка Независимый переключатель, флажок Активное состояние = точка в круге Активное состояние = флажок в квадрате Заметь, что в таблице указано несколько других наименований этих компонентов, которые могут встретиться в справке C++Builder или операционной системы Windows. Последние штрихи Для того чтобы визуально выделить разные группы компонентов, выбе¬ рем для индикаторов с флажками другой шрифт. r> Последовательно щелкни мышью все управляющие поля и в свойстве FoNT выбери небольшую кнопку с многоточием. В диалоговом окне ШриФт выбери любой шрифт. Для параметра НдчЕР- тдниЕ предлагаю выбрать Курсив или Полужирный курсив, а размер за¬ дать равным 12. После установки нажми кнопку ОК. Для того чтобы форма выглядела привлекательнее, объединим индика¬ торы с флажками в группу. Для этого воспользуемся компонентом, с помощью которого мы начертим подходящую рамку. Последние штрихи
+ Найди в палитре компонентов значок GR0upB0x и щелкни его мышью. ^ Обведи рамкой три управляющих поля. Измени надпись Сдртюы на сло¬ во Мпостась. Куда подевались индикаторы с флажками? Панель GpoupBox закрыла собой все три компонента — Душа, Дух и Тело. На помощь приходит мышь. ^ Щелкни правой (!) кнопкой мыши панель GROUPBox. 4 Открывается меню. Выбери в нем команду SEND То Вдск (ПокдзАть здд- НИЙ ПЛАН). Индикаторы с флажками снова появляются в форме. После выбора параметров от ЧудЕСно до Ну что ж и значений ипостаси от ДушА до ТЕЛО нам нужна кнопка, с помощью которой мы будем сигна¬ лизировать» что готовы получить диагноз. 5. Библиотека компонентов
+ Щелкни мышью в палитре компонентов значок BurroN и размести кноп¬ ку в форме под индикаторами с флажками. ^ Задай для нее в инспекторе объектов в текстовом поле (Сдртюы) над¬ пись Готово. Можно переопределить и шрифт (FoNT). Ответы для переключателей Правда, форма выглядит неплохо? Но чтобы при выборе тех или иных компонентов совершались соответствующие действия, нам следует из¬ менить методы. Придется немного потрудиться над текстом кода. Прежде чем мы займемся текстами методов, давай, определим, что будет делать наша программа. • Сначала выберем ответ среди надпис'ей переключателей. Над¬ писи следует сохранять в текстовой переменной. Назовем ее Answer (Ответ). • Затем активируем один, два или три квадратика от Души до ТЕлд. Эти значения тоже должны где-то храниться, чтобы их можно было учесть при ответе. Назовем эту переменную Choice (Выбор). • При нажатии кнопки Готово открывается окно сообщений, в ко¬ тором указывается ответ. Начнем с описания методов для переключателей: 4 Дважды щелкни мышью группу переключателей, чтобы открыть окно pe- дакторакодастекстом программы метода RadioGrouplClick. Введисле- дующий исходный текст (HALL06.CPP): Ответы для переключателей
void fastcall TForml::RadioGrouplClick(TObject *Sender) { switch (RadioGroupl->ItemIndex) { case 0: Answer = "Чудесно!"; break; case 1: Answer ="Радостно слкжать!"; break; case 2: Answer ="Еце ничего!"; break; case 3: Answer ="Сожалею!"; break; case 4: Answer ="Это плохо!"; break; case 5: Answer ="Hy что ж"; } } Структура программы очень похожа на структуру switch двух предыду¬ щих версий. Компонент RadioGroup тоже имеет свой индекс ItemIndex. Только на этот раз в ответвлениях программы нет обременительного оператора Application->MessageBox, так как вместо него указан соот¬ ветствующий текст ответа, сохраненный в переменной Answer. ■> Добавь в объявления еще одну небольшую строку: String Answer; Кнопка «Готово» Для того чтобы у нас была возможность наконец-то проверить, как рабо¬ тает программа, и выполняются ли задуманные действия, перейдем сразу к кнопке Готово (индикаторами с флажками мы займемся немного позже). 5. Библиотека компонентов
^Дважды щелкни мышью эту кнопку и измени метод ButtonClick (KLEMP4.MAK). void fastcall TForml::ButtonlClick(TObject *Sender) { Application->MessageBox (Answer, "Ответ", 4+64); } 4 Запусти программу. Но система C++Builder не хочет запускать программу. Вместо этого она выдает сообщение об ошибке: Ей не нравится тип переменной Answer. Но ведь строка объявляется оператором String? По крайней мере, мы поступали так в предыдущих главах. Все дело в методе MessageBox, — это ему не нравится выбранный нами тип переменной. В сообщении упоминается объявление char со звездоч¬ кой (*). Если мы не хотим испортить отношения с C++Builder, мы должны пред¬ ложить ей все, что она просит. ^ Поменяй описание переменной Answer со String на char и снабди ее звездочкой: char *Answer; ■> Сохрани весь проект под именами HALL06.CPP и KLEMP4.MAK. ^ Запусти программу, щелкни мышью разные переключатели и затем на¬ жми кнопку Готово. Кнопка «Готово?»
В итоге мы оказались примерно в том же положении, что и в предыду¬ щих версиях программы Камертон настроения. Массивы переменных и начальные значения Нас ожидают индикаторы с флажками, которыми мы сейчас и займем¬ ся. Установка флажка должна отразиться на содержании окна сообще¬ ний. Дополним надписи в соответствии со следующей таблицей: I Индикатор Название| Душа Диагноздля души Дух Тело Все три индикатора Диагноз для духа Дйагноз для тела Диагноз для души, духа и тела Введем новые переменные, в которые запишем эти три текста. *> Добавь в объявление следующие строки: char *Answer; char *Title; bool Choice[3]; String Area[3]; Для сохранения названия ответа я выбрал переменную с тем же назва¬ нием Answer. Переменная должна иметь тип char*, так как мы будем использовать ее в окне сообщений (MessageBox). Area (Область) и Choice содержат по три переменные для опроса индикаторов. 5. Библиотека компонентов
Введем еще один новый тип переменной bool. Переменные этого типа не являются ни символами, ни знаками, и могут принимать только одно их двух значений: Значение переменной Смысл Индикатор true » Истинно, правильно Установлен флажок false Неверно Флажок не установлен Ты, наверное, хочешь выяснить, что же обозначают прямоуголь¬ ные скобки? При использовании нескольких переменных одного типа они описываются с помощью обычных средств следующим образом: String Textl, Text2, Text3, Text4, Text5, Text6, Text7; Это описание несколько сложное, особенно если количество по¬ добных переменных велико и равно, например, 100. С++ предла¬ гает в таком случае описывать переменные следующим образом: String Text[7]; Таким образом, описывается сразу целый набор переменных (в данном случае 7 штук). В квадратных скобках ([ ]) указывается количество элементов, из которых состоит массив переменных. В программе используются отдельные переменные массива, номер которых тоже заключается в квадратные скобки: В С++ первая переменная обозначается x[0], а седьмому эле¬ менту массива присваивается название x[6]. Указанную в квадрат¬ ных скобках цифру называют индексом. В его качестве могут ис¬ пользоваться не только константы, но и переменные. Массивы переменных и начальные значения
Для того чтобы ввести квадратные скобки, перейди к английской раскладке клавиатуры и нажми соответствующие клавиши. Чтобы присвоить массиву переменных начальные значения, добавим в программу следующие команды присваивания: Area[0] = " для дуии ''; Choice[0] = false; Area[l] = " для духа "; Choice[l] = false; Area[2] = " для тела "; Choice[2] = false; Таким образом, мы присваиваем тексттрем строкам массива Area. (Сле¬ ди за количеством пробелов!) А трем переменным массива Choice присваивается значение false, оз¬ начающее, что не выбрано ни одного индикатора. Где разместить эти команды? Лучше всего вставить их в тот метод, с по¬ мощью которого мы создавали форму. H> Дважды щелкни мышью форму. -i> Дважды щелкни мышью код и введи в текст метода TForml:: FormCreate команды присваивания для переменных Area и Choice. + Сохрани свой проект в файлах HALL06.CPP и KLEMP4.MAK. Правильный выбор Нам не хватает трех методов для индикаторов с флажками. Речь пойдет о свойстве Checked, доступном в инспекторе объектов. Оно может при¬ нимать одно из двух значений: true или false. Нам подойдет уже объяв¬ ленная переменная Choice. Методы Click для трех индикаторов с флажками записываются следую¬ щим образом: void fastcall TForml::CheckBoxlClick(TObject *Sender) { Choice 10] = CheckBoxl->Checked; } 5. Библиотека компонентов
// void fastcall TForml::CheckBox2Click{TObject *Sender) { Choice[l] = CheckBox2->Checked; } // void fastcall TForml::CheckBox3Click(T0bject *Sender) { Choice[2] = CheckBox3->Checked; } 4 Дважды щелкни мышью все индикаторы и введи соответствующие ко¬ манды присваивания. В каждый из трех методов CheckBoxClick вставляется соответствующая пе¬ ременная Choice, содержащая текущее значение индикатора с флажком. Но чтобы наконец-то получить записанную в массивах Area и Choice информацию, необходимо дополнить метод ButtonClick некоторыми командами. ^Дважды щелкни мышью кнопку Готово и дополни метод ButtonClick (HALL06.CPP, KLEMP4.MAK): void fastcall TForml::ButtonlClick(TObject *Sender) { String Text = “Ответ"; for (int Nr=0; Nr<3; Nr++) if (Choice[Nr]) Text = Text + Area[Nr]; •Title = Text*e^str(); Application->MessageBox (Answer, Title, 4+64); } Структура for И снова в программу проникли неизвестные тебе слова и конструкции. Сей- чася говорю неостранном обозначении с_з^,аорасположенном несколь¬ ко выше слове for (к обозначению c_str мы вернемся чуть позже). Структура for
Если ты помнишь структуру while из предыдущей главы, ты, наверное, не забыл и что такое цикл. Структура for — это еще одна возможность языка С++ повторять действия. С помощью этого схематичного рисунка поясняется следующее назна¬ чение структуры: ПРИ определенном условии компьютер должен ПОВТОРИТЬ команды тела цикла. Начиная с исходного значения (с так называемой инициализации), цикл выполняется до тех пор, пока значение счетчика цикла удовлетворяет заданному условию. Цикл инициализируется со значением int Nr=0; При этом задействуется переменная Nr, объявляемая как целое число (int) не в начале программы, а непосредственно там, где она использу¬ ется. Начальное значение переменной равно 0. Подошла очередь условия: Nr<3; А для того чтобы все операции повторились еще раз, переменная Nr в процессе выполнения цикла увеличивается на 1: Nr++ Этот операторный блок выполняется три раза: if (Choice[Nr]) Text = Text + Area[Nr]; 5. Библиотека компонентов
При выборе индикатора к его названию добавляется соответствующий текст. В качестве условия (Choice [Nr]) можно оставить оператор сравне¬ ния (например, ==). Переменная Choice так же, как и переменная типа bool принимает одно из двух значений — true или false. Можно сказать, что условие принадлежит типу bool. Если условие выполняется в любом случае, то компьютер попадает в так называемый бесконечный цикл. Это происходит, например, в случае если: for (int Nr=0; Nr<3; Nr--) В этом же цикле объявляется строковая переменная Text. Подоб¬ ный способ объявления называют локальным. Локальная пере¬ менная действует только в той области, где она описывается, то есть переменные Text и Мгдругим методам неизвестны. Перемен¬ ная Nr теряет свое действие вне цикла for. Но это не страшно, так как вне цикла она не используется. Переменные, описываемые за пределами всех методов, называются глобальными. Глобальные переменные действуют для всей про¬ граммы. Цикл for можно использовать и в методе FormCreate, чтобы присвоить всему массиву переменных значение false: void fastcall TForml::FormCreate (TObject *Sender) { Area[0] = " для душ ''; Area[l] = " для духа ”; Area[2] = " для тела ''; for (int Nr=0; Nr<3; Nr++) Choice[Nr] = false; } Структура for
Структура for не просто цикл-счетчик (каким она является в язы¬ ках Pascal или Basic). Она могла бы заменить все остальные цик¬ лические структуры, поскольку обладает большими возможностя¬ ми. Бесчувственные идеологи языка С++ упаковывают внутрь цикла следующий операторный блок: for (int Nr=0; Nr<3; Choice[Nr++] = false); При выполнении цикла переменной массива присваивается опре¬ деленное значение, и одновременно увеличивается наединицу зна¬ чение ее индекса, то есть происходит переход к следующей пере¬ менной массива. В этом случае программу сложно охватить одним взглядом, и повышается риск возникновения ошибок. Остановим¬ ся пока на одной возможности структуры for, а именно — на ее умении считать. Разнообразие типов строк Стоит сначала попробовать запустить программу еще раз, прежде чем мы перейдем к рассмотрению такого необычного типа переменных как char (со звездочкой). ^ Сохрани свой проект в файлах KLEMP4.MAK и HALL06.CPP, а затем запусти его. После того как ты сделаешь выводы о состоянии своей души, духа и тела, мы вернемся к проблеме, связанной со строковым переменными. Как я уже говорил, С++ появился на основе языка С и кое-что унасле¬ довал от него. В наследство он получил и строковые типы. В С не существует строк (Strings). В нем есть только один тип char, опи¬ сывающий единственный символ. Это значит, что переменная данного типа может принять только один символ. Такие переменные удобны, если например, существуют два ответа («да» или «нет») на некоторый воп¬ рос, и пользователь довольствуется буквами «д» и «н». 5. Библиотека компонентов
Намного чаще передается не один символ, а целая цепочка. Эту задачу в языке С можно решить, используя следующие массивы переменных: char Character; char CChain[20]; Переменная Character(3HaK) состоит из одного символа, в то время как переменная Chain Щепочка) описывает строку из 20 символов. Доступ к этому массиву символов несколько затруднителен, и тебе это известно по типу String. Команда присваивания CChain = "Привет!"; приводит к сообщению об ошибке. Поскольку речь идет о массиве пе¬ ременных, команда присваивания выглядит несколько сложнее: strcpy (CChain, "Привет!"); Оператор strcpy (это сокращенное название StringCopy) копирует ряд символов в массив символов. При этом очень важно, чтобы размер мас¬ сива соответствовал количеству символов. Стоит запастись громадным терпением, чтобы работать с подобными командами. Посочувствуем программистам, работавшим на языке С, и поищем более удобный способ. Похоже, его нам поможет найти малень¬ кая звездочка (*). Ты, наверное, помнишь, как мы описывали переменные Answern Titte. char *Answer; char *Title; Теперь эта команда присваивания не вызовет никаких проблем: Title = "Привет!"; При присваивании переменной типа char* массива символов вообще не создается, а устанавливается так называемое указание. Если говорить точнее, символ (*) определяет адрес в памяти компьютера. По указан¬ ному адресу начинается переменная Answer или Tit!e. Таким образом, мы описали два указателя. Разнообразие типов строк
Поскольку адрес хранящейся переменной указывается оператором (*), он называется оператором косвенной адресации.Я ставлю символ (*) непосредственно перед именем переменной, адрес ко¬ торой указывается. Можно выбрать одну из трех следующих воз- можностей: char * Answer; // выглядит как умножение char* Answer; // оператор char превращается в указатель char *Answer; // переменная Answer становится указателем Я бы совсем отказался от первого варианта. Второй вариант очень распространен — ясно, что используется не символ типа char, а тип* указателя массива символов. Поскольку третий вариант для описа¬ ния объектов применяется чаще, остановимся на массиве типа char. При объявлении необходимое место в памяти предоставляется автома¬ тически. Во многих других случаях при работе со строками программи¬ сту необходимо самому позаботиться о том, чтобы для строки было от¬ ведено место в оперативной памяти. Существенное отличие между тремя типами строк заключается в . следующем: • Для строки типа String место в памяти резервируется при объяв¬ лении. Работать с указаниями и другими операциями удобнее и проще, чем со строками типа String. • То же происходит и при использовании типа char [ ], то есть мас¬ сива символов. Однако доступ к переменным в этом случае не¬ сколько усложнен. • Для строки типа char* место в памяти при объявлении не резер¬ вируется. В этом случае длина строки не указывается, а ее ко¬ нец распознается по (невидимому) символу окончания, значе¬ ние которого соответствует нулевому символу (заметь, — не числу ноль; цифра 0 для компьютера имеет значение 48). 5. Библиотека компонентов
Для того чтобы при использовании типа char* была предоставлена ячейка памяти, действуют правила, отличные от правил обработки переменных типа String. В тех случаях, когда требуется применить этот тип, C++Builder, к сожалению, не всегда в состоянии помочь. Если ты хочешь поэкспериментировать со строками типа char*, будь готов к тому, что твой компьютер будет иногда «зависать». Программируя на языке С++, мы не можем полностью отказаться от использования типа char*. Но если мы станем часто использовать стро¬ ки, нам придется быть готовыми к трудностям, связанным с применени¬ ем этоготипа. При выполнении модуля StringToChar метода Bu11onC 1 iсk переменная ГехГобъявляется сразу же. Ей присваивается значение, ко¬ торое появляется во всех надписях: String Text = "Ответ"; Затем в циклесчетчике из содержимого переменной Text и трех пере¬ менных массива Агеа составляется выдаваемый ответ: for (int Nr=0; Nr<3; Nr++) if (Choice[Nr]) Text = Text + Area[Nr]; В конце эту строку необходимо превратить в цепочку символов класси¬ ческого типа. Видно, что переменная типа String тоже является объек¬ том, поскольку у нее есть метод с именем c_str. Он служит для превра¬ щения строк (Strings) в С-строки (С-Strings) (преобразование типа String в char*): Title = Text.c_str(); Преобразование осуществляется уже знакомым нам оператором String: Text = String (Titel); Почему C++Builder не использует более удобные строки? По¬ скольку операционная система Windows работает только со стро¬ ками типа char, C++Builder должен иметь в своем арсенале этот тип. Кроме того, существует огромное количество написанных на С++ программ, которые буквально нашпигованы объявлениями char. Разнообразие типов строк
И, наконец, программа C++Builder должна соответствовать, на¬ сколько это возможно, стандартам языка С++, которые считают- ся международными. Это значит, что созданные в C++Builder при¬ ложения без особых трудностей могут совершенствоваться почти во всех системах разработки С++. Выводы Нам потребовалось много терпения для того, чтобы завершить после¬ днюю версию проекта Камертон настроения. При этом мы познакоми¬ лись с целым рядом новых компонентов, свойств и методов: Check3ox RadioButton RadioGroup GroupBox CheckBoxClick RadioGroupClick, Radio3uttonClick Items ItemIndex Checked Индикатор с флажком (тип TCheckB6x), позволяющий выбрать параметры (в группе аналогичных индикаторов с флажками можно активировать несколько компонентов) Поле параметра (тип TRadioButton), позволяющее выбрать или отменить выбор параметра (в группе полей параметров разрешается активировать только одно поле) Группа переключателей (тип TRadioGroup) Групповая панель, объединяющая компоненты (например, индикаторы с флажками) Этот метод активируется щелчком переключателя мышью Этот метод активируется щелчком группы переключателей или одного переключателя мышью В строке находятся записи, отображаемые в окне списка, в редактируемом списке или группе переключателей В этом свойстве содержится номер строки, выбранной из списка TListBox, TComboBox и TRadioGroup Свойство компонентов TpheckBax и TRadioButton.-Если выбран параметр, приобретает значение true (— флажок/точка), иначе — false 5. Библиотека компонентов
Font Свойство, которым обладают все содержащие текст компоненты. Позволяет задавать параметры шрифта Новые слова: for ПРИ некотором условии (определенное количество повторений) выполняется тело цикла bool Тип переменной, принимающий только значение false (= неверно) или true (= верно) char, char* Тип переменной: отдельные символы или цепочки. Этот классический тип используется как в языке С, так и в Windows Копировать цепочку символов в строку типа char* (соответствует указанию значения) Тип переменной: цепочки символов удобной формы Преобразовать String в char* Преобразовать char* в String Квадратные скобки для массивов переменных (количество переменных в массиве или индекс) Оператор косвенной адресации, например, для цепочки символов (тип char**) Вопросы... 1. Чем отличается переключатель от индикатора с флажком? 2. Какие типы строк ты знаешь? ... и несколько задач 1. Замени в программе Гороскоп (см. главу 2) все кнопки (Button) на пе¬ реключатели (RadioGroup). 2. В палитре компонентов есть элемент RadioButton. В последней вер¬ сии программы Камертон настроения замени группу переключателей на шесть отдельных переключателей (и затем объедини их группо¬ вым окном GroupBox). Обрати внимание на то, что вместо метода RadioGroupClick появляются шесть методов RadioButtonClick. strcpy Str.ing c_str String [] Вопросы..
6. Проект €<Камертон настроения» В школе и на работе человек вовлекается в стремительный ритм совре¬ менной жизни. Подчас только сидя у компьютера можно найти успокое¬ ние (если не охватит игровая или программистская лихорадка). Для того чтобы выдержать предлагаемый цивилизацией темп, люди часто обращаются к психотерапевту. Но зачем тратить деньги на врачей? Поче¬ му бы не создать психотерапевтическую программу на компьютере? В этой главе ты научишься: • использовать компоненты Panel (ПднЕль) и ScrollBar (Полосд про- крутки); • работать с компонентами класса TStringList; • открывать из программы текстовые файлы; • использовать в программе текстовые данные с жесткого диска.
Две кнопки и несколько групповых окон Тебе понадобятся две кнопки (Button) и тр'и групповых окна (GroupBox). В предыдущей главе мы узнали, что групповое окно представляет собой рамку с надписью, поэтому его называют еще и рамочным окном. Две кнопки и несколько групповых окон Для того чтобы групповые окна располагались посередине формы, их следует выровнять. Продолжим работу с проектом Камертон настроения. Создадим дру¬ гую версию этой программы, которая будет лучше соответствовать выб¬ ранному названию. Первые версии проекта были, по сути, лишь имита¬ цией разговора, который может происходить в булочной или парикмахерской. Если система C++Builder открыта, выбери в меню FiLE команду NEW AppLiCATiON, чтобы начать работу над новым проектом. Увеличь рабочую область формы и попробуй запол^ть ее следующим образом:
4 Последовательно щелкни правой кнопкой мыши все груп¬ повые окна. При каждом щелчке открывается небольшое меню. Выбери в нем команду AuGN. ^ В диалоговом окне AuGNMENT в левом спис¬ ке переключателей HoRizoNTAL выбери ко¬ манду CENTER iN wiNDOw (не в списке VERTICALl). ^ Нажми кнопку ОК. Нам необходимо присвоить компонентам соответствующие надписи, ко¬ торые будут отображаться в форме: I Компонент Надпись (Caption) | GroupBox1 Это скажутебея GroupBox2 Это скажешь мне ты GroupBox3 Регулятор Button1 Повторить Button2 Готово Form1 Камертон настроения ^ Занеси в инспекторе объектов указанные в таблице фразы в со¬ ответствующие текстовые поля СВОЙСТВа CAPTION. Групповое окно приобретает некое назначение только в том случае, если в рамке размещена какая-либо информация. В одном окне могут 6. Проект «Камертон настроения
располагаться один или несколько компонентов. В нашем проекте в каж¬ дом групповом окне необходимо разместить по одному компоненту. Окно редактирования, панель и полоса прокрутки Начнем с самого верхнего группового окна. В качестве надписи исполь¬ зуется фраза Это скджЕшь мнЕ ты. Итак, нам нужен компонент для ввода текста. ■^ Найди в палитре компонентов значок Ешт и щелкни его мышью. В это окно ты будешь вводить фразу, которую ты пожелаешь доверить программе Камертон настроения. ^ Размести окно редактирования в первом групповом окне с надписью Это СКАЖЕШЬ МНЕ ТЫ. -> Для того чтобы окно редактирования расположилось посередине, его следует выровнять. Щелкни его правой кнопкой мыши. Выбери в меню команду AuGN. Активируй в списке HoRizoNTAL переключатель CENTER iN wiNDOw. Нажми кнопку ОК. Окно для ввода * Далее найди в палитре компонентов значок PANEL (ПднЕЛь) и щелкни его мышью. Окно редактирования, панель и полоса прокрутки
Наряду с компонентом Label система разработки C++Builder имеет еще одно поле для отображения текста — компонент Panel, который не просто является меткой, но еще может принимать и различные пластичные формы. Чтобы отличать этот компонент от Label, назовем его панелью. На ней позже разместится фраза, с помощью которой Камертон настроения будет придавать тебе уверенность. ■> Размести панель во втором групповом окне с надписью Это скджу ТЕБЕ я. * Выровняй панель по центру, выбрав в диалоговом окне AuGNMENT пара¬ метр CENTER IN WINDOW (в СПИСКе HORIZONTAL). Поле для вывода ответа Как и другие компоненты, эти элементы имеют разные названия. Не буду их от тебя скрывать: компонент вгоирВох называют так¬ же группирующим окном, а Panel — обслуживающим окном или обслуживающей панелью. Перейдем к следующему новому элементу из коллекции компонентов — к полосе прокрутки. Ты, наверняка, знаком с этим элементом по тек¬ стовым редакторам. В окне редактора имеются две полосы прокрутки: вертикальная и гори¬ зонтальная. С помощью полосы прокрутки можно листать текст, кото¬ рый полностью не помещается в окне. Данный компонент необходим для «регулирования» настроения. *^ Найди в палитре компонентов значок ScROLLBAR и щелкни его мышью. 6. Проект «Камертон настроения»
С помощью компонента Scro!IBar, то есть полосы прокрутки, мы изменим ответ, если нам не понравится тот, что выдаст «Камертон насторения». 4 Размести в третьем групповом окне с надписью РЕГУЛятор отвЕтов поло¬ су прокрутки. ^ Расположи полосу прокрутки посередине, выбрав в диалоговом окне ALIGNMENT параметр CENTER IN WINDOW. Исчез один из следующих компонентов: окно редактирования, па¬ нель или полоса прокрутки. На экране видно только групповое окно. Причина может заключаться в том, что групповое окно зак¬ рыло собой другие компоненты. • Щелкни правой кнопкой мыши соответствующее групповое окно. • Выбери в меню команду SEND То Вдск. Компонент должен снова стать видимым. Если этого не произош¬ ло, то он больше не существует, и его необходимо создать заново. Окно редактирования, панель и полоса прокрутки
Для того чтобы при запуске программы окна Это скджЕШь мнЕ ты и Это скАЖУ ТЕБЕ я не содержали какого-либо текста, необходимо удалить за¬ данные начальные значения для окна редактирования (Edit) и панели (Panel). 4 Щелкни мышью окно редактирования и удали в инспекторе объектов в свойстве ТЕХТ текст Editl. * Щелкни мышью окно редактирования и удали в инспекторе объектов в окне CAPTiON текст Panell. Продолжим работу над внешним видом панели. 4 Установи в инспекторе объектов для свойства BEVELOuTER (ВнЕшний скос) величину BVLOWERED (Нижняя ГРАНИЦА CKOCA), а свойству BEVELlNNER (ВНУТ- РЕНиий скос) задай evNoNE (Скос отсутствуЕт). Свойства BEVELlNNER и ВЕУЕЕОитЕРОпределяютформу внутренней и внеш¬ ней границы индикаторной панели. Попробуй выбрать другие установки и посмотри, как при этом меняется вид панели. Может быть, тебе боль¬ ше понравится другое сочетание установочных параметров. Подготовка перед запуском программы -> Сохрани проект под именами KLEMP5.MAK и BEFUND1.CPP. Если сейчас ты запустишь программу, то другой возможности, кроме как ввести в окно редактирования какой-нибудь текст, поиграть с полосой прокрутки и нажать две кнопки, у тебя не будет. Это неудивительно, так как мы не описали ни одного метода. Для выполнения программы Ка¬ мертон настроения необходим материал, который она будет обрабаты¬ вать. А поскольку ей уже при запуске нужна информация для работы, подготовкой соответствующего исходного материала займется проце¬ дура PormCreate: 6. Проект «Камертон настроения»
void fastcall TForml::FormCreate(TObject *Sender) { randomize (); Answer[0] = *Гм...’; Answer[l] = "Вот это здорово!"; Answer[2] = "Ты только посмотри!”; Answer[3] = "Что мне на это ответить?"; Answer[4] = "Действительно?"; Answer[5] = "Значит, такие у тебя дела."; Answer[6] = "У тебя проблемы."; Answer[7] = "Это я могу понять."; Answer[8] = "Мне нечего сказать."; Answer[9] = "Да-а-а..."; } Сначала запускается генератор случайных чисел, затем массив тексто¬ вых переменных заполняется имеющими смысл выражениями. 4 Дважды щелкни мышью форму и введи операторы присваивания в окне редактора кода (BEFUND1.CPP, KLEMP5.MAK). Можешь придумать свои фразы, если тебе не нравятся мои, и ввести их. Естественно, необходимо объявить переменную Answer, константу Мах и переменную для счетчика Nr. *> Добавь к объявлениям в окно редактора кода следующие строки (BEFUND1.CPP): const int Max = 10; String Answer[Max]; int Nr; Ответ €«Камертона настроения» Для того чтобы при нажатии кнопки Готово Камертон настроения выда¬ вал ответ, необходимо расширить соответствующий метод ButtonClick. Ответ «Камертона настроения»
т> Дважды щелкни мышью правую кнопку (Готово) и введи следующие ко¬ манды: void fastcall TForml::Button2Click(T0bject *Sender) { Nr = random (Max); Panell->Caption = Answer[Nr]; } Создается случайное число от 0 до 9 (объявленное максимальное значе¬ ние —1). Компоненту Panel1 в качестве надписи (CAPTiON) присваивается соответствующая фраза из массива переменных Answer. 4 Запусти программу. Введи информацию в окно редактирования. При щелчке мышью кнопки Готово выводится одна из фраз программы Камертон настроения. Если ты захочешь, чтобы игра начиналась снова, тебе придется несколь¬ ко усложнить программу. Перед введением нового предложения сна¬ чала необходимо удалить старое. Эту операцию доверим кнопке По¬ вторить: void fastcall TForml::ButtonlClick(TObject *Sender) { Panell->Caption = ""; Editl->Text = ”"; Editl->SetFocus (); } Первые две команды удаляют метку и введенный текст. А метод SetFocus снова устанавливает фокус на окно редактирования. В процессе выполнения программы метод SetFocus устанавливает фо¬ кус на один из компонентов формы. Если я щелкну мышью другой ком¬ понент, фокус соответственно переместится. Для ToYo чтобы окно редактирования уже при запуске программы име¬ ло фокус, выполни следующую установку в инспекторе объектов. 6. Проект «Камертон настроения»
* Щелкни мышью форму, а затем нажми кнопку с изоб¬ ражением треугольника в свойстве AcTiVEC0NTR0L. ♦ Щелкни мышью в меню строку EDIT1. 4 Сохрани проект еще раз и запусти программу. Проверь, что происходит при нажатии кнопок Повторить и Готово. Тебе, конечно же, не терпится узнать, что делает полоса прокрутки и что такое «Регулятор ответов»? В зависимости от положения ползунка на расположенной на панели по¬ лосе прокрутки появляется тот или иной ответ. Назовем операцию пере¬ мещения ползунка регулированием. Как «оживить» регулятор? Добавим в метод команды, реагирующие на передвижение ползунка. •♦Дважды щелкни мышью полосу прокрутки и добавь в метод ScrollBarlChange следующие команды (BEFUND1.CPP): void fastcall TForral::ScrollBarlChange(TObject *Sender) { Panell->Caption = Answer[ScrollBarl->Position]; } Заметь, что и в этот раз метод называется не Click, а ScrollBarlChange, поскольку речь идет не о щелчке мышью, а об изменении состояния по¬ лосы прокрутки. В свойстве ScrollBarl->Position сохраняется текущее положение пол¬ зунка полосы прокрутки. Таким образом передается текст ответа, кото¬ рый затем появляется на панели. Полоса прокрутки Полоса прокрутки
Для того чтобы значение ScrollBari. Position соответствовало положе¬ нию ползунка, необходимо определить нижнюю и верхнюю границу зна¬ чений: ScrollBarl->Min = 0; ScrollBarl->Max = Max - 1; Итак, диапазон перемещения ползунка начинается с нуля и заканчивает¬ ся, не достигнув значения заданной константы Мах (в данном случае 9). ^ Добавь обе команды в метод TForml->FormCreate (BEFUND1.CPP). 4 Сохрани проект и затем запусти его. Новые ответы Если тебе перестали нравиться формулировки ответов, их можно по¬ менять. При увеличении константы Мах растет число вариантов отве¬ тов. Для более удобного способа введения новых ответов воспользуемся текстовым редактором и сохраним их в обычном текстовом файле. Ка¬ мертон настроения будет выбирать из него фразы и превращать их в от¬ веты. Для воплощения задуманного необходимо отредактировать текущую версию программы. Начнем с метода FormCreate, который мы сократим до следующего варианта: void fastcall TForml::FormCreate(TObject *Sender) { - 6. Проект «Камертон настроения»
randomize (); Answer = new TStringList; Answer->LoadFromFile (DateiName); ScrollBarl->Min = 0; ScrollBarl->Max = Answer->Count - 1; Заметь, что исчезли обращения к массиву Answer. Переменная Answer получает новую, на первый взгляд необычную задачу, которая, однако, легко объяснима. В этом случае я отказался от использования массива строк, и вместо него объявил новый объект, присвоив ему освободив¬ шееся имя Answer. TStringList *Answer; Оператор TStringList вводит очень удобный класс — список строк. Именно он нам и нужен. Он похож на массив строк, но более гибкий и легко управляемый. Одним объявлением нам не обойтись, так как только после добавления следующего обращения (в метод FormCreate) список получает место в памяти: Answer = new TStringList; Место в памяти резервирует оператор new. Он обеспечивает сохранение в оперативной памяти компьютера текстов, принятых в объявленный спи¬ сок. Эта небольшая звездочка (*) постоянно нас преследует. Мы уже встречали ее при объявлении формы Form1 и несколько раз в спис¬ ке параметров: TForml *Forml; // объявление формы (TObject *Sender) // параметр в методе, например, объекта TForm На этот раз мы использовали оператор косвенной адресации для создания указателя на список строк. Новые ответы
Для одного объекта (типа TStringList) объявляется один указатель. Ты, наверное, помнишь, что при объявлении в оперативной памяти компьютера определяется только один адрес. И только после сле¬ дующего обращения, начиная с этого адреса, для переменных ре¬ зервируются ячейки памяти. ?StringLi.st *Answer; Answer = new ?StringList; В следующей команде мы можем в полной мере насладиться удобством использования объекта Answer. Answer->LoadFromFile (FiieName); Действия, которые в предыдущей версии осуществлялись несколькими командами, выполнит метод LoadFromFiie. Имя файла необходимо объя¬ вить в блоке объявлений как константу: const String FileName = "Answer.txt"; Это имя файла, в который записываются ответы компьютера. Можно указать и другое имя. Я предлагаю использовать именно этот файл, по¬ скольку в нем уже записаны примеры ответов. Строки из списка Для списка строк нам не понадобится константа Мах. Однако свойство полосы прокрутки МАХ должно получить новое значение: ScrollBarl->Min = 0; ScrollBarl->Max - Answer->Count - 1; 6. Проект «Камертон настроения»
Свойство CouNT содержит текущее количество строк в списке ответов. Поскольку подсчет начинается с 0, для определения значения свойства МАХ необходимо из значения Соиыт вычесть 1. Такие же изменения необходимо внести и в методы TForml:: Button2Click и TForml::ScrollBarlChange: void fastcall TForml::Button2Click(T0bject *Sender) { Nr = random (Answer->Count); Panell->Caption = Answer->Strings[Nr]; } // void fastcall TForml::ScrollBarlChange(TObject *Sender) { Panell->Caption = Answer->Strings[ScrollBarl->Position]; } Случайное значение для переменной Nr создается на основе Answer-> Count, а не Мах, так как свойство Соиыт заменило переменную Мах, ко¬ торую мы использовали в последней версии программы Камертон на¬ строения. Поскольку доступ к строкам списка диагнозов с помощью индекса не¬ возможен, добраться до них нам поможет свойство STRiNGs: Strings[Nr] С помощью этой команды мы выбираем из списка конкретную строку, обозначаемую индексом Nr. То же самое происходит и в методе ScrollBarChange. В нем индекс определяется положением ползунка по¬ лосы прокрутки: Strings[ScrollBarl->Position] 4 Измени соответствующим образом исходные тексты всех методов, кро¬ ме TForml:: ButtonlClick. Сохрани новый проект (в файлах KLEMP6.MAK и BEFUND2.CPP). 4 Запусти программу Камертон настроения. Строки из списка
Тебе не совсем понятно, как работает программа? • Введи в окне редактирования любой текст (можноничего не скрывать, так как оценки выставляться не будут). • Нажав по окончании ввода кнопку Готово, ты увидишь на пане¬ ли Это скАЖУ ТЕБЕ я ответ, который не обязательно будет соот¬ ветствовать введенному тексту. • При нажатии кнопки Повторить фраза удаляется из окна редак¬ тирования, и ты можешь ввести новый текст (комментарий к от¬ вету или что-нибудь другое). • Если ответы программы покажутся тебе не очень подходящими, воспользуйся полосой прокрутки и выбери правильный ответ. У тебя есть большой выбор вызываемых из текстового файлафраз. В текстовом редакторе можно менять, удалять или добавлять пред¬ ложения по своему вкусу. Преимущество такого способа работы заключается в том, что для изменения выводимых ответов нам достаточно простого текстово¬ го редактора, например редактора Windows Notepad. Важно лишь после изменения ответов сохранить файл в текстовом формате с расширением TXT. Если ты не хочешь делать изменений в текстовом файле, а жела¬ ешь создать один или несколько новых файлов такого типа, со¬ хранить их можно в той же папке, но под другими именами. Имя нового файла необходимо указать в исходном тексте програм¬ мы проекта, а именно в объявлении константы FHeName (имя фай¬ ла), например: const String FileName = "MyStaff.txt"; 6. Проект «Камертон настроения»
Приема нет Есть еще поле для совершенствования программы. К ее текущей вер¬ сии на всякий случай можно добавить следующее условие: если файла с ответами не существует, выполнение программы останавливается сооб¬ щением об ошибке. Снова обратимся к структуре передачи управления, знакомой нам по главе 3: try { Answer->LoadFromFile (Fi.leNarne) ; } catch (...) { Answer->Add ("Приема нет"); } Оператор try сначала пытается загрузить файл. Если это не удается, всту¬ пает в действие блок catch. Метод Add добавляет в список строк одну-единственную фразу: «При¬ ема нет». Ты можешь вместо нее ввести какую-нибудь другую фразу, например «Кабинет закрыт». Программа продолжает работать благодаря использованию этой коман¬ ды. Если хочешь, определи несколько разных ответов на случай отсут¬ ствия файла с ответами, например: Answer->Add ("Приема нет"); Answer->Add ("Кабинет закрыт"); Answer->Add ("Помоги себе сам!"); Значение свойства Соиыт увеличивается автоматически. 4 Добавь в метод ?Forml:: FormCreate конструкцию try-catch (KLEMP6A.MAK и BEFUND2A.CPP). Исходный текст станет выглядеть следующим об¬ разом: void fastcall TFormi::FormCreate(TObject *Sender) { randomize (); Приема нет
Answer = new TStringList; try { Answer->LoadFromFile (FileName); } catch (...) { Answer->Add ("Приема нет"); Answer->Add ("Кабинет закрыт"); Answer->Add ("Помоги себе сам!1'); } ScrollBarl->Min = 0; ScrollBarl->Max = Answer->Count - 1; } ^ Для проверки можно временно поменять имя файла, например, на Diagnosy. txt. Убедись, что в диалоговом окне ENVIRONMENT OPTiONS меню OPTIONS СНЯТ флажок BREAK ON EXCEPTION. Протокол сеанса терапии Для того чтобы познакомиться со способом сохранения списка строк в файле, расширим далее возможности программы. Определим еще один список строк и одновременно объявим еще одну константу для сохра¬ нения имени файла: const String CollectName = "PsychoX.txt"; TStringList *Psycho; Список Psycho должен собирать всю информацию, передаваемую про¬ грамме через окно редактирования, и по окончании сеанса сохранять ее в файле PSYCHOX.TXT. Вместо X можно ввести число собираемых файлов. В методе FormCreate необходимо объявить переменную Psycho так же, как мы объявляли переменную Answer. Это действие выполняет следующая команда: Psycho = new TStringList; 6. Проект «Камертон настроения
Для того чтобы собирались все введенные в окне редактирования тек¬ сты, необходимо дополнить метод TFormi: :Button2Click. Команда Psycho->Add (Editl->Text); служит для того, чтобы каждый раз при нажатии кнопки Готово введен¬ ный в окно редактирования текст записывался в список строк. (Если до нажатия кнопки текст не был введен, соответствующая строка остается пустой.) Наконец, сохрани созданный список строк на жестком диске. Лучше всего сделать это в конце сеанса, когда при нажатии кнопки с крестиком (в правом верхнем углу формы) вступает в действие метод FormClose. Если в него ввести еще одну команду, она будет выполняться до полно¬ го завершения программы. *> Щелкни мышью рабочую область формы, а затем в инспекторе объектов закладку EvENTS. ♦ Дважды щелкни мышью событие ONCtosE (ЗдкрытиЕ Формы). В окошке справа от имени события появляется имя соответствующего метода FormClose. В окне редактора кода одновременно отображается текст программы метода ?Forml: :ForraClose. Протокол сеанса терапии 6-4109
4 Введи необходимый текст: void fastcall TForml::FormClose(TObject *Sender, TCloseAction &Action) try { Psycho->SaveToFile (CollectName); } catch (...) { Application->Message3ox ("Ошибка при сохранении файла!”, "Внимание!", 0+48); } Метод SaveToFile обеспечивает сохранение списка строк в файле на жестком диске. Если попытка не удается, структура try-catch выдает со¬ ответствующее сообщение об .ошибке. ^ Сохрани созданную версию программы (KLEMP7 и BEFUND3) и про¬ верь, как она работает. Для того чтобы проверить, правильно ли обра¬ батываются ошибки, можно временно в методе FormClose, например, до¬ полнить значение константы CollectName обозначением дисковода A:\PSYCHOX.TXT. (Флажок BREAK ON EXCEPTioN необходимо снять.) Наряду с оператором new, который служит для создания объектов, существует оператор delete для их удаления. При удалении осво¬ бождается место, занимавшееся объектом в оперативной памяти. В метод FormClose мы могли бы добавить следующие команды: delete Answer; delete Psycho; Поскольку C++Builder по окончании работы программы удаляет все объекты и освобождает использовавшуюся ими память, эти строки добавлять не нужно. Однако данные команды могут потре¬ боваться, если возникнет необходимость удалить объект в процессе выполнения программы. (Советую придерживаться следующего правила: все созданные оператором new объекты удалять операто¬ ром delete.) 6. Проект «Камертон настроения»
Выводы Пожалуй, можно сделать небольшой перерыв! Сквозь компоненты уже не видно текста. ForraClose OnClose SetFocus ActiveControi Panel ScroliBar ScrollBarChange StringList LoadFromFile SaveToFile Count Strings [Index] Add Этот метод выполняет действия, которые должны совершаться непосредственно перед окончанием работы программы Это событие происходит непосредственно до закрытия формы Этот метод присваивает текущий фокус компоненту Свойство объекта TForm. Оно содержит имя компонента, на который установлен фокус после запуска программы Панель (тип TPanel), на которой отображается текст. Ее можно опустить или приподнять относительно уровня рабочей области формы Полоса прокрутки (тип TScrollBar), с помощью которой, например, можно перемещаться по окну. Она используется также для выбора значений с помощью ползунка Этот метод активируется перемещением ползунка полосы прокрутки или с помощью кнопок полосы прокрутки Список строк (тип TStringList) Функция, которая открывает имеющийся текстовый файл и загружает его содержимое в список строк Функция, сохраняющая список строк в текстовом файле Свойство объекта TStringList, соответствующее количеству строк в списке Свойство объекта TStringList. Содержит строку, номер которой соответствует значению индекса Этот метод объекта TStringList добавляет в список новую строку Итоги 6-
Мы познакомились с несколькими новыми операторами языка С++: пот Создает новый объект и резервирует для него место в оперативной памяти delete Удаляет объект и освобождает место в памяти * Объявляет указатель, например, на объект (или, в общем случае, на переменную) Вопросы... 1. Чем отличается метка (Label) от панели (Panel)? 2. Что происходит с компонентом, когда ему присваивается фокус? 3. Как можно открывать и сохранять текстовые файлы из программы? 4. Какие два действия необходимо выполнить, чтобы получить возмож¬ ность использовать новый объект? ... и несколько задач 1. Создай программу, которая выводит значение от 0 до 100 в зависи¬ мости от положения ползунка полосы прокрутки. 2. Расширь возможности одной из версий программы, которая собира¬ ет введенную информацию (Editl->Text) и случайные ответы компь¬ ютера (Panell->Caption), и сохраняет все собранные данные в общем файле. 6. Проект «Камертон настроения»
7. Меню и системные диалоговые окна Умение программы Камертон настроения загружать текст и сохранять его очень полезно. Но оно позволяет открывать файл с записанными ответами и сохранять диагнозы только в заранее определенном файле. Мне этого недостаточно, я желаю большего! Поскольку ты придерживаешься такого же мнения, давай, дополним Камертон настроения таким образом, чтобы ты мог сам определять, ка¬ кой файл открыть, и под каким именем сохранять введенный текст. В этой главе ты научишься: • вводить в программу меню; • пользоваться диалоговыми окнами OPENDlALOG, SAVEDlALOG и pRINTDlALOG; • открывать и сохранять любые текстовые файлы; • работать с компонентом RichEdit; • распечатывать тексты.
Меню «Камертона настроения» Для начала нам потребуется меню, в котором мы можно было указать, собираемся ли мы открыть файл или сохранить что-либо в нем. ^ Растяни форму книзу. Если хо¬ чешь, немного передвинь компо¬ ненты: начерти мышью рамку вокруг всех (!) объектов, выделив их таким образом. Щелкни один из них мышью, и, удерживая на¬ жатой ее кнопку, перемести вы¬ деленный компонент книзу. ^Найди в палитре компонентов зна¬ чок MAINMENU (ГЛАВ- HOE МЕНЮ) и щелкни его мышью. 4 Щелкни мышью любое место ра¬ бочего поля формы. В ней появ¬ ляется указанный в палитре ком¬ понентов значок. !^Камертон настроения II При связывании меню и формы появляется небольшой значок. Само меню мы заполним собственными командами. 4 Дважды щелкни мышью значок на форме. От¬ крывается окно конструктора меню. 7. Меню и системные диалоговые окна
В конструкторе меню введен первый пункт меню Фдйл. В инспекторе объектов кое-что тоже изменилось. Этот текст появляется в текстовом поле свойства Сдртюы. Данному пункту меню присваивается имя File1. (А если ты посмотришь на верхнее окно инспектора объектов, то уви¬ дишь, что для File1 в качестве типа указано TMenuItem). ^Дополни меню следующими пунктами: Открыть, Сохрлнить, ПЕЧАТЬ, Вы¬ ход. Ты сделал ошибку или ввел на один пункт больше? Это легко мож¬ но поправить: • щелкни мышью название команды; • если ты хочешь удалить текст, нажми клавишу Del; • если ты хочешь изменить текст, сделай это в инспекторе объек¬ тов в текстовом поле свойства CAPTiON. Меню «Камертона настроения»
А если тебе захочется что-нибудь добавить в меню, щелкни пра¬ вой кнопкой мыши текст, перед которым должна появиться новая надпись. В меню выбери команду lNSERT. Появляется местодля вво¬ да нового текста. Меню готово? Тогда конструктор меню можно закрыть. ■^ Нажми кнопку X в правом верхнем углу окна. Вид формы несколько изменился. Сразу под панелью заголовка фор¬ мы появилась одинокая запись Фдйл. А в инспекторе объектов для объек¬ та MainMenul указан тип TMainMenu. ■^Сохрани свой проект (KLEMP8.MAK и BEFUND4.CPP) и запусти про¬ грамму. ШШ 7. Меню и системные диалоговые окна
В процессе ее выполнения меню может быть открыто (если щелкнуть его мышью). Но это пока все, что может выполнить созданная нами про¬ грамма. Не хватает методов, которые реагировали бы на щелчки мы¬ шью, — событий OnClick. Два диалоговых окна Начнем с команд меню Открыть и Сохрлнить. Что должно происходить при их выборе? • При указании команды Открыть должно открываться диалоговое окно, в котором мы могли бы выбрать необходимый текстовый файл и заг¬ рузить его. • При выборе команды Сохрднить нам должна быть предоставлена воз¬ можность задать в диалоговом окне имя файла и сохранить файл. Эта затея на первый взгляд кажется трудновыполнимой. Однако в на¬ шем распоряжении есть два объекта, которые выполнят за нас всю ра¬ боту — OpenDialog (диалоговое окно ОткрытиЕ ФАйлд) и SaveDialog (ди¬ алоговое окно СохРАНЕНИЕ). Нам только остается разыскать их и вставить в форму. 4 Щелкни мышью в палитре компонентов закладку DiALOGS (ДидлоговыЕ окнд). Два диалоговых окна
На этой закладке нам предлагается целый ряд компонентов для органи¬ зации диалоговых окон, например для печати, поиска и замены текста, а также для открытия и сохранения файлов. ■^ Найди значок OpENDiALOG и щелкни его мышью. Затем щелкни мышью форму (например, рядом со значком меню). 4 Найди значок SAVEDiALOG и щелкни его мышью. Затем щелкни мышью форму. Итак, в нашей форме установлена связь с объектами типа TOpenDialog и TSaveDialog. Таким образом, мы можем обратиться к ним в окне редактора кода при создании методов для команд меню Открыть и Сохрднить. Сначала необходимо ввести несколько ограничений, для того чтобы в диалоговом окне появлялись только текстовые файлы. ^ Щелкни мышью значок объекта OpENDiALOG на форме. 7. Меню и системные диалоговые окна т> Щелкни мышью в инспекторе объектов небольшую кнопку в поле ввода свойства FiLTER (Фильтр).
Открывается редактор фильтров (Filter Editor). 4 Введи в левую часть таблицы редактора фильтров текст ТЕКСТОВЫЕ ФАй- лы (*.тхт). В правую часть таблицы вводится фильтр *.тхт, который оп¬ ределяет в диалоговом окне ОткрытиЕ докумЕнтА отображение имен фай¬ лов только с расширением .TXT. А теперь еще раз повторим все эти операции для диалогового окна Co- ХРАНИТЬ. + В форме щелкни мышью значок компонента SaveDialog. Затем щелк¬ ни мышью в инспекторе объектов небольшую кнопку в поле ввода свой¬ ства FlLTER. 4 Введи в редакторе фильтров в левую частьТЕКСтовыЕ ФАйлы (*.тхт), а в пра¬ вую — *,TXT. Последний фильтр определяет, что в диалоговом окне CoxPA- нить КАК будут отображаться только имена файлов с расширением .TXT. Оба объекта необходимо разместить в соответствующих методах меню. Два диалоговых окна •^ В меню Фдйл (в левом верхнем углу формы) выбери команду Открыть.
4 Укажи команду Открыть. 4 Введи в инспекторе объектов в текстовое поле свой¬ ства ЫдмЕ имя OPEN 1 (используем для обозначения метода пункта меню «Открыть» английское слово «Ореп»). Открытие и сохранение Итак, метод, позволяющий открывать файлы, получил осмысленное имя: void fastcall TForml::OpenlClick(TObject *Sender) { OpenDialogl->Execute (); Answer->LoadFromFile (OpenDialogl->FileName); ScrollBarl->Max = Answer->Count - 1; } Сначала активируется диалоговое окно для открытия файла: OpenDialogl->Execute (); 7. Меню и системные диалоговые окна Ты оказываешься в окне редактора кода во фрагменте программы со¬ ответствующего метода. Но подожди! Обрати внимание, что объект на¬ зывается TForml: :N2Click. Русскоеслово «Открыть»системаС++ВшЮег заменила на N2, так как язык С++ не использует для обозначения пере¬ менных, объектов и методов русские буквы. ^Дважды щелкни мышью в форме значок меню. Открывается конструк¬ тор меню.
Оператор Execute не только открывает диалоговое окно, но и принима¬ ет имя выбранного файла. Имя файла сохраняется в переменной OpenDialogl->FileName и может быть передано методу LoadFromFiie как параметр: Answer->LoadFromFile (OpenDialogl->FileName); После того как файл загрузится, приведем в соответствие значение пе¬ ременной ScrollBarl->Max: ScrollBarl->Max = Answer->Count - 1; Следующий метод, которым мы займемся, — метод сохранения введен¬ ных данных: void fastcall TForml::SavelClick(TObject *Sender) { SaveDialogl->Execute (); Psycho->SaveToFile (SaveDialogl->FileName); } Сначала активируется окно для сохранения: SaveDialogl->Execute () ; Затем содержимое списка строк сохраняется в соответствующем файле: Psycho->SaveToFile (SaveDialogl->FileName) На этом можно было бы и закончить, но мы стремимся как можно лучше усовершенствовать программу. Нам необходимо учесть еще две возмож¬ ные ситуации: • Что произойдет, если не выбран никакой файл? • Что произойдет, если файл не может быть открыт или сохранен? В нашем распоряжении имеются структуры передачи управления i f и try-catch. Воспользуемся ими. В результате методы приобретают сле¬ дующий вид: Открытие и сохранение
void fastcall TForml::OpenlClick(TObject *Sender) { try { if (OpenDialogl->Execute ()) Answer->LoadFromFile (OpenDialogl->FileName); ScrollBarl->Max = Answer->Count - 1; } catch (...) { Application->MessageBox ("Ошибка при открытии файла!", "Внимание!11, 0+48); } } // void fastcall TForml::SavelClick(TObject *Sender) { try { if (SaveDialogl->Execute ()) Psycho->SaveToFile (SaveDialogl->FileName); } catch (...) { Applicat ion->MessageBox ("Ошибка при сохранении файла!", "Внимание!", 0+48); } } Функции OpenDialog и SaveDialog превратились в условия структуры if. Это произошло потому, что оператор Execute возвращает значение рав¬ ное либо true, либо false. Если вводится или выбирается имя файла, то Execute возвращает значение true. В противном случае — false. Таким образом, поставленная задача решается очень просто: if (OpenDialogl->Execute ()) Psycho->SaveToFile (SaveDialogl->FileName); if (SaveDialogl->Execute ()) Answer->LoadFromFile (OpenDialogl->FileName); 7. Меню и системные диалоговые окна
^Укажи в меню Фдйл команду Сохрднить. Введи текст для метода TForml::SavelClick. Курс «похудания» Из-за того, что блок программы, в котором открывается файл с ответами, переместилсяввметодТРоой:: Open 1С1 i с k, код метода TForml: :FormCreate стал короче: void fastcall TForml::FormCreate(TObject *Sender) { randomize (); Answer = new TStringList ; Psycho = new TStringList; Answer->Add ("Приема нет") ; ScrollBarl->Min = 0; ScrollBarl->Max = Answer->Count - 1; Это означает, что в начале сеанса приема нет, пока ты не откроешь в меню Фдйл текст с ответами. По окончании работы программы автома¬ тическое сохранение избыточно, поэтому данные строки перекочевали в метод TForml:: SavelClick. Таким образом, можно полностью отказать¬ ся от использования метода TForml:: FormClose. Курс «похудания» А если позволить себе быть еще ленивее, можно использовать структу¬ ру try-catch: ► Укажи в меню Фдйл команду Открыть. Введи текст для метода TForml::OpenlClick.
-> Измени соответствующим образом метод FormCreate (BEFUND4.CPP). ■> Удали только строки, расположенные между фигурными скобками, а не весь метод FormClose! Удалением методазаймется C++Builder. Онауве- ренно справляется с этим действием, если между фигурными скобками в методе не остается ни одной команды. -> Сохрани полученные результатывфайлах KLEMP8.MAK и BEFUND4.CPP и запусти программу. Если ты сохранишь введенные данные, ты сможешь использовать файл с ответами в качестве диагнозов. Вывод ответов на печать Если у тебя нет принтера, пропусти этот раздел. Если же он у тебя есть, мы попытаемся научить Камертон настроения пе¬ чатать. У тебя появится возможность получить результаты «терапии» на бумаге. И опять на помощь к нам приходит палитра компонентов прило¬ жения C++Builder: ■>Открой палитру с компонентами диалоговых окон. Найди значок PRiNTDiALOG (ПЕЧдть) и щелкни его мышью. Щелкни мышью форму (на¬ пример, рядом с другими значками диалоговых окон). ^Укажи в меню формы Флйл команду ПЕЧАть. Что должен содержать метод Click для печати? Сначала строку, кото¬ рая вызывала бы диалоговое окно печати: PrintDialogl->Execute (); Ты можешь поменять настройки принтера в диалоговом окне Ртыт (ПЕ- 4ATb), нажав кнопку PROPERTIES (Свойствд). 7. Меню и системные диалоговые окна
Другие настройки в диалоговом окне Ртыт не влияют на работу нашей программы. Мы распечатаем в одном экземпляре файл с ответами. (Для решения поставленной нами задачи остановимся на простейшем вари¬ анте выбора параметров.) Итак, твой принтер включен и готов к печати, бумага вставлена, но объект PrintDialogl пока не в состоянии выполнить задачу. К сожалению, у переменной Answer класса TStringList нет метода для печати. Нам необходимо найти объект, который мог бы записать текст и затем переслать его на принтер. Ты, наверное, сразуже вспомнил о TEdit. Неплохая идея, но, к сожалению, объекттипа?Еб1ь может осилитьтолько одну текстовую строку. К тому же этот компонент не способен передать ее на принтер. Однако в этом семействе компонентов есть класс, который способен спра¬ виться с поставленной нами задачей. Нас осчастливит объект TRichEdit. Компонент RichEdit является многострочным окном редактирования операционной системы Windows, которое может сохранять и отобра¬ жать обычные строки и даже текст в определенном формате. Он назы¬ вается Rich Text Format. Файлы такого формата имеют расширение .RTF, поэтому данный компонент также называют RTF-окном. Поскольку большинство текстовых редакторов работают с указанным форматом, объект RichEdit удобно использовать. Записанные в этом окне тексты читаются и в редакторе Word. Вывод ответов на печать
-^ Щелкни мышью в палитре компонентов закладку WiN95, для того чтобы перейти на другую страницу палитры. -> Щелкни мышью значок RiCHEDiT. У нас возникла небольшая проблема. Нам необходим компонент RichEdit, но мы не хотели бы его видеть в процессе выполнения программы. В большинстве случаев он используется для отображения текста. Нам не¬ обходимо разместить этот компонент так, чтобы он был невидим: ■> Размести компонент на форме, например, рядом со значками меню и диалоговых окон. ^Используя свойство FoNT, установи размер шрифта для печати 10-12. 7. Меню и системные диалоговые окна > В инспекторе объектов задай свойству VisiBLE (Види¬ мый) значение false. Таким образом, RTF-окно при за¬ пуске программы не будет отображаться на экране.
Вывод на печать ответов осуществляется двумя командами: RichEditl->Text = Answer->Text; RichEditl->Print ("Ответ"); В первой строке свойство Text принимает значение RichEditl, то есть содержание всего списка строк. А во второй строке метод Print пере¬ сылает этот текст на принтер. В качестве параметра метод Print может использовать имя текста или название, присваиваемое всему выводи¬ мому тексту. (Параметр на печать не выводится.) Теперь у нас есть все необходимое для печати. В этом методе мы также используем функцию Execute в качестве условия структуры if, на слу¬ чай если ты не захочешь выводить данные на печать и нажимаешь для этого в диалоговом окне ПЕЧАТЬ кнопку ОтмЕнд или кнопку X. На всякий случай нам следует также добавить структуру try-catch. 4 Введи для метода TForml:: Print lClick следующий текст (BEFUND4A.CPP): void fastcall TForml::PrintlClick(TObject *Sender) { try { if (PrintDialogl->Execute ()) { RichEditl->Text = Answer->Text; RichEditl->Print ("Ответ"); ) } catch (...) { Application->MessageBox ("Ошибка при печати файла!", "Внимание!", 0+48); } } r^ Сохрани проект в файлах KLEMP8A.MAK и BEFUND8A.CPP, и затем запусти программу. Поупражняйся в открытии, сохранении, загрузке и распечатке файла. Вывод ответов на печать
Опрос на всякий случай Одна строка нашего меню Фдйл осталась незадействованной — та, что завершает работу программы. Необходимо предусмотреть возможность прекращать работу иначе, чем закрытием формы. Способ, используе¬ мый в профессиональных программах, должен быть и нам по зубам. Можно было бы закрыть программу простой командой Close: void fastcall TForml::EndlClick(TObject *Sender) { Close (); } Но мы постараемся добиться того, чтобы перед завершением работы про¬ грамма еще раз спрашивала нас, хотим ли мы еще что-нибудь сохранить. Сначала опишем переменную типа bool: bool Safety; При запуске переменная Safety(5e30nacH0cmb) получаетзначение false. Оно означает, что текущие введенные данные пока не сохранены. Эта команда добавляется в метод FormCreate: Safety = false; После каждого сохранения переменная ЯаЛе^принимаетзначение crue. Это происходит в структуре if метода TForml: :SavelClick: if (SaveDialogl->Execute ()) { Psycho->SaveToFile (SaveDialogl->FileName); Safety = true; } Если по каким-то причинам сохранить данные не удается, переменная Safety принимает значение false. Это происходит в блоке catch того же метода: Applicat ion->MessageBox ("Ошибка при сохранении файла!'1, "Внимание!", 0+48); Safety = false; 7. Меню и системные диалоговые окна
Переменная Safety принимает значение false, когда после ввода нажи¬ мается кнопка Готово. В метод .Forml: :Button2Click необходимо до¬ бавить следующую команду: Psycho->Add (Editl->Text); Nr = random (Answer->Count); Panell->Caption = Answer->Strings[Nr]; Safety = false; Голова идет кругом от такого количества переменных Safety. Все эти действия имеет смысл выполнять, только если следует предусмотреть в методе Click для команды меню Выход возможность сохранять данные (BEFUND5.CPP): void fastcall TForml::EndlClick(TObject *Sender) { int Button; if (!Safety) { Button - Application->MessageBox ("Сохранить все введенные данные (еще раз)?", "", 3+32); if (Button == lDYES) SavelClick (Sender); } if (Button != IDCANCEL) Close (); } Сначала опишем локальную переменную Button. При вызове метода Application->MessageBox появляется окно сообщений с тремя кнопка¬ ми. Какая кнопка будет нажата, можно определить, например таким об¬ разом: Button = Application->MessageBox (Text, Title, Modus); Переменная Button получает значение нажатой кнопки. В нашем случае используются кнопки Дл, НЕТ и ОтмЕНА. В С++ для них существуют за¬ данные константы с именами IDYES, IDNO и IDCANCEL. Эти кнопки иниции¬ руют следующие действия: • принажатиикнопки ДАвведенныеданныесохраняются,азатемпро- грамма закрывается; Опрос на всякий случай
• при нажатии кнопки НЕТ программа закрывается без сохранения вве¬ денных данных; • При нажатии кнопки ОтмЕнл программа продолжает работу, не со¬ храняя введенные данные. Опрос метода Application->MessageBox осуществляется только в том случае, если введенные данные не были до обращения к диалоговому окну сохранены: if (!Safety) Ты припоминаешь оператор сравнения (!=)? Условие могло бы выгля¬ деть следующим образом: if(Safety != true) // Если значение переменной Safety не равно true Это условие я сократил до ! Safety, поскольку переменная Safety имеет тип bool. Восклицательный знак (!) преобразует условие или значение переменной типа bool в противоположное. Этот оператор называется реверсивным или оператором Not. (Свое название он получил от анг¬ лийского слова not, которое в переводе означает «нет»). Следует отметить, что мы вызываем метод, который в других случаях реагирует только на одно событие: if (Button == IDYES) SavelClick (Sender); Если по окончании работы задается вопрос «Сохранить все введенные данные (еще раз)?», и ты нажимаешь кнопку Дд, появляется другое ди¬ алоговое окно, в котором выбирается или вводится имя файла для со¬ хранения данных. 4 Щелкни мышью в меню формы Фдйл команду Выход. ^Добавь в метод TForm: :EndlClick приведенный выше текст. r>Введи в методы TForml::FormCreate, TForml::Button2Click и TForml: :SavelClick переменные Safety. На все случаи жизии Мы лочти все сделали, и нам осталось лишь предусмотреть тот случай, если кто-нибудь станет завершать работу программы не с помощью 7. Меню и системные диалоговые окна
команды меню Выход, а нажав кнопку X. Нам необходимо повторить все действия предыдущего раздела еще раз для метода FormClose. Мы могли бы выделить все команды и скопировать их в него. Но текст модуля для метода FormClose должен выглядеть немного иначе. Сначала следует создать в окне редактора кода рамку для метода FormClose. *> Щелкни мышью рабочую область формы. Перейди в инспекторе объектов на закладку EvENTS и дважды ЩеЛКНИ МЫШЬЮ СТрОКу ONCLOSE. 4 Введи в метод TForml: :FormClose следующий текст: void fastcall TForml::FormClose(TObject *Sender, TCloseAction &Action) int Button; if (!Safety) { Button = Application->MessageBox ("Сохранить все введенные данные (еще раз)?", "", 4+32); if (Button == IDYES) SavelClick (Sender); } В диалоговом окне необходимо разместить на одну кнопку меньше (кнопка ОтмЕнд не нужна, поскольку программа завершит работу в любом случае). Не нужна также строка Cu>SE, так как завершение осуществля¬ ется автоматически. ■> Прежде чем ты проверишь работу проекта Камертон настроения, сохра¬ ни его еще раз в новых файлах (KLEMP9.MAK и BEFUND5.CPP). Те¬ перь ты можешь экспериментировать с ним, сколько твоей душе угодно. На все случаи жизни
Выводы Ты узнал очень много нового. Нельзя сказать, что программа стала со¬ вершенной, но, по крайней мере, ты можешь удобно расположиться у экрана компьютера и насладиться плодами своей работы. Так, выдержав экзамен на подмастерье, ты делаешь еще один шаг на пути к мастеру. В этой главе мы познакомились с некоторыми объектами, методами и свойствами: MainMenu Меню, состоящее из заголовка и команд (тип TMainMenu) MenuItem Пункт меню (тип TMenuItem) OpenDialog Диалоговое окно (тип TOpenDialog) для открытия файла SaveDialog Диалоговое окно (тип TSaveDialog) для сохранения файла PrintDiaIog Диалоговое окно (тип TPrintDialog) для вывода файла на печать Execute Функция открытия диалогового окна. Возвращает значение true при нажатии кнопки OK и false, если диалог был прерван FileName Свойство объектов TOpenDialog и TSaveDxalog, содержащее полное имя файла с указанием пути RichEdit Многострочное окно редактирования (тип TRichEdit) для обычных текстов и текстов в формате RichText Format (RTF) Text Свойство списка строк и окон редактирования, содержит весь тёкст Print Метод объекта TRichEdit, передающий весь текст на принтер И еще один оператор языка С++: i Превращает условие в прямо противоположное 7. Меню и системные диалоговые окна
Несколько вопросов... 1. Как вставить меню в форму? Как создать методы для команд меню? 2. Как для объектов типа TOpenDialog, TSaveDi.aiog или TPrintDialog уз¬ нать, закончилось ли обращение к диалоговому окну нажатием кноп¬ ки OK, или оно было прервано? 3. Чем отличается объект типа TStringList от TRichEdit? ... и ни одной задачи Несколько вопросов..
8. Графические объекты В первых семи главах мы уже довольно близко познакомились с интег¬ ральным средством разработки C++Builder. Мы постоянно пользова¬ лись одним и тем же словом «объект», называя им все, что только ни попадалось в C++Builder. Для того чтобы произвести какое-либо действие над объектом, мы пользовались методами. Если в процессе работы у тебя появилось же¬ лание самому сотворить объект, давай вместе попробуем сделать это. Приближаясь к цели небольшими шагами, мы выполним поставленную задачу. В этой главе ты научишься: • использовать некоторые графические возможности C++Builder; • работать с объектом Canvas (Канва); • менять цвет текста, шрифта или фона; • отображать текст на форме; • работать с классами и объявлять их в коде; • пользоваться конструктором; • отличать функции от процедур.
О точках и координатах Когда мы говорим о графических изображениях, мы обязательно пользу¬ емся понятием координаты. Надеюсь, ты помнишь из уроков математи¬ ки, что это такое и поймешь, о чем пойдет речь. Все, что ты видишь на мониторе, состоит из множества точек, называе¬ мых пикселами. Их количество, отнесенное к единице площади экра¬ на, называется его разрешением. В таблице приведены типичные зна¬ чения разрешения для современных мониторов: I Ширина x высота Количество цветов | 640 X 480 от 16 до 16 миллионов 800 x 600 от 16 до 16 миллионов 1024 X 768 от 16 до 16 миллионов 1280 x 960 от 16 до 16 миллионов 1280 x 1024 от 16 до 16 миллионов За вывод рисунка на экран отвечает графический адаптер (карта). Он позволяет отображать до миллиона цветов. Разрешение определяется не только графической картой. Экран должен быть достаточно боль¬ шим, чтобы отображать рисунки с высоким разрешением. Для того чтобы компьютер знал, где он должен разместитьточки графического объекта при его отображении, экран разделен невидимой сет¬ кой. Каждая точка сетки описывается двумя числами. Они определяют расстояние от фик¬ сированного начала координат (x=0 и y=0), рас¬ положенного в C++Builder (и в большинстве графических программ) в левом верхнем углу экрана. При отсчете от начала координат горизонтально вправо мы будем пере¬ мещаться по оси x. Ее называют также горизонтальной осью. Если вести отсчет от начала координат вертикально вниз, то мы будем продвигаться по оси у. Ну что, вспомнил наконец, чему тебя учили? Оси координат выглядели по-другому? Верно, они имели следующий вид. О точках и координатах
Дело в том, что компьютерщики решили обой¬ тись без отрицательных чисел. И вообще, им ка¬ залось более логичным создавать рисунок там, где мы обычно начинаем писать: в левом верх¬ нем углу. Так же, как и на используемой математиками координатной плоскости, значение хувеличивается, если двигаться вправо. А вот значение у в ма¬ тематике при движении сверху вниз не увеличивается, а уменьшается. От математической плоскости координат компьютерщики позаимство¬ вали только один квадрант в виде перевернутой латинской буквы L (на¬ зываемый некоторыми «виселицей»). Первая картинка Довольно мучитьтебя теорией, пришло время практики! Итак, займемся первой графической программой. Точнее говоря, методом ?Forml: :ButtonlClick, который позволит при нажатии кнопки предста¬ вить на экране графическое изображение: Ты прав, полагая, что логично было бы ввести и ось z, которая от¬ вечает за глубину изображения. Для отображения трехмерных фигур существует специальная про¬ грамма. Современные ЗР-графические карты обеспечивают наи¬ более быструю и качественную передачу изображений. Например, компьютерные автомобильные гонки выглядят таким образом, как будто экран обладает перспективой, и изображение меняется при каждом повороте гоночной машины. 8. Графические объекты
void fastcall TForml::ButtonlClick(TObject *Sender) { // Прямоугольник и эллипс Canvas->Rectangle (10, 10, ClientWidth-10, ClientHeight-10); Canvas->Ellipse (20, 20, ClientWidth-20, ClientHeight-20); // Координатная сетха Canvas->MoveTo (0, ClientHeight/2); Canvas->LineTo (ClientWidth, ClientHeight/2); Canvas->MoveTo (ClientWidth/2, 0); Canvas->LineTo (ClientWidth/2, ClientHeight); } *^Создай новый проект и в центре формы размести кнопку. Напиши на ней Ндрисуй-кд!. 4 Дважды щелкни мышью кнопку и введи этоттекст в код метода ButtonClick. -> Запусти программу и щелкни мышью кнопку. Первая картинка
Рассмотрим подробнее, какие рисунки создаются с помощью програм¬ мы. На экране появляется прямоугольник, эллипс и две пересекающие¬ ся линии. За их отображение в C++Builder отвечает объект Canvas (Кан¬ ва) (тип TCanvas). Он сочетает в себе целый ряд методов для рисования и черчения. Каким образом определяется выполняемое действие, ты можешь догадаться самостоятельно. Используя метод MoveTo, ты управляешь невидимым карандашом: MoveTo (x, у); Метод LineTo позволяет провести линию от первой описанной точки ко второй: LineTo (xTarget, yTarget); С помощью этих двух методов чертятся любые графические объекты. Если необходимо создать прямоугольник или эллипс, используются два более простых метода: Rectangle (xLeft, yUpper, xRight, yLower); Ellipse (xLeft, yUpper, xRight, yLower); В обоих методах в качестве параметров ис¬ пользованы левый верхний и правый ниж¬ ний углы. Таким образом, определяется (невидимая) прямоугольная область, в ко¬ торой позже разместятся прямоугольник и эллипс. О назначении свойств CuENTWiDTH и CuENTHEiGHT уже шла речь в главе 4. Следует отметить, что Canvas ото¬ бражает объекты только в пределах указанной пользо¬ вателем области. Все, что выходит за ее рамки, стано¬ вится невидимым. В пределах этой области располагается и левый верхний угол с коорди¬ натами x=0 и y=0. А если оба заданных для высоты и ширины значения разделить на 2, можно найти середину формы и нарисовать крест: Canvas->MoveTo (0, ClientHeight/2); Canvas->LineTo (ClientWidth, ClientHeight/2); 8. Графические объекты
Canvas->MoveTo (ClientWidth/2, 0); Canvas->LineTo (ClientWidth/2, ClientHeight); На очереди цвет Давай, поэкспериментируем с уже знакомыми нам методами объекта Canvas. Для этого нам потребуются краски. Старый проект вполне по¬ дойдет, так как нам необходимо изменить только метод ButtonCiick. ^Дважды щелкни мышью кнопку Ндрисуй-кд! и заново введи следующий код или исправь соответствующим образом старый. void fastcall TForml::ButtonlClick(TObject *Sender) { // цветные лучи for (int i=0; i<70; i++) { Canvas->Pen->Color = Colour[random(CMax)j; Canvas->MoveTo (0, i*4); Canvas->LineTo (ClientWidth, ClientHeight-i*4); Canvas->MoveTo (i*6, 0); Canvas->LineTo (ClientWidth-i*6, ClientHeight); } } Цикл for предписывает выполнить тело цикла 70 раз и нарисовать боль¬ шое количество линий. Я полагаю, что ты разберешься с методами MoveTo и LineTo самостоятельно, как только увидишь графическое изображе¬ ние, появившееся в результате выполнения этого кода. При выполнении циклов со счетчиками обычно для счетчика вво¬ дится переменная с коротким именем (лучше всего, если оно со¬ стоит из одной буквы). Буква i является сокращением слова lndex (индекс). Индекс — это число, использующееся, например, при ну¬ мерации. Конечно, для цикла for можно снова использовать пере¬ менную Nr или любое другое имя. Рассмотрим первую команду цикла подробнее: Canvas->Pen->Color = Colour [random(CMax)]; На очереди цвет
Оператор Pen отвечает за свойство Pen (Перо) объекта Canvas, а Color — цвет этого инструмента. Функция random создает случайное число мень¬ ше определенного значения. Это число определяет цвет. Определить цвет непросто, так как в Windows имеется 16 миллионов цветов, которые, конечно, поддерживает и C++Builder. Воспользуемся стандартными цветами, для которых в C++Builder определены констан¬ ты, и запишем их в массив: const int CMax = 16; 7Coior Colour[CMax] = {clBlack, clMaroon, ciGreen, clNavy, clTeal, clPurple, clOlive, ciGray, clSilver, clRed, clLime, clBlue, clAqua, clFucasia, clYellow, clWhite}; Итак, для 16 стандартных цветов определим целую константу. Массив Colour принадлежит типу TColor, который уже объявлен в C++Builder. Имя каждой обозначающей цвет константы начинается с cl (сокраще¬ ние от английского слова color). Таким образом, компьютер выбирает для отображения следующей ли¬ нии новый цвет: Название цвета в C++Builder Цвет clBlack черный clMaroon темно-красный, красно-коричневый clGreen темно-зеленый clNavy темно-синий clTeal темно-бирюзовый clPurple темно-сиреневый clOlive темно-желтый, оливково-зеленый clGray темно-серый clSilver светло-серый clRed светло-красный clLime светло-зеленый clBlue светло-синий clAqua светло-бирюзовый clFuchsia светло-сиреневый clYellow светло-желтый clWhite белый 8. Графические объекты
^ В редакторе кода чуть выше строки TForml *Forml; введи объявления. ■> Для того чтобы генератор случайных чисел начал работать, дважды щел¬ кни мышью форму и добавь в метод FormCreate следующие команды: void fastcall TForml::FormCreate(TObject *Sender) { randomize (); } 4 Теперь ничего не может помешать работе нашей программы. Запустив ее, щелкни мышью несколько раз кнопку Ндрисуй-кд! (ты можешь поменять некоторые формулы или значения и посмотреть, что произойдет с линиями). Прямоугольники и эллипсы Для следующей программы мы можем использовать уже созданную. Изменить нужно только метод ButtonClick. ■> Введи следующий код или измени старый (LINIEN3.CPP и GRAFIK3.MAK): void fastcall TForml::ButtonlClick(TObject *Sender) { for (int i=0; i<55; i++) { Canvas->Pen->Color = Colour[random(CMax)]; Canvas->Rectangle (i*3, i*2, ClientWiath-i*3, ClientHeight-i*2); } Прямоугольники и эллипсы
Обрати внимание, что с каждым следующим выполнением тела цикла соответствующим образом меняются координаты углов изображаемо¬ го прямоугольника. ■+Для того чтобы понять, как это происходит, посмотри, что изменится, если ты определишь новые значения. Чтобы неостанавливаться надостигнутом, предлагаюсразужеследующую версию, в которой снова меняется только метод для нажатия кнопки. H> Введи заново следующий код (LINIEN4.CPP, GRAFIK4.MAK): void fastcall TForml::ButtonlClick(TObject *Sender) { for (int i=0; i<55; i++) { Canvas->Pen->Color = Colour[random(CMax)]; Canvas->Ellipse (i*3, i*2, ClientWidth-i*3, ClientHeight-i*2); } И опять счетчик / считает от 0 до 55. При каждом следующем шаге эл¬ липс уменьшается. ■^ Поэкспериментируй, изменив несколько значений. Попробуй добиться, чтобы эллипсы заполнили всю форму. Вложенные прямоугольники 8. Графические объекты
А теперь текст Объект Canvas позволяет не только рисовать. Ему можно передавать и строки, и отображать их на экране. Чтобы компоненты получили надпи¬ си, мы присваивали им текст. Мы передавали текст форме, когда разме¬ щали надпись в полосе заголовка. Объект Canvas позволяет вставить текст в любое место формы. Как это делается, демонстрирует следую¬ щая версия нашей программы. void fastcall TFoml::ButtonlClick(TObject *Sender) { for (int i=0; i<100; i++) { Canvas->Font->Color = Colour[random(CMax)]; Canvas->Font->Height = random(50)+10; Canvas->TextOut (random (ClientWidth),random (ClientHeight), "Привет"); } Так же, как и при использовании оператора Canvas->Pen, свойство Color может быть установлено с помощью оператора Canvas->Font. Кроме того, иногда необходимо изменить размер шрифта. Для этого воспользуем¬ ся свойством Height. Метод TextOut отображает на канве строку (тип String). Место располо¬ жения надписи задают два первых параметра: TextOut (x,y,Text); А теперь текст 7*
^Удали старый код метода ButtonClick и введи новые команды (TEXTOUTt.CPP, GRAFIK5.MAK). При желании можно изменить над¬ пись кнопки в свойстве CAPTiON на Ндпиши-КА. Затем запусти программу. Если ты обратишься к примерам программ из предыдущей главы, то на¬ верняка найдешь коды, в которых ту или иную запись для свойства CAPTiON можно отображать с помощью команды TextOut. Кисть С помощью объекта Canvas можно не только рисовать цветные изобра¬ жения, но и раскрашивать объекты в разные цвета. И в этом случае сле¬ дует изменить метод ButtonClick, в котором выполняются основные дей¬ ствия. Однако изменить нужно и метод FormCreate. С него мы и начнем: void fastcall TFoml::FormCreate(TObject *Sender) { randomize (); Color = Colour[random(CMax/2)]; } Переменная Color обозначает цвет фона формы. Попробуем изобра¬ зить темное небо. Кнопка должна как можно меньше мешать празднику красок, поэтому сделаем в этом случае ее немного уже и разместим внизу, у границы формы. Неплохо бы изменить и надпись на кнопке. | gy, Г ра»икав ЯВ ЁГ А теперь обратимся к методу TForml: :ButtonlClick. Цикл for мы бу¬ дем использовать и дальше. В нем 500 раз будут задаваться случайные 8. Графические объекты
значения для координат x, у и переменной Thickness (Толщина), кото¬ рые определяют место расположения и диаметр изображаемых с помо¬ щью метода Ellipse кругов: void fastcall TForml::ButtonlClick(TObject *Sender) { int x,y, Thickness; for (int i=l; i<500; i+^) { x = random(ClientWidth); у = random(ClientHeight); Thickness = random(20); // рисовать цветные круги Canvas->Brush->Coior = Colour[random(CMax)]; Canvas->311ipse (x,y,x+Thickness,y+Thickness); } Отличие заключается в том, что случайно выбранный цвет присваивается не Pen,aBrush(KncTb) —ещеодномусвойствуобъектаСапуа5(Со1ог — цвет этой кисти). ■^ А теперь измени положение кнопки на форме. Затем заново введи коды для методов TForml:: FormCreate и TForml:: ButtonlClick или измени со¬ ответствующим образом старые коды. Запусти программу несколько раз. Давай, перейдем к точке, вернее ко множеству точек. Пусть форма пре¬ вратится в небольшое небо, на котором зажигаются тысячи звездочек. Мы воспользуемся способностью объекта Canvas «зажигать» отдель¬ ные пикселы изображения. Кисть
Воспользуемся последней версией графической программы, в которой мы заменим код метода ButtonClick. Структура for будет выполняться значительно большее количество раз. Два случайных числа x и у опре¬ деляют меняющую свой цвет точку: void fastcall TForml::ButtonlClick(TObject *Sender) { int x,y; for (int i=l; i<10000; i++) { x = random(ClientWidth); у = random(ClientHeight); Canvas->Pixels[x][y] = Colour[random(CMax)]; } } Размерность Рассмотрим свойство PixeIs (Пикселы) объекта Canvas, которое пред¬ ставляет собой двумерный массив. Объявленный нами массив Colour— одномерный. Свойство PixeIs так же, как и Colour, имеет тип TColor: TColor Pixels[Quantityl][Quantity2]; TColor Colour[Quantityl]; Все значения свойства PixeIs являются значениями свойства Color, ка¬ кими они были созданы в методе FormCreate при запуске программы: Color = Colour [random(CMax/2)]; Команда Canvas->Pixels[x][y] = Colour[random(CMax)]; случайной точке присваивает произвольный цвет. (Этой точке может быть случайно присвоен цвет фона формы). 8. Графические объекты
Массив представляет собой структуру данных. В общем случае массивы переменных объявляются следующим образом: Тур Name[Max]; В прямоугольных скобках указано количество элементов, из кото¬ рых состоит объявляемый массив. Отдельные переменные такого массива вызываются или используются также с помощью квадрат¬ ных скобок. Например, следующая команда присваивает всем эле¬ ментам массива начальные значения: for (int i=0; i< Max; i++) Name[i] = Value; Большинство массивов одномерные. Однако, как мы увидели на примере свойства Pixels, существуют многомерные массивы. В этом случае объявление выглядит следующим образом: TypName2D[xMax][yMax]; // например, поверхность Тур Name3D[xMax][yMax][zMax]; // например, физическое тело По этой причине для обращения к конкретной переменной необ¬ ходимо указать несколько индексов. Например, для того чтобы все точки имели одно и то же значение, необходимо выполнить следу¬ ющие команды: for (int i=0; i< xMax; i++) for (int j=0; j< yMax; j++) Name2D[i][j] = Value; for (int i=0; i< xMax; i++) for (int j=0; j< yMax; j++) for (int k=0; k< zMax; k++) Name3D[i][j][k] = Value; т> Измени код методаТРогт1: :ButtonlClick и затем запусти программу не¬ сколько раз (PUNKTE2.CPP, GRAFIK7.MAK). Размерность
И все-таки она вертится Когда видишь богатые возможности, которыми располагают такие компо¬ ненты и объекты, как Canvas, появляется желание ими воспользоваться. Начнем с простейшего случая, взяв простую графическую фигуру — круг. Попробуем добиться того, чтобы в результате нажатия кнопок он появ¬ лялся на форме, передвигался и исчезал. ■> Для начала тебе понадобится форма с тремя кнопками, расположенны¬ ми в нижней ее части: llu,Rundi ЯИЕЗ r> Надпиши кнопки следующим образом: Появись, ПЕРЕМЕСтиеь и ИсчЕЗНИ. А теперь займемся методом FomCreate. В нем определяются размеры круга: void fastcall TForml::FormCreate(TObject *Sender) { x = 30; У = 30; Thickness = 150; Переменные x и у обозначают верхний левый угол невидимой квадрат¬ ной области, в которой будет располагаться круг (или эллипс, высота и ширина которого равны). Переменная ThicknessonpeRenner его диаметр. Все три переменные объявляются как целые числа: int x, у, Thickness; 8. Графические объекты
В код метода TForml: :ButtonlClick необходимо добавить только одну команду: void fastcall TForml::ButtonlClick (TObject *Sender) { Canvas->Ellipse (x, у, x+Thickness, y+Thickness); } Эта команда размещает круг в левой части формы. Он должен переме¬ щаться вправо при щелчке мышью кнопки Подвинься. Метод ButtonClick для второй кнопки несколько длиннее: void fastcall TForml::Button2Click(T0bject *Sender) { TRect Source, Target; for (int i=x-5; i<ClientWidth-Thickness-x-5; i++) { Source = Rect(i, y-5, i+Thickness+5, y+Thickness+5); Target = Rect(i+1, y-5, i+Thickness+6, y+Thickness+5); Canvas->CopyRect(Target, Canvas, Source); for (int j=0;j<1000000;j++) ; } } Перемещение круга осуществляется командой Canvas->CopyRect(Target, Canvas, Source); Метод CopyRect служит для копирования на текущую канву фрагмента ей предшествующей. Поскольку мы будем копировать круг в пределах одной и той же канвы, имя текущей канвы Canvas расположено между переменными Source (Источник) и Target (Цель). Логично было бы указать на первом месте источник (откуда копиро¬ вать), а на последнем — цель (куда копировать). В данном случае все наоборот (спроси разработчиков Windows, почему выбран именно та¬ кой способ). Переменные Source и Target необходимо определить точнее. Метод CopyRect (сокращенное название от Copy Rectangle, означающее в И все-таки она вертится
переводе «копировать прямоугольник») требует в качестве параметров Source и TargetyKa3aib прямоугольные области. Для этого оператором 7Rect Source, Target; объявляются две переменные типа TRect. Они принимают по четыре зна¬ чения, которые определяют координаты углов необходимых прямоуголь¬ ных областей. А команды Source = Rect(i, y-5, i+Thickness+5, y+Thickness+5); Target = Rect(i+1, y-5, i+Thickness+6, y+Thickness+5); присваивают эти значения переменным Source и Target. Поскольку i в цикле for каждый раз увеличивается на 1, прямоугольник-источник и прямоугольник-цель каждый раз перемещаются на 1 пиксел: for (int i=x-5; i<ClientWidth-Thickness-x-5; i++) Условие i<ClientWidth-Thickness-x-5 появилось по следующей причи¬ не: поскольку движение начинается в левой части формы от точки x-5, круг должен закончить движение на таком же расстоянии от правого края. Для вычисления значения координаты финальной точки движе¬ ния используется и ширина перемещаемого круга. Сумма этих значений вычитается из значения ClientWidth. Далее вставлен еще один цикл for, смысл которого неочевиден: for (int = 0;j<1000000;j++) ; Поскольку метод CopyRect работает очень быстро, движение круга про¬ исходит скачком, — он исчезает из левой части формы и через некото¬ рое время появляется справа. Скорость прыжка зависит от быстродей¬ ствия компьютера, на котором запускается эта программа. Вложенный в большую структуру for небольшой цикл со счетчиком i используется для замедления процесса перемещения круга: перемен¬ ная j увеличивается на единиЦу 1000000 раз. Следующее за условием тело цикла пусто, поскольку он только считает. На подсчет уходит неко¬ торое время, в течение которого круг может перевести дух. Благодаря этому циклу движение круга несколько замедляется. Числовое значение границы счета зависит от тактовой частоты процес¬ сора компьютера. 8. Графические объекты
Осталась третья кнопка. С ней мы поступим просто: удалим рисунок. Эту задачу выполнит метод Refresh: void fastcall TForml::Button3Click(T0bject *Sender) { Refresh (); } J> Введи коды для методов трех кнопок Появись, Подвинься и ИсчЕзни (RUND1.CPP, KREIS1.MAK). Запусти программу и посмотри, как круг появится, совершит прыжок и исчезнет. 4 Проверь, как будет работать программа после того, как ты вставишь в замедляющую петлю символ комментария (//) (две косые черты). По¬ смотри, как работает программа с различными числами меньше и боль¬ ше миллиона. (При слишком больших значениях движение круга может стать бесконечным!) Новый класс Итак, на данный момент мы располагаем кругом, который появляется в форме, перемещается по ней и затем исчезает. Обрати внимание на то, что у свойств x, у и Thickness так же, как и у методов кнопок Появись, Подвинься и Исчезни, есть нечто общее. Все они могли бы стать свой¬ ствами и методами одного круга, если бы существовал такой объект. Что нам мешает создать его? Как он должен выглядеть? Попробуем объявить объект следующим образом: Новый класс
class TCircle { public: int x, y, Thickness; void Appear (void); void Move (void); void Disappear (void); TCircle (int xx, int yy, int dd); }; Таким образом, это объявление принадлежит классу TCircle. В самом начале книги мы говорили о том, что такое класс. Давай, вернемся к это¬ му понятию еще раз. В языке С++ может быть объявлено несколько объектов одного типа. Такой тип объекта называется в С++ классом. Например, кнопки явля¬ ются объектами классаТЗ-jtton. Объект называется экземпляром клас¬ са. Ты, например, экземпляр класса Человек. За время чтения этой книги ты стал экземпляром класса Программисты. (Или, иначе говоря, объек¬ том типа Программист.) Объявление любого класса начинается с оператора class: class TForm class TButton class 'TCircle В фигурных скобках заключены имена всех свойств и методов, кото¬ рыми располагает класс. Свойства и методы выбранного нами класса TCircle представлены в таблице. 8. Графические объекты
Наименование Значение Appear Move Disappear TClass У Thickness x Свойство: x — расстояние слева (соответствует свойству Left в инспекторе объектов) Свойство: у — расстояние сверху (соответствует свойству Тор в инспекторе объектов) Свойство: толщина (= ширина и высота) (соответствует свойствам WidtH и Height в инспекторе объектов) Метод: появляется круг Метод: круг перемещается Метод: круг становится невидимым Метод: круг инициализируется (конструктор) В то время как в C++Builder элементы класса называются свой¬ ствами и методами, по старой традиции в С++ их называют дан- ными-элементами и функциями-элементами. Все они являют¬ ся элементами класса. Тебе могут встретиться понятия массива (в нашем примере поля¬ ми являются х,уи Thickness) и прототипа (для названий методов). Стоящее в самом начале объявления класса слово public (с двоеточием после него) очень важно. Только благодаря ему ты сможешь использо¬ вать все свойства и методы по своему желанию. Без объявления в раз¬ деле public (в переводе означает «открытый») ты не смог бы что-либо сделать с объектом типа TCircle. Объявление трех свойств должно быть понятно. Для того чтобы одно¬ значно описать круг, мы используем соответствующие значения данных- элементов x, у и Thickness. Но для работы с методами недостаточно лишь объявить имена: void Appear (void); void Move (void); void Disappear (void); Что обозначает оператор void? Обычно метод объявляется следующим образом: Несколько методов и конструктор Несколько методов и конструктор
Typ Name (Тур Parameter) ; Методы Appear (Появись), Move (Подвинься) и Disappear (Исчезни) не имеют типа, поэтому они вводятся оператором void. Такие методы называют процедурами. Функции — это нечто совсем другое. После выполнения они возвращают определенное значение, например: // Строка в качестве параметра, возвращаемое значение - // десятичное число float StrToFloat (String Text); // Десятичное число в качестве параметра, возвращаемое значение - // строка String FloatToStr (float Number); Поскольку данные методы не используют параметры, в скобках указы¬ вается слово void, хотя его можно и опустить: void Appear (); void Move (); void Disappear (); Система C++Builder знает, о чем идет речь, но она принимает такой ва¬ риант написания команд только как пережиток языка С (ведь С++ явля¬ ется преемником С). С++ любит однозначность, поэтому первый вари¬ ант объявления более корректен. Давай, поговорим о слове fastcall. До сих пор мы просто пользо¬ вались этим оператором, поскольку оно создавалось в C++Builder автоматически и предварялось двумя знаками подчеркивания ( ). При объявлении класса я опустил это слово. Оно имеет значение только тогда, когда над одним проектом работают на разных язы¬ ках программирования. В книге этот вопрос не обсуждается. При¬ ми это слово как данность, когда C++Builder автоматически вста¬ вит его в код. Если ты боишься ошибиться, используй его и в своих методах. Мы должны были бы написать следующее: 8. Графические объекты
void fastcail Appear (void); void fastcail Move (void); void fastcall Disappear (void); fastcall TCircle (int xx, int yy, int dd); Тебе непонятно, что делает самый последний метод? Почему-то он на¬ зывается так же, как и сам класс: TCircle (int xx, int yy, int dd); Что-то не так, да? Эта команда имеет определенный смысл. В ней не указан тип, — вместо него должно стоять имя класса! Данный метод ини¬ циализирует значение свойств, так как сначала переменные x, у и Thickness в лучшем случае имеют нулевые значения. Метод TCircle описывает круг, используя его параметры: int xx, int yy, int dd Tы, наверное, догадываешься, что трем свойствам x, у и Thickness присваи¬ ваются значения указанных параметров. Это входит в задачу метода TCircle, который называют конструктором. Он позволяет создавать объект. Конструктор определяется следующим образом: TCircle::TCircle(int xx, int yy, int dd) { x = xx; у = yy; Thickness = dd; } Этот фрагмент очень похож на структуры, которые мы уже давно исполь¬ зуем. Они назывались, например, T?orml::FormCreate, TForml::ButtonlClick. Имя классауказывается переддвойным двоеточием (::), за которым сле¬ дует имя метода. Поскольку в данном случае конструктору присваива¬ ется имя класса, оно совпадает с именем метода. Двойное двоеточие (::) в кодах встречалось уже не раз. Это опера¬ тор разрешения области действия. Он указывает, для какого класса разрешается доступ к методу. Несколько методов и конструктор
Объявление и инициализация 4 Прежде чем мы займемся другими методами, необходимо изменить код в соответствии с вышесказанным. За основу возмем старую версию про¬ граммы Круг. Однако сохраним ее сначала под другими именами (RUND2.CPP и KREIS2.MAK). Для того чтобы знать, где должно стоять объявление TCircle, посмот¬ рим код первой версии программы Круг. В коде присутствует объявление TForml *Forml; Оно есть во всех программах, которые мы создавали. Перед ним стоит объявление глобальных переменных, то есть переменных, которые дол¬ жны быть доступны во всей программе: int x, у, Thickness; Во второй версии (которую я только что предложил сохранить под новым именем) три перемен¬ ные превратились в свойства но¬ вого класса: 8. Графические объекты
Кроме того, перед объявлением объекта Forml всплыл новый объект: TCircle *Circle; Circle является не просто экземпляром класса TCircle. Благодаря опе¬ ратору косвенной адресации (*) указывается объект по имени Circle. Экземпляр появляется, только когда ему предоставляется место в памя¬ ти, а инициализацией класса занимается конструктор: void fastcall TFormi::FormCreate(TObject *Sender) { Circle = new TCircle (30, 30, 150); } Операция new предоставляет объекту место в оперативной памяти (ди¬ намически распределяет память), а конструктор TCircle определяет три значения, чтобы присвоить их в качестве начальных значений свойствам x, у и Thickness. Команда создания объекта Circie заменяет в методе FormCreate следу¬ ющие три операции присваивания: x = 30; у = 30; Thickness = 150; Непосредственно за объявлением Forml и Circle может следовать объяв¬ ление конструктора. Появление, перемещение и исчезновение Затем вводятся описания других методов, которые представлены ниже (RUND2.CPP): Появление, перемещение и исчезновение
void TCircle::Appear (void) { Foml->Canvas->Ellipse (x, y, x+Thickness, y+Thickness); 1 i П void TCircle::Move (void) { TRect Source, Target; for (int i=x-5; i<Forml->ClientWidth-Thickness-x^5; i++) { Source = Rect(i, y-5, i+Thickness+5, y+T'hickness+5); Target = Rect(i+1, y-5, i+Thickness+6, y+Thickness+5); Forml->Canvas->CopyRect(Target, Forml->Canvas, Source); for (int j=0;j<1000000;j++) ; } } // void TCircie::Disappear (void) { Forrnl->Refresh (); } С этими кодами трех методов ButtonClick мы уже работали. 4 Если хочешь сэкономить время, выдели соответсвующие блоки, выре¬ жи и вставь их в соответствующие методы класса ?circle (RUND2.CPP и KREIS2.MAK). А можешь ввести коды методов заново. Текст кода не совсем такой же. При ближайшем рассмотрении видно, что в разных местах его появляется слово Forml, которое предшествует командам: Forml->Canvas->Ellipse Forml->ClientWidth Forml->Canvas->CopyRect Forml->Refresh Оно используется по следующей причине: пока эти методы или свой¬ ства использовались непосредственно в одном методе, относящемся к классу TForm, система разработки C++Builder знала, что речь идет о 8. Графические объекты
ее собственных методах. Например, объект Canvas так же, как и Refresh, относится к объявлению класса TForm. При использовании этих методов или свойств в самостоятельно произ¬ веденном классе TCircle C++Builder не знает, принадлежат ли они клас¬ су TCircle, и поэтому выдает сообщение об ошибке. Если бы C++Builder искала данные методы и свойства в других классах, это было бы бес¬ смысленно, так как может существовать бесконечное число классов с такими свойствами. (Взять, например, свойство Caption. У какого объек¬ та нет этого свойства, KpoMeTCircle?) ^Добавь в исходный текст необходимые команды для Forml. Теперь методы ButtonClick определены полностью. Программа выгля¬ дит немного смешно, так как при щелчке мышью одной из трех кнопок ничего не происходит. Нам необходимо активировать новый объект, вызвав соответствующие методы: void fastcall TForml::ButtonlClick(TObject *Sender) { Circle->Appear(); } // void fastcall TForml::Button2Click(T0bject *Sender) { Circle->Move(); } _ // void fastcall TForml::Button3Click(T0bject *Sender) { Circle->Disappear (); Для доступа к методам объявленного нами объекта используется оператор доступа — стрелка (->). Таким образом, мы сообщаем нашему объекту (кругу), какой метод необходимо активировать. ^Дополни исходные тексты трех методов ButtonClick тремя командами. Сначала дважды щелкни мышью кнопку, так как (пустые) методы систе¬ ма C++Builder удалила. Появление, перемещение и исчезновение
4 Попробуй несколько раз запустить программу. Измени параметры вы¬ зова конструктора TCircle в методе FormCreate. Функция или процедура Одно мне не дает покоя: хорошо ли ты понимаешь, чем отличается функция от процедуры. Может, стоит рассмотреть эту тему подробнее? Во всяком случае, не будет лишним, если к объявлению класса TCircle я добавлю еще два метода, которые вычисляют площадь и длинуокружности круга. Ктому же, у меня появится возможность объяснить тебе, что такое функция. Сначала расширим объявление класса: class TCircle { public: int x, у, Thickness; void Appear (void); void Move (void); void Disappear (void); float Square (void); float Circumference (void); TCircle (int xx, int yy, int dd); }; Таким образом, два новых метода объявлены. C++Builder знает, что они относятся к классу TCircle. Ниже представлено полное объявление: float TCircle::Square (void) { const pi=3.141592654; return pi*(Thickness/2)*(Thickness/2) ; } // float TCircle::Circumference (void) { const pi=3.141592654; return pi*Thickness; } 8. Графические объекты
Если ты не помнишь формулы для вычисления площади и длины окруж¬ ности круга, ничего страшного в этом нет, поверь мне. Пока не так важ¬ но, чтобы они были верны. Важнее рассмотреть оператор return. По окончании метода он возвращает некоторое значение, которое в нашем случае относится к типу float. Поскольку возврат некоторого значения предполагается после выполнения метода, он называется функцией. В процедуре не бывает оператора return, то есть после ее выполнения зна¬ чение не возвращается. Тебе хотелось бы знать, зачем в скобках функции стоит оператор void? Эти два метода не имеют параметров. Возвращаемое значение парамет¬ ром не является. Если бы функции были объявлены за пределами клас¬ са TCircle, они имели бы следующий вид RUND2A.CPP: float Square (int. 'Thickness) { const pi=3.141592654; return pi* (Thickness/'2) * (Thickness/2) ; } // float Circumference (int Thickness) { const pi=3.141592654; return pi*Thickness; } Для того чтобы проверить работу функций, измени метод ButtonlClick следующим образом (RUND2A.CPP, KREIS2A.MAK): void fastcall TForml::ButtonlClick(TObject *Sender) { Circle->Appear (); Canvas->TextOut (65,90, "Площадь = '' - String(Circle->Square())); Canvas->TextOut (65,110, "Длина окружности = " + String(Circle-> Circumference())); } Функция или процедура
В таблице представлены имеющиеся в C++Builder типы методов: Конструктор Объявляется именем класса, инициализирует объект Как уже известно, каждый метод описывается два раза. Эту проце¬ дуру называют объявлением: сначала указывается имя метода, затем метод объявляется в классе: Тур ИмяМетода (Параметр); Эту операцию называют также прототипом или прототипом функции. Затем идет описание, то.есть разъяснение того, что делает объяв¬ ленный метод: Тур ИмяКласса::ИмяМетода (Параметр) { // программный блок // return для функций } Следует обратить внимание нато, что при объявлении в конце про¬ тотипа обязательно ставится точка с запятой (;), а в конце имени метода при его объявлении точка с запятой не ставится! Выводы Наэтом, пожалуй, закончим главу. В ней мы объявили свой первый класс, создали собственный объект и нарисовали несколько графических объектов. Посмотрим, что нового мы узнали о C++Builder: Функция Объявляется оператором Тур, всегда возвращает значение Процедура Объявляется оператором void, никогда не возвращает значение 8. Графические объекты
Canvas Канва (тип TCanvas) для создания графических объектов MoveTo Перемещает (невидимый) графический курсор в определенную точку LineTo Рисует линию до определенной точки (то есть перемещает (видимый) графический курсор в определенную точку) Rectangie Рисует прямоугольник (квадрат) Ellipse Рисует эллипс (круг) TextOut Отображает текст CopyRect Копирует фрагмент экрана Pixels Меняет цвет точки Pen Свойство класса TCanvas, определяющее начертание линий и фигур Brush Свойство класса TCanvas, определяющее цвет и стиль заполнения замкнутых фигур и фона Font Свойстйо класса TCanvas, определяющее шрифт, которым выводится текст Color Свойство многих объектов, определяющее цвет фона, текста и контура компонента Refresh Метод класса TForm, переносящий изображение компонента на экарен TRect Тип данных, определяющий координаты прямоугольника Rect Функция, определяющая координаты прямоугольной области Твои знания о языке С++ тоже пополнились: class Объявляет, класс return Создает возвращаемое функцией значение public Таким образом объявленные внутри класса переменные, объекты и т.п. становятся общедоступными new Резервирует место в памяти для нового объекта void Tип НЕ для процедур, поскольку они не возвращают значения :: Оператор разрешения области действия (соединяет имя класса и имя метода) * Определяет указатель на объект (или переменную) (оператор косвенной адресации) Функция или процедура
Вопросы... 1. Чем отличается команда MoveTo от LineTo? 2. Некоторые работающие на С++ программисты утверждают, что в языке С++ процедуры тоже являются функциями. Что ты об этом ду¬ маешь? 3. Что произойдет, если при объявлении класса ты забудешь вставить оператор public? ... и несколько задач 1. В составленной нами графической программе замени цветные круги на цветные квадраты. 2. Преврати класс TCircle в класс TQuadrate, и пусть по экрану вместо круга путешествует цветной (!) квадрат. 8. Графические объекты
9. Инкапсуляция и наследование В предыдущей главе мы только и делали, что с помощью объекта Canvas рисовали цветные картинки и, в конце концов, нам удалось лишь заста¬ вить бродить по форме худосочный круг. Попытаемся в этой главе создать что-нибудь более внушительное. Но сначала мы отправимся в страну объектно-ориентированного програм¬ мирования (сокращенно ООП). В этой главе ты узнаешь: • как создавать новый класс; • что подразумевается под определением Unit; • что такое заголовочный файл; • как наследуется класс; • что означает директива #include.
Под одной крышей Что самое главное в объекте? Ты используешь его, и тебя не интересует, что данные и коды объединены в одном модуле. Что означает это странное слово код? Под ним подразумеваются все объявления и команды, расположенные в исходном тексте. Поэтому ис¬ ходный текст можно называть также исходным кодом, или просто кодом. Данные — это значения, которые принимают свойства объекта. Ими могут быть цифры, строковые переменные, рисунки или даже файлы, которые используют один и тот же объект. Процесс объединения некоторых взаимосвязанных элементов называ¬ ется инкапсуляцией. В результате получается класс. На основе класса может быть создано множество объектов. Каждый объект имеет свои собственные данные. Для их обработки используют¬ ся методы, которые он получил от своего класса. При этом объект оста¬ ется независимым от других объектов. Экземпляр класса обладает всеми свойствами класса и может пользоваться его методами. Объект решает, какими данными за¬ полняется свойство, и когда используются методы. Важно понимать, чем отличается класс от объекта. В классе все методы определяются таким образом: Тур ИмяКласса::ИмяМетода (Тур параметр) Если объект объявлен экземпляром этого класса, к методу можно обратиться только следующим образом: Объединение элементов 9. Инкапсуляция и наследование
ИмяОбъекта->ИмяМетода (Параметр); Инициализация и объявлениеосуществляютсяследующим образом: Класс *Объект; Объект = new Конструктор (параметр); // ИмяКонструктора = ИмяКласса ! С объектами мы работаем, начиная с первой главы. Основой всегдабыла форма, объект типа TForm. На ней резвились разные кнопки, экземпля¬ ры класса TButton и множество других компонентов. Щелкая мышью значок компонента, а затем форму, мы размещаем в ней объект. Двойной щелчок мышью расположенного на форме компо¬ нента позволяет оказаться в исходном тексте (или коде) метода, напри¬ мер в том, который активирует нажатие кнопки. Как ты думаешь, — все эти действия можно проделывать только с компонентами, для которых в палитре есть значок? При создании объекта Circle в предыдущей главе мы не пользовались для его размещения на форме упомянутым способом. От начала и до конца все операции мы проделывали в окне редактора кода вручную. Но наше собственное творение все-таки появлялось на форме и получи¬ ло возможность перемещаться и исчезать. (Все эти действия обслужи¬ вали, прежде всего, методы объекта Canvas). Под одной крышей
Как ты знаешь из предыдущей главы, у программиста существует воз¬ можность создавать собственные классы. В принципе, ты можешь де¬ лать это независимо от C++Builder, — необходимо лишь соблюдать пра¬ вила языка С++. Замечу, что если ты пожелаешь самостоятельно создать сложные объекты, тебе придется немало поработать и решить множе¬ ство проблем. Посмотрим, какие еще компоненты предлагает система разработки C++Builder. Может быть, хоть что-нибудь нам удастся позаимствовать у нее для решения поставленной задачи. Мы уже работали со взятыми напрокат компонентами. Нам только необходимо узнать, как пользовать¬ ся классами C++Builder. Посмотрим более внимательно на имеющиеся в коде объявления, с ко¬ торыми мы уже работали: TForml *Forml; Обрати внимание на цифру 1. ?Form *Forml; Мы всегда считали, что форма является объектом типа TForm. А теперь оказывается, что это не совсем верно. Если ты зайдешь в какой-либо исходный текст и изменишь объявление так, чтобы объект Forml стал экземпляром класса ?Form, то при запуске программы появится сооб¬ щение об ошибке. Нам не нужен объект типа TForm, поскольку данный класс определяет абсолютно пустую форму без кнопок и меток. Нам требуется класс форм, в котором уже имеются некоторые компоненты и методы. Тип TForml предлагает лишь форму, созданную нами в соответствии с программой. ^№ 9. Инкапсуляция и наследование
Все, что есть на форме TForm, есть и на TForml. Очевидно, в языке С++ существует механизм, с помощью которого новый класс может насле¬ довать от имеющегося. Но как определяется наследование? В процессе просмотра исходного текста в файле с расширением CPP мы не нахо¬ дим такого объявления. Единственным указанием остается строка TFormI *Forml; Проект и модуль Отправимся на поиски файла, в котором могли бы располагаться объяв¬ ления. Для каждого проекта создается, как минимум, шесть файлов. Все остальные файлы являются дополнительными и позже удаляются. Один из указанных в таблице файлов должен быть искомым: PROJECT.MAK В этом файле указано, как C++Builder работает с проектом PROJECT.CPP В этом файле размещается исходный текст главной программы, в котором обычно ничего не нужно менять PROJECT,RES В этом файле содержится информация для файлов ресурсов Windows UNIT.DFM В этом файле размещаются данные формы и ее компонентов UNIT.CPP В этом файле размещается исходный текст, с которым мы обычно работаем UNIT.H В этом файле размещаются объявления, которые обычно C++Builder создает автоматически Проект и модуль
Слово Unit уже наверняка встречалось тебе при создании проекта не один раз. В то время как файл нового проекта называется PROJECT1, файл с исходными текстами в C++Builder называется UNIT1. Это понятие было заимствовано из системы разработки Delphi, ко¬ торая послужила примером при создании C++Builder. Если Delphi использует язык программирования Pascal, то система C++Builder написана на языке С++. В ней нет модулей, как в языке Pascal, но это понятие очень удобно для наименования законченного про¬ граммного блока. Язык Pascal позволяет объединять программные блоки в модуль Unit и сохранять его в одном файле. Проект большого размера может состоять из любого количества модулей, каждый из кото¬ рых представляет собой законченную единицу. Модуль в языке С++ называется исходным текстовым файлом или файлом, содержащим описания. Как мы уже видели, в C++Builder для одного проекта может использоваться несколько модулей. Основа модуля 9. Инкапсуляция и наследование
При автоматическом создании модуля система разработки C++Builder сначала объявляет форму и конструктор TForml. TForml *Forml; // fastcall TForml::TForml(TComponent* Owner) : TForm(Owner) Перед этими объявлениями стоит несколько строк, начинающихся со слов #include и #pragma: #include <vcl\vcl.h> #pragma hdrstop #include "Unitl.h'' #pragma resource ''*.dfm" Не буду пока подробно останавливаться на объяснении этих понятий. Сейчас нас интересует только одна строка, в которой указано имя фай¬ ла UNIT1.H. Из приведенной выше таблицы ясно, что именно этот файл мы и искали. Как его открыть? Для начала важно знать, что файлы с расширением (.h) создаются при первом сохранении (нового) проекта. При создании нового проекта с помощью команды №w Аррисдтюы меню FiLE появля¬ ется только форма и исходный текст в окне редактора кодов. (Исполь¬ зуя меню ViEw, ты можешь просмотреть соответствующий исходный текст проекта (CPP) и исполняемый файл проекта (MAK)). Проект и модуль
Заголовочный файл Необходимо полностью сохранить проект, как минимум, один раз, преж¬ де чем ты сможешь загрузить заголовочный файл. В языках С и С++ определенные объявления и описания собираются в одном блоке, называемом заголовком (поскольку по-английски это слово Header, становится понятно, откуда взялась буква «Н» в расши¬ рении заголовочного файла). Данный блок является заголовком всей программы, так как в нем системе C++Builder сообщается о том, напри¬ мер, какие типы и методы определил пользователь. Для того чтобы лучше понять, о чем идет речь, стоит просмотреть хотя бы один заголовочный файл. ■^ Создай новый проект и сохрани его (имена можешь не менять). Затем выбери команду OPEN в меню FiLE. ■^Найди в тектовом поле Тип ФАй- лов диалогового окна OPEN запись С FlLE (*.CPP,*.HPP,*.C,*H). 4 Или введи в поле Имя ФАйлд текст *.H и нажми кнопку Открыть. Имя файла, ph | Дткрыть ~] | В поле под списком Пдпкд представлены все расположенные в текущей папке файлы с указанным расширением. 9. Инкапсуляция и наследование •> Выбери заголовочный файл, ко¬ торый ты хочешь открыть, и на¬ жми кнопку Открыть.
Выбранный заголовочный файл открывается в окне редактора кодов. Этот файл имеет тот вид, в каком его автоматически создала система C++Builder. В верхней части окна редактора !л^и.ш^ндшдммщшяпвд» кода появляются две закладки с ш'т Un>1hl надписями UNu1.cpp и UNrr1.H. Щел¬ кая мышью закладки, можно переходить со страницы, отобра¬ жающей исходный текст модуля, к странице заголовочного фай¬ ла, и наоборот. Рассмотрим подробнее исходный текст. Он содержит небольшое, но полное объявление класса TForml: class TForml : public TForm { published: // управляемые компоненты интегрированной // среды разработки (ИСР) private: // определение пользователя public: // определение пользователя fastcall TForml(TComponent* Owner); }; Заголовочный файл 8-4109
Поскольку кроме конструктора ?Forml другие свойства и методы не объявлены, можно предположить, что речь идет о пустой форме. В инс¬ пекторе объектов ты найдешь некоторые свойства класса TForml. Но где они описаны? Получение наследства Решение нам подскажет первая строка объявления: class TForml : public TForm После имени классариЫ i с TForm объявляетотом, что новый класс TForml перенимает все свойства и методы (старого) класса TForm, то есть насле¬ дует их. Раздел объявления public указывает на то, что на использова¬ ние наследства не накладывается никаких ограничений. (Если ты уда¬ лишь слово public, появится сообщение об ошибке. В этом случае объявление класса не будет принято.) Таким образом, наряду с инкапсуляцией мы познакомились с еще од¬ ним важным свойством объектно-ориентированного программирова¬ ния, — наследованием. Благодаря ему можно создавать новые классы на основе уже имеющихся и использовать их свойства и методы. В общем виде первая строка объявления выглядит следующим образом: class ИмяНаследника : public ИмяРсдителя Двоеточие (:) соединяет родительский класс и наследника. Эти отноше¬ ния называются родственными. Новый класс — это ребенок, наследую¬ щий от своего предка как плохие, так и хорошие качества и привычки. Наследование Все, что в новом объявлении расположено между фигурными скобками, относится к особенностям нового класса. К старому классу они не имеют 9. Инкапсуляция и наследование
никакого отношения. Посмотрим, что произойдет, если мы размес¬ тим на пустой форме несколько кнопок и «оживим» некоторые ме¬ тоды. Для этого воспользуемся старым проектом Круг (KREIS2, RUND2). Посмотрим, во что превратится в данном проекте класс TForml (RUND2.H): class TForml : public TForm { published: // управляемые компоненты ИСР TButton *Buttonl; TButton *Button2; TButLon *Button3; void fastcall ButtonlClick(TObject *Sender) void fastcall Button2Click(T0bject *Sender) void fastcall Button3Click(T0bject *Sender) void fastcall FormCreate(TObject *Sender); private: // объявления пользователя public: // объявления пользователя fastcall TForml(TComponent* Owner); }; Тебе кажется, что текст довольно большой? Но мы еще не видели заго¬ ловочный файл последней версии проекта Камертон настроения (KLEMP9, BEFUND5). В проекте Kreis2Knacc TForml преумножил полученное от TForm наслед¬ ство, добавив к нему три новых свойства и четыре метода. Тебе хочется узнать, относится ли к заголовочному фалу объявление объекта TCircle? В предыдущей главе мы рассматривали способ объяв¬ ления класса. Наиболее простой способ заключается в том, чтобы вста¬ вить объявление в исходный текст, с которым мы уже работали. ■^Наверстаем упущенное. Открой проект KREIS2.MAK, а затем файл RUND2.H. Скопируй объявление TCircle из файла CPP в одноименный заголовочный файл и вставь его до объявления TForml. По окончании сохрани результат под именем KREIS2B или RUND2B. Получение наследства
Новорожденный У проекта Круг из последней главы есть некоторые недостатки. Так на¬ пример, TCircle пользуется методами других объектов (например, фор¬ мы или канвы). Экземпляр этого класса несамостоятелен. Давай, создадим объект, который будет абсолютно независим от дру¬ гих объектов C++Builder. Ниже представлен способ объявления такого класса: class TInterest { float Principal; float Percentage; float Interest; }; В этом проекте пойдет речь о расчете процентов, поэтому мы создаем объект TInterest. Объект такого типа принимает значение капитала, про¬ центной ставки и процентов, и по ним определяет другие значения. По¬ началу мы располагаем только тремя свойствами: Principal (Капитал), Percentage (Процентная ставка) и Interest (Проценты). Для расчета процентов нам не хватает методов. Объект данного типа мог бы рассчитывать проценты по формуле вполне независимо, однако лучше всего создать его на основе другого, уже имеющегося класса. 9. Инкапсуляция и наследование
Я имею в виду класс TObject — так называемый прародитель всех клас¬ сов в C++Builder. В нем объявляются основные свойства и методы, ко¬ торыми пользуются другие классы, в том числе TForm и TButton. Это ка¬ сается также создания и управления объектом в C++Builder. С самого начала необходимо уяснить (хотя бы на основе TObject), как производятся классы, в результате чего все они автоматически попада¬ ют в семью классов системы C++Builder. (Это можно было бы проде¬ лать и для объекта TCircle.) А для того чтобы остаться верными традициям C++Builder, необходи¬ мо во все методы, которые мы приписываем новому классу, вставить оператор fastcall. В усложненном виде объявление класса Tlnterest выглядит следующим образом: class TInterest : public TObject { public: float Principal; float Percentage; float Interest; void fastcall SetPrincipal (String Text); void fastcall SetPercentage (String Text); void fastcall SetInterest (String Text); String fastcall CalcPrincipal (void); String fastcall CalcPercentage (void); String fastcall CalcInterest (void); fastcall TInterest (void); }; Добавление public TObj ect указывает на то, что TInterest наследует все методы базового класса C++Builder TObject. У класса TObject нет свойств, но зато имеется достаточно много методов. Это можно увидеть, щелкнув мышью слово TObject и вызвав клавишей Fl справку C++Builder. Эти методы гаранти¬ руют беспрепятственную обработку объекта. Тебе только нуж¬ но сделать так, чтобы все созданные классы были наследника¬ ми TObject. Новорожденный
Вернемся к методам объекта TInterest. Три метода Set принимают в ка¬ честве параметра строки, например, из окна компонента Edit. Они пре¬ образуют эти строки в десятичные числа и приписывают их соответству¬ ющему свойству: Principal, Percentage или Interest. Три метода Calc рассчитывают по значениям любых двух свойств значение третьего, ис¬ пользуя стандартные формулы для расчета процентов. Прежде чем мы перейдем к описанию этих методов, позаботимся о со¬ ответствующей форме для объекта. ^Создай новый проект, указав в меню FiLE команду №w AppLiCATiON. До¬ бавь в форму метку, три панели, три окна редактирования и две кнопки. Ориентируйся на рисунок и таблицу. Label1 Введи ноль, чтобы отметить 12 вычисляемое значение! Panel1 Капитал: 12 Panel2 Процентная ставка: 12 Panel3 Проценты: 12 Edit1 (пусто) 12 Edit2 (пусто) 12 Edit3 (пусто) 12 Button1 Повторить 12 Button2 Готово 12 ■^Сохрани проект в файлах ZINS1.CPP и MATHE4.MAK. Одновременно создается и заголовочный файл. ^ Открой заголовочный файл ZINS1.H, указав в меню FiLE команду OPEN. Установи в списке Тип ФАйлов расширение *.H или введи имя непос¬ редственно в текстовом поле Имя ФАйлд. _ Вычмсяение процентов 9. Инкапсуляция и наследование
4 Введи в заголовочный файл объявление класса TInterest перед объяв¬ лением TForml. (Следи за тем, чтобы перед оператором fastcall сохра¬ нялись два символа подчеркивания!) В этом же файле в классе TForml мы объявим экземпляр. Пользуясь удоб¬ ной возможностью, объявим сразу и переменную, и метод: *^Дополни объявление TForml в разделе private следующими строками: TInterest *Interestl; int Modus; void fastcail SetZero (void); Таким образом, Interestl становится свойством TForml и может исполь¬ зоваться его методами. Переменная Modus определяет свойство, уста¬ навливающее вычисляемое значение: величину капитала, процентной ставки или процентов. К методу SetZero мы вернемся чуть позже. Ос¬ тановимся пока на этих скупых объяснениях и проследим за тем, чтобы объявления попали в нужное место кода. Методы Set и Calc Дпя того чтобы объект Tnterestl принял параметры от методов класса TInterest, необходимо выполнить следующие объявления. 4 В окне редактора кода щелкни мышью зак¬ ладку ZiNs1.CPP с исходным текстом файла модуля. Методы Set и Calc
■> Введи этот длинный исходный текст, не забыв про два знака подчерки¬ вания перед fastcall (ZINS1.CPP): fastcall TInterest::TInterest (void) : TObject () { } // void fastcall TInterest::SetPrincipal (String Text) { Principal = StrToFloat (Text); } // void fastcall TInterest::SetPercentage (String Text) { Percentage = StrToFloat (Text); } // void fastcall TInterest::SetInterest (String Text) { Interest = StrToFloat (Text); } // String fastcall TInterest::CalcPrincipal (void) { Principal = Interest * 100 / Percentage; return (String(FloatToStrF(Principal,ffNumber,8,2))); } // String fastcall TInterest::CalcPercentage (void) { Percentage = Interest * 100 / Principal; return (String(FloatToStrF(Percentage,ffNumber,8,2))); } // String fastcall TInterest::CalcInterest (void) { Interest = Principal * Percentage / 100; return (String(FloatToStrF(Interest,ffNumber,8,2))); 9. Инкапсуляция и наследование
Начнем с методов Set. Все они принимают в качестве параметра строку и занимаются только ее преобразованием в десятичное число, которое затем присваивается свойству Principal, Percentage или Interest: *Principal = StrToFloat (Text); Percentage = StrToFloat (Text); Interest = StrToFloat (Text); Если ты припомнишь, как вычисляются проценты, то поймешь, как я со¬ ставил методы Calc. Сначала по двум значениям свойств вычисляется значение свойства Principal, Percentage или Interest: Principal = Interest * 100 / Percentage; Percentage = Interest * 100 / Principal; Interest = Principal * Percentage / 100; Оператор return возвращает вычисленное функцией значение: return (String(FloatToStrF(Principal,ffNumber,8,2))); return (String(FloatToStrF(Percentage,ffNumber,8,2))); return (String(FloatToStrF(Interest,ffNumber,8,2))); Благодаря функции FloatToStrF появляется строка, которую можно ис¬ пользовать для создания надписи или в окне редактирования. Тяжелое наследие Насамом верху, перед методом TInterest, восседает конструктор. К нему мы пока не обращались, поскольку он имеет очень странный вид: fastcall TInterest::TInterest (void) : TObject () { } Фигурные скобки пусты, но это не означает, что конструктор ничего не делает. Он выполняет все то, что ему поручает родительский класс TObject, поскольку класс TInterest унаследовалуТО^е^ вместесо все¬ ми методами и конструктора. Поскольку для класса TInterest не нужно инициализировать ничего нового, унаследованного конструктора дос¬ таточно. Тяжелое наследие
Таким образом, конструктор активирует наследство: ИмяНаследника:: ИмяНаследника (Объявление параметров) : ИмяРодителя (наследование параметров) Расположенная после двойного двоеточия часть более не является объявлением, — это вызов метода, а именно метода родителя. Если ты обратишься к файлу модуля, в котором вызывается конструк¬ тор TForml, ты обнаружишь похожую конструкцию: fastcall TForml::TForml (TComponent *Owner) : TForm(Owner) { } Поскольку первая строка является объявлением, то необходимо объя¬ вить тип расположенного в скобках за TForml параметра: // Метод объявляется параметром типа TComponent TForml (TComponent *Owner) Вторая строка выглядит иначе, так как в ней вызывается метод, а значе¬ ние параметра передается непосредственно: // Метод наследует текущее значение Owner TForm (Owner) Эта команда соответствует следующей структуре: Тип ИмяМетода (тип ИмяПараметра) { Команда (ИмяПараметра); } Такую структуру можно увидеть в расположенном следом за конструк¬ тором описании метода: void fastcall TInterest::SetPrincipal (String Text) { Principal = StrToFloat (Text); } 9. Инкапсуляция и наследование
В конструкторе этот вызов переместился наверх. Иначе его код выгля¬ дел бы следующим образом (но в TForml он бы не работал): fastcall TForml:: TForml (TComponent *Owner) { TForm (Owner); } Расчет процентов Прежде чем запустить в работу объект Interest, его необходимо иници¬ ализировать, что выполняется в методе ?ormCreate: void fastcall TForml::FormCreate(TObject *Sender) { Interestl = new TInterest (); SetZero (); } В классе TForml появляется новый метод SetZero: void fastcall TForml::SetZero (void) { Interestl->Principal = 0; Interestl->Percentage = 0; Interestl->Interest = 0; Editl->Text = "0"; Edit2->Text = "0"; Edit3->Text = "0"; Modus = 0; } Не произошло ничего необычного: свойствам Principal, Percentage и Interest присваиваются исходные значения равные 0. В соответствии с этим в трех окнах редактирования отображается 0. Обнуляется и пере¬ менная Modus. Расчет процентов
Почему мы разместили эти операции во внешнем методе, когда все они отно¬ сятся к методу TForml:: FormCreate? По очень простой причине: уже в первом методе ButtonClick вновь появляются все операторы присваивания: void fastcall TForml::ButtonlClick(TObject *Sender) { SetZero (); Labell->Caption = "Введи ноль, чтобы отметить вычисляемое значение!"; } Благодаря методу SetZero блок кода получился достаточно коротким. Метод кнопки Готово, напротив, должен унаследовать львиную долю кода: void fastcall TForml::Button2Click(T0bject *Sender) { Interestl->SetPrincipal (Editl->Text); if (Editl->Text == ,''0") Modus++; Interestl->SetPercentage (Edit2->Text); if (Edit2->Text == "0") Modus+=10; Interestl->SetInterest {Edit3->Text); if (Edit3->Text == "0") Modus+=100; switch (Modus) { case 1: Editl->Text = Interestl->CalcPrincipal(); break; case 10: Edit2->Text = Interestl->CalcPercentage (); break; case 100: Edit3->Text = Interestl->CalcInterest (); break; default: Labell->Caption = "Слишком много нулей!”; } Modus = 0; } 9. Инкапсуляция и наследование
Наконец-то ты можешь увидеть, какую роль играет переменная Modus, являющаяся новым свойством TForml: if (Editl->Text == ”0") Modus++; // Modus = 1; if (Edit2->Text == "0") Modus+=10; // Modus = 10; if (Edit3->Text == "0”) Modus+=100; // Modus = 100; В зависимости от ситуации, ноль превращается в единицу, 10 или 100. Только эти значения используются при следующем анализе значе¬ ния оператором условного выбора switch. В зависимости от того, где стоит ноль, выполняется вычисление размера капитала, процентной ставки или процентов. Результат сразу же передается в окно редак¬ тирования: Editl->Text = Interestl->CalcPrincipal(); Edit2->Text = Interestl->CalcPercentage {); Edit3->Text = Interestl->CalcInterest (); Если вдруг появится второй или третий ноль, переменная Modus примет недопустимоезначение. В результатеобъект Interestl не выполнитдей- ствий, поскольку не будет указано, значение какого свойства вычисля¬ ется. Поэтому в структуру switch включена ветвь default: default: Labell->Caption = "Слишком много нулей!”; Эта ветвь соответствует блоку else структуры if. Если не соблюдается ни одно условие (case), то выполняется следующий за default оператор. По окончании операции переменная Modus снова принимает значе¬ ние 0. (На первый взгляд, смысл этого действия непонятен, но оно необходимо, если перед новым вводом не будет нажата кнопка По¬ вторить.) ■♦Добавь в методы FormCreate и 3uttonClick модуля Interest приведен¬ ный выше исходный текст. Вставь также метод TForml: :SetZero. ♦ Запусти программу (если в классе TForml не пропущено объявление SetZero и Modus!) Расчет процентов
Соблюдай следующие правила: Если ты хочешь рассчитать одно из трех значений, то задай для него 0 и введи два других значения. Приведу пример. Если ты вве¬ дешь в окно КдпитАЛ значение 2000 рублей и укажешь для ПроцЕН- тной стАВКИ величину 4,5, то при нажатии кнопки Готово ты уви¬ дишь в окне ПроцЕнты результат — 90 рублей. Так происходит при условии, что до начала расчетов в поле ПроцЕнты стоял 0. Если это значение стоит в более чем одном окне редактирования, вычисле¬ ния не производятся. При нажатии кнопки Повторить значение всех вычисляемых переменных устанавливается на 0. Наследник Tlmage Оставим теперь классы TInterest и TCircle. Обаобъекта не предлагают ничего особенно увлекательного, — в одном случае на экране появляет¬ ся несколько чисел, в другом по форме передвигается несложный рису¬ нок. Гораздо интереснее было бы наблюдать за движением настоящей картинки или сложной фигуры. Давай, займемся совершенно новым проектом. Объявляемый в нем класс будет наследником родителя — TImage, которого мы выберем из боль¬ шой семьи классов C++Builder. При путешествии по иерархии классов C++Builder привлекает внима¬ ние TImage. Он интересен по многим причинам. Во-первых, экземпляр этого класса является компонентом, который может быть создан с по¬ мощью палитры компонентов (закладка ADDiTiONAL). Производимый класс будет наследником данного компонента. 9. Инкапсуляция и наследование
Кроме того, объекты Canvas и Picture являются его свойствами. С объектом Canvas мы знакомы по предыдущей главе и знаем, что TForm является его свойством. С помощью этого объекта можно рисовать гра¬ фические изображения. В отличие от Canvas или в дополнение к нему, объект Picture предлагает возможность открывать и отображать выпол¬ ненные в графических программах картинки. Даже если мы не будем пользоваться возможностями Canvas, от дру¬ гих предложений ?Image мне отказываться не хотелось бы. Как назвать новый класс? Ниже представлены первые объявления: class TKovie : public T^mage { fastcail TMovie(TComponent* Owner); Поскольку мы займемся созданием небольшого фильма, назовем этот класс TMovie. И, как видишь, вновь появляется конструктор. Его пара¬ метр унаследован от конструктора Tlmage. Приведенное выше объявление мы вводить не станем, а позволим сде¬ лать это автоматически системе разработки C++Builder. ♦ Создай новый проект, указав в меню FiLE команду №w AppucATioN. Со¬ храни проект в файлах HOPS1.CPP и OOP1.MAK. Поскольку до сих пор мы обрабатывали исходный текст в модуле, в ко¬ тором также описаны новые методы объекта TForml, добавим в проект собственный модуль. Наследник Tlmage
• Выбери в меню FiLE команду NEW (Созддть). Открыв< ется диалоговое окно со всем собранием элементов так называемых шаблонов. 4 Найди значок COMPONENT (КомпонЕнт) и щелкни его мышью. Подтверди свой выбор нажатием кнопки ОК. r> Или выбери в меню COMPONENT команду NEW. ОткрЫВаеТСЯ ДИаЛОГОВОе ОКНО COMPONENT WlZARD (МАСТЕР КОМПОНЕНТОВ), в котором необходимо зарегистрировать новый тип объекта. ^ Введи в текстовом поле CLASS NAME (Имя клАССд) TMovie, а в поле ANCESTOR TYPE (Тип ПРЕДКА) - TImage. Выбери из раскрывающегося списка поля PALETTE PAGE (СТРАНИЦА ПАЛИТРЫ КОМПОНЕНТОВ) имя страницы палитры компонентов ADOITIONAL. Нажми кнопку ОК. 9. Инкапсуляция и наследование
♦ Сохрани новый модуль, указав в меню FiLE команду SAVE As, и присвой ему имя MOVIEl.CPP. При желании можешь посмотреть, что система C++Builder добавила в новый модуль (MOVIE1.CPP): #include <vcl\vcl.h> #pragma hdrstop ##include "Moviel.h” // static inline TMovie *ValidCtrCheck() { return new TMovie(NULL); } // fastcall TMovie::TMovie(TComponent* Owner) : TImage(Owner) { } // namespace Moviel { void fastcall Register() { TComponentClass classes[l] = { ;classid(TMovie)}; RegisterComponents("Additional", classes, 0); } } После того, как ты оправишься от первого шока, рассмотрим конст¬ руктор: fastcall TMovie::TMovie(TComponent* Owner) : TImage(Owner) { } Наследник Tlmage
И в нем между фигурными скобками ничего не указано, но в строке Tlmage (Owner) вызывается унаследованный от Tlmage конструктор. Он расположен между двумя похожими на методы структурами. static inline TMovie *ValidCtrCheck() void fastcall Register() Они следят за тем, чтобы произведенный тобой класс был принят систе¬ мой C++Builder и соответствующим образом зарегистрирован в ней. Эта операция чем-то напоминает выдачу пропуска в некоторых учреждени¬ ях. Поскольку C++Builder не знакома с бюрократической волокитой, данное действие выполняется гораздо быстрее. Директива #include До сих пор TMovie умеет не больше, чем TImage. Но мы уже многое сде¬ лали. Класс, например, может загружать и отображать картинку. Фак¬ тически эти операции станут выполняться, только если мы вставим в форму экземпляр нашего класса. Попробуем сначала сделать следующее: class TForml : public TForm { published: // управляемые компоненты ИСР private: // объявления пользователя TMovie *Moviel; public: // объявления пользователя fastcall TForml(TComponent* Owner); В заголовочный файл HOPS1.H добавляется Moviel — свойство TForml. ♦ Открой этот файл, указав в диалоговом окне OPEN тип файла *.H, или непосредственно введи в соответствующее поле имя файла HOPS1.H. Затем вставь строку TMovie *Moviel в объявление TForml (в раздел private:). К сожалению, мы не смогли оправдать все ожидания C++Builder, — не хватает одного объявления. 9. Инкапсуляция и наследование
Директива #include Объявление 7Movie располагается в заголовочном файле MOVIEt.H. Каким-то образом необходимо сообщить системе C++Builder о ее по¬ ведении по отношению к HOPS1.H. Пока класс TMovie не объявлен (то есть неизвестен), TForml не может что-либо делать. Проблема решается директивой #include, которую необходимо добавить в код: #include "Moviel.h" Чтобы система C++Builder обработала все созданные тобой файлы, тебе требуется только сообщить ей, как они взаимосвязаны. Это выполняет¬ ся в строке #include, в которой для текущего исходного текста указыва¬ ется имя файла. Вставленный перед командой символ (#) сообщает C++Builder о том, что эта команда предназначена для нее и не относит¬ ся к тексту кода. Команды такого рода называются директивами. Директива #include среды разработки C++Builder не только открывает файл, но и включает его копию в то место, где находится эта директива, прежде чем будет создана выполняемая программа.
Если ты посмотришь внимательнее, то обнаружишь в исходных текстах некоторых файлов целый список директив #include. Все они предназ¬ начены для того, чтобы связать объявления проекта. Не будем пока об¬ ращать внимание на то, что имена файлов заключаются при этом в скоб¬ ки и имеют расширение HPP. В следующей таблице представлена взаимосвязь между файлами. Файл Hops1.CPP Hops1.H Movie1.CPP Movie1.H Директива #include "Hopsl.h" #include ''Moviel.h" #include "Moviel .h* Цель связывания Описываются методы TForml Объявляется экземпляр TMovie Описываютсяметоды TMovie ^Добавь в исходный текст HOPS1.H директиву #include ''Moviel.h\ Если ты запустишь программу, то ничего не увидишь, кроме пустой формы. Наконец-то рисунок Очевидно, следует выполнить еще некоторые действия, прежде чем по¬ явится Moviel. Этот объект пока не инициализирован. После инициали¬ зации нам необходимо будет открыть картинку и выполнить предвари¬ тельные установки. Все эти операции выполняются в методе FormCreate. ^Дважды щелкни мышью форму. Ты переходишь в файл HOPS1.CPP, куда необходимо ввести следующий текст: void fastcall TFoml::FormCreate{TObject *Sender) { Moviel = new TMovie (this); Moviel->Parent = this; Moviel->Stretch = true; Moviel->Picture->LoadFromFile ("Hopsl.bmp”); } Что обозначает параметр this? Это слово является указателем на объект, для которого вызывается ме¬ тод. Поскольку им является метод FormCreate объекта TForml, this ука¬ зывает на Forml. 9. Инкапсуляция и наследование
Каждый объект автоматически получает в подарок в момент рож¬ дения указатель (this). В нем содержится информация о том, где находится объект, то есть его адрес в памяти. Конструктору Moviel этот указатель передается в качестве параметра. Тем самым опре¬ деляется, что Moviel принадлежит форме Forml. ч При объявлении конструктора в качестве имени параметра указы¬ вается Owner, благодаря чему Forml становится «владельцем» Moviel. Форме принадлежат все компоненты, которые расположе¬ ны на ее поверхности. Отношения владения создаются автомати¬ чески, когда ты щелчком мыши добавляешь в окно формы компо¬ нент и получаешь возможность работать с его свойствами в инспекторе объектов. Для производимых компонентов, которые связываются только в исход¬ ном тексте, эти связи необходимо определять вручную. После того как объект Moviel сможет функционировать в качестве эк¬ земпляра TMovie (и станет свойством или собственностью TForml), но¬ вые значения получат сразу два свойства: Moviel->Parent = this; Moviel->Stretch = true; Текущий объект подчинен объекту Parent. Родительским для компонен¬ та является отображающий его объект (который необязательно должен быть собственником компонента). В нашем случае объектом Parent по отношению к находящемуся в ней компоненту Moviel является форма. На это указывает this. STRETCH (Подгонкд) является весьма полезным свойством Tlmage. Если оно принимает значение true, то картинка подгоняется под размер кан¬ вы Moviel. Нам нужен отображаемый на экране рисунок. Картинку загружает ко¬ манда Moviel->Picture->LoadFromFile ("Hopsl.bmp"); С методом LoadFromFile мы уже знакомы по спискам строк проекта Ка¬ мертон настроения (см. главы 6 и 7). Этот метод позволяет открыть файл. Наконец-то рисунок
В качестве его имени ты можешь указать имя графического файла с расширением BMP. (BMP — сокращение от слова Bitmap, обозначаю¬ щее стандартный графический формат операционной системы Win¬ dows.) Тебе следует указать папку или полный путь файла, чтобы система C++Builder смогла найти его. Например: const Str:ng Path = "c:\\cpp\\buch\\"; // или, например, путь для дисковода компакт-дисков Moviel->Picture->LoadFromFile (Path+"Hopsl.bmp"); Если в большом проекте имя файла используется не один раз, целесо¬ образно объявить его и путь как значение константы. В этом случае при изменении пути замену необходимо будет произвести только в одном месте. Для чего нужнадвойная обратная косая черта? Ты уже знаешь, что С++ питает особую страсть к удвоениям. Поскольку простая обратная косая черта используется в С++ как управляющий символ для отображения строк, для обозначения пути используется двойная обратная косая чер¬ та (\\). Эти две строки обозначают одно и то же: // обычный способ указания пути "c:\cpp\test\" // модифицированный способ отображения этой же строки в C+* "c:\\cpp\\test\\" ♦ Сохрани всесозданное еще раз (OOP1.MAK, HOPS1.CPP, MOVIE1.CPP). Запусти программу и посмотри на рисунок. (В данном случае им оказал¬ ся рисунок забавного кролика.) 9. Инкапсуляция и наследование
Твоя программа не запускается, а C++Builder, не найдя связанно¬ го через директиву #include заголовочного файла, выдает целый список сообщений об ошибках? Тебе следует ввести полный путь (дисковод ^ папка ^ файл), на¬ пример: #include “с:\\cpp\\buch\\Moviel.h" #include "c:\\cpp\\test\\Moviel.h" Но лучше всего следить за тем, чтобы все необходимые файлы (МАК, CPP, H, DFM) сохранялись в одной папке. Выводы Теперь немного передохни. В следующей главе ?Movie получит новые методы, благодаря которым он станет отличаться от своего родителя TImage. Прочитав эту главу, ты узнал, чем отличаются файлы с расширением CPP и H: NAME.H Заголовочный файл: содержит объявления объектов NAME.CPP Файл модуля: содержит объявления переменных и описание методов Нам встретились следующие типы, свойства и методы системы разра¬ ботки C++Builder: TObject Базовый класс почти для всех объектов C++Builder Ovmer Свойство многих объектов (за исключением типа TObject): собственники объектов
Свойство многих объектов (за исключением типа TObject): родительский объект Класс, который может работать с изображениями Свойство TImage: «канва» (тип TCanvas) для отображения графических объектов Свойство TImage: «емкость» (тип TPicture) для открытия, отображения и сохранения графических объектов Свойство ?lmage: при значении true открытый рисунок подгоняется под размер канвы экземпляра Timage LoadFroroFiie Метод объекта TPicture: открывает графический файл И еще несколько «жемчужин» из лексикона С++: #include Директива для привязки заголовочного файла public При объявлении в этом разделе свойства, методы и наследование считаются открытыми, то есть общедоступными return Этот оператор возвращает значение из функции this Указатель на объект, метод которого активирован switch ПЕРЕЙДИ к выполнению блока команд case ЕСЛИ переменная принимает определенное значение (для организации циклов) default ЕСЛИ значение переменной не равно ни одному из указанных в операторах CASE значению Вопросы... 1. Чтотакое инкапсуляция? 2. Что такое наследование? 3. Чем отличается заголовочный файл от файла модуля? 4. Как создать новый класс компонентов? Pafent TBrage Canvas Picture Stretch ... и только одна задача 1. Расширь проект Калькулятор таким образом, чтобы в методах отслежи¬ валась передача строк, которые невозможно преобразовать в числа. 9. Инкапсуляция и наследование
10. Собственные компоненты Какими новыми свойствами обладает класс, созданный в последней гла¬ ве? Лишь теми, которые объект TMovie унаследовал от Tlmage. Гордись собой, так как тебе удалось создать новый класс собственных компо¬ нентов! Расширим возможности этого класса и внесем его в фамильную гале¬ рею C++Builder (под «галереей» я, конечно же, подразумеваю палитру компонентов.) В этой главе ты узнаешь: • как появляются и исчезают компоненты; • в чем состоит отличие между private и public; • как создавать и устанавливать в палитру компонентов собственные компоненты; • как создавать значки и файлы ресурсов для компонентов.
TMovie умеет больше, чем Tlmage Я предлагаю оставить первую версию проекта ООП в том виде, в каком она представлена в последней главе, а сделанные дополнения сохра¬ нить как новый проект. Для этого необходимо выполнить следующие действия. ♦ Открой проект OOP1.MAK, атакже файлы MOVIE1.CPP и MOVIE1.H. Сохрани все файлы в следующей последовательности: Старое имя файла Сохранить как I 1 MOVlE1.H MOVIE2.H 2 MOVIE1.CPP MOVIE2.CPP 3 HOPS1.H HOPS2.H 4 HOPS1.CPP HOPS2.CPP 5 OOP1.MAK OOP2.MAK Перед каждой операцией сохранения (меню FiLE, команда SAVE As) щел¬ кай мышью соответствующую закладку в окне редактора кодов. Пожа¬ луй, в файле HOPS2.H директиву #include "Moviel.h" придется заме¬ нить на #include "Movie2.h". Наконец, сохрани весь проект (меню FiLE, команда SAVE PROJECT As). Эти действия выполняй каждый раз, когда необходимо сохранить весь проект под новым именем. ? Ни в коем случае не изменяй имена вручную, пользуясь средства- MHWindows(RnHD0S)!nocKOflbKynpH3TOMCooTBeTCTByK3U4Heccbm- ки в общей системе управления проектами не изменяются, то при открытии проекта (с новым именем) система разработки С++ на¬ чинает безуспешно искать старые файлы. Далее займемся заголовочным файлом MOVIE2.H, в котором объявлен класс TMovie. ► Открой этот файл и дополни класс TMovie следующими объявлениями: class TMovie : public TIraage { private: protected: public: 10. Собственные компоненты
void fastcall SetSize (TRect Rectangle); void fastcall ShowImage (String File); void fasccall HideImage (void); fastcall TMovie(TComponent* Owner); published: } ; В нашем распоряжении имеются три метода, в которых используются английские обозначения. Наша цель — сделать класс Tmovie членом се¬ мьи классов C++Builder, а в ней говорят по-английски. Для чего предназначены данные методы? Это видно из следующей таб¬ лицы. Setgize Определяет размер и положение объекта ShowImage Объект становится видимым и отображается рисунком HideImage Объект становится невидимым (но еще существует) Ниже представлены соответствующие описания методов (MOVIE2.CPP): void fastcall TMovie::SetSize (TRect Rectangle) { Left = Rectangle.Left; Top = Rectangle.Top; Width = Rectangle.Right - Rectangle.Left; Height = Rectar.gle.Bottom - Rectangle.Top; } / / void fastcall TMovie::ShowImage (String File) { Show (); Picture->LoadFromFile (File); // vo.id fastcall TMovie::HideImage (void) { Hide (); } TMovie умеет больше, чем Tlmage
Собственно, не так много нового. Метод SetSize наследует координаты прямоугольной области и определяет по ним для класса TMovie положе¬ ние (Left/Top) (слева/сверху) и размер (Width/Height) (ширина/высо¬ та) прямоугольной области: Left = Rectangle.Left; Top = Rectangle.Top; Width = Rectangle.Right - Rectangle.Left; Height = Rectangle.Bottom - Rectangle.Top; Тип TRect определяет необходимые значения координатдля левого вер¬ хнего и правого нижнего углов. Метод ShowImage позволяет отобразить объект, а HideImage — сделать его невидимым. При этом они вызывают унаследованные от TImage ме¬ тоды Show и Hide. Кроме того, при выполнении ShowImage загружается графический файл, представляющий изображение. ->Введи исходный текст трех новых методов класса TMovie в файл MOVIE2.CPP. Появление и исчезновение объектов Для того чтобы определить, что теперь может делать экземпляр класса TMovie, необходимо создать соответствующую форму. Нам понадобят¬ ся кнопки и панель, в которой мы отобразим объект Movie. Форма мо¬ жет выглядеть примерно таким образом: -> Размести в правой части формы три кнопки. Начерти слева от них па¬ нель. Выбери ДЛЯ СВОЙСТВ BEVELlNNER И BEVELOllTER ПЭрЭМвТр BVLOWERED, благодаря чему создается впечатление, будто панель утоплена в форму. 10. Собственные компоненты
Эффект можно усилить, установив значение свойства BEVELWiDTH (Шири- НА скосА) равным 2 или 3. Для того чтобы программа стала более профессиональной, присвоим кнопкам не только надписи, но и новые имена. До сих пор мы доволь¬ ствовались такими именами, как например Button1 или Button3. • ♦ Измени в инспекторе объектов записи в свойствах Сдртюы и ЫдмЕ для всех трех кнопок в соответствии с таблицей. I Кнопка Caption Name | 1 Появись ShowButton 2 Исчезни HideButton 3 Выход EndButton Займемся методами формы. Начнем с FormCreate: i 1 void fastcall TForml::FormCreate(TObject *Sender) { Moviel = new TMovie (this); Moviel->Parent = Panell; Moviel->Stretch = true; Moviel->SetSize (Rect(10,10,Panell->Width-10,Panell->Height-10)); } После инициализации объекта Moviel родительским объектом (Parent) становится панель: Moviel->Parent = Panell; Таким образом, объект Moviel становится составной частью панели Panell, и все размеры действуют в ее пределах. Это обстоятельство не¬ обходимо учитывать при передаче координат углов, позволяющих оп¬ ределить положение и размеры Moviel: Появление и исчезновение объектов
Moviel->SetSize (Rect(10,10,Panell->Width-10,Panell->Height-10)); Функция Rect определяет четыре числа, задающие прямоугольную область. Метод, которым мы пользовались в главе 9 для открытия и отображения рисунка, исчез. Теперьон описывается в методе ButtonClick кнопки Появись. Ниже приведен код методов Click всех трех кнопок (HOPS2.CPP): void fastcall TForml::ShowButtonClick(TObject *Sender) { Moviel->ShowImage JPath+"Hopsl.bmp"); } U void fastcall TForml::HideButtonClick(TObject *Sender) { Moviel->HideImage (); } // void fastcall TForml::EndButtonClick(TObject *Sender) { Close (); } При нажатии кнопки Появись вызывается метод ShowImage, при исполь¬ зовании кнопки ИсчЕЗНи — HideImage. Оба метода принадлежат объекту Moviel. При нажатии кнопки Выход оператор Close закрывает форму. Примечательно, что теперь методы называются не ButtonlClick или Button3Click. В их названиях использованы имена трех созданных кнопок. ^Введи все команды в файл HOPS2.CPP, сохрани проект (OOP2.MAK, HOPS2.CPP, MOVIE2.CPP) и запусти про¬ грамму. 10. Собственные компоненты
На экране снова появляется рисунок кролика. Он расположен несколь¬ ко удачнее, чем в предыдущей главе. На панель можно вывести любой другой рисунок, если в качестве параметра метода ShowImage ты задашь имя файла, в котором хранится соответствующее изображение. Обрати внимание на то, что Moviel подгоняет рисунок по заданным размерам панели, поэтому при его отображении могут возникнуть искажения, как это было при выводе рисунка в главе 9. (Во избежание искажений задай свойству STRETCH значение false.) Кролик «оживает» При последовательном нажатии кнопок Появись и ИсчЕзни кролик то появляется, то исчезает. Однако появление и исчезновение рисунка не в полной мере соответствует той задаче, которую мы ставили перед клас¬ сом TMovie. Тогда вперед, к новым методам! void fastcall TMovie::MoveImage (String File) { // определяется номер текущего рисунка Path - File.SubString(l,File.Length()-5); Nr = StrToInt(File.SubString(File.Length()-4,l)); if (Nr > 4) Nr = 1; // Последовательно загружается рисунок и его "дополнение" Show (); for (i.nt i=0; i<5; i + + ) { Illustration = File; Picture->LoadFromFile (Illustration); for (int j=0; j<Brake; j++) ; Refresh (); Illustration = Path + IntToStr(Nr+4) + ".bmp"; Picture->LoadFromFile (Illustration); for (int j=0; j< Brake; j++) ; Refresh (); } } Кролик «оживает»
Метод MoveImage помогает классу TMovie оправдать свое название. Мы должны научить кролика бегать. Я воспользуюсь восемью рисунками кролика, пронумерованными от HOPS1.BMP до HOPS8.BMP. Hops1 Hops2 Hops3 Hops4 Hops5 Hops6 Hops7 Hops8 Предлагаю тебе создать свою собственную галерею рисунков, сохра¬ нив их в том же порядке под номерами от 1 до 8. Например: FIGUR1.BMP, FIGUR2.BMP и т.д. (Кроме того, все рисунки должны иметь одинаковый размер.) При этом действуют следующие правила: Графический Первое Графический Второе файл положение файл положение FIGUR1.BMP спереди FIGUR5.BMP спереди FIGUR2.BMP справа FIGUR6.BMP справа FIGUR3.BMP сзади FtGUR7.BMP сзади FIGUR4.BMP слева FIGUR8.BMP слева Каждый вид представлен в двух разных вариантах, сохраненных в соот¬ ветствующих файлах. Если, например, по очереди показывать рисунки Figur1 и Figur5, то создастся впечатление, будто кролик быстро идет. Для описания метода MoveImage нам понадобится несколько команд, ко¬ торые позволяют по имени файла определить номер рисунка. Команда File.SubString (1, File.Length()-5) создает из части имени файла строку. Строка, полученная в результате выполнения функции SubString, начинается с первого символа имени 10. Собственные компоненты
файла. Последние пятьсимволов имени отбрасываются. Функция Length определяет длину строки в символах. Таким образом, в результате вы¬ полнения функции SubString строка "c:\\cpp\\buch\\Hopsl.bmp" превращается в строку ''c:\\cpp\\buch\\Hops" // нет последних пяти знаков Для определения номера текущего рисунка мы используем эти же фун¬ кции, только на этот раз для полученной в результате их выполнения строки: File.SubString (File.Length()-4, 1) После выполнения этой функции от строки "c:\\cpp\\buch\\Hopsl.bmp" остается только один символ «1». Функция StrToInt превращает его в число, определяющее номер рисунка, который будет показан первым. Дополняющий его рисунок имеет номер, больший на четыре единицы (если использовать предложенный на рисунке порядок нумерации). Больший, чем 4, номер передаваться не будет: if (Nr > 4) Nr = 1; Таким образом, мы можем назначить параметр для загрузки второго рисунка. При первом проходе переменная Illustration получает имя оригинального файла в том виде, в каком он был передан при вызове MoveImage: Illustration = File; Picture->LoadFromFile (Illustration); // например, Hopsl.bmp for (int j=0; j<Brake; j++) ; Refresh (); Во внешнем цикле for необходимо предотвратить превращение бега в скачки. Какое значение для тормозящего цикла ты выберешь, зависит от быстродействия твоего компьютера. (При работе на сравнительно быстром процессоре используй для тормозящего цикла значение рав¬ ное нескольким миллионам.) Кролик «оживает» 9-4109
При втором проходе рисунок (Illustration) загружается из файла, имя которого содержит номер, превышающий номер первоначально загру¬ женного рисунка на 4 и оканчивающийся на .bmp (обрати внимание на точку!): Illustration = Path + IntToStr(Nr+4) + ".bmp"; // например, Hops5.bmp Picture->LoadFromFile (Illustration); for (int j=0; j<Brake; j++) ; Refresh (); Во внешнем цикле for объект имитирует бег. В нашем примере кролик меняет положение ног 10 раз: for (int i=0; i<5; i++) Вызов метода Show позволяет наблюдать на экране загружаемые из файлов картинки, пока не нажата кнопка ИсчЕЗни и не активирован ме¬ тод Hide. Бег и вращение Для того чтобы класс TMovie не выглядел скудным, несмотря на новый метод MoveImage, я хочу добавить в него еще и TurnImage. Данный метод позволяет фигуре вращаться вокруг собственной оси. void fastcall TMovie::TurnImage (String File) { // Сообщить номер текущего рисукка Path = File.SubString(l,File.Length()-5); Nr = StrToInt(File.SubString(File.Length()-4,l)); if (Nr > 5) Nr = 1; // Последовательно загружать и отображать рисунки Show (); for (int i=Nr; i<Nr+4; i++) { Illustration = Path + IntToStr(i) + ".bmp"; Picture->LoadFromFile (Illustration); for (int j=0; j<Brake; j++) ; Refresh (); } 10. Собственные компоненты
// Показать первую фигуру Picture->LoadFromFile (File); Refresh (); } Этот метод очень похож на предыдущий. Отличие заключается в том, что последовательно загружаются и отображаются не два, а четыре ри¬ сунка. Номер первого рисунка не должен превышать 5. Внешний цикл for служит для того, чтобы последовательно отображать файлы от HOPS1 до HOPS4. В результате смены рисунков создается впечатление, как будто кролик (или другой персонаж) кружится вокруг собственной оси. Как и следовало ожидать, в представленном коде много повторений. Это означает, что одни и те же последовательности команд появляются в методах MoveImage и TurnImage несколько раз (иными словами, в коде много лишних команд). Очевидно, необходимо объединить эти повто¬ ряющиеся команды в методы: void fastcall TMovie::GetNr (String File, int x) { Path = File.SubString(l,File.Length()-5); try {Nr = StrToInt(?ile.SubString(File.Length()-4,l));} catch (...) {Nr = -1;} if (Nr > x) Nr = 1; } // void fastcall TMovie::GetImage (String File) { Picture->LoadFromFile (File); for (int j=0; j<Brake; j++) ; Refresh (); } Снова появляется конструкция try-catch. Для обеспечения надежности работы программы эту структуру можно было бы использовать повсюду, Бег и вращение
но в данном случае она действительно необходима, чтобы загружать графические файлы, по имени которых невозможно определить номер рисунка. (TMovie используется не только для перемещения рисунков!) Если необходимо загрузить не входящий в некую серию рисунок, пере¬ менной Nr присваивается значение —1. Таким образом, появляется воз¬ можность прервать выполнение методов движения. Тогда от методов MoveImage и TurnImage останется лишь следующее (кро¬ лик все равно будет бегать) (MOVIE2.CPP): bool fastcall TMovie::MoveImage (String File) { GetNr (File, 4); if (Nr == -1) return false; Show (); for (int i=0; i<5; i-+) { GetImage (File); GetImage (Path + IntToStr(Nr+4) + ".bmp"); } if (Illustration != '"') GetImage (Illustration); return true; } // bool fastcall TMovie::TurnImage (String File) { GetNr (File, 5); if (Nr == -1) return false; Show (); for (int i=Nr; i<Nr+4; i++) GetImage (Path + IntToStr(i) + ".bmp*); if (Illustration != "") GetImage (Illustration); return true; } Не смог не добавить еще одну команду в сокращенный вариант кода: if (Nr == -1) return false; 10. Собственные компоненты
Если не загружается ни одного рисунка из серии, обеспечивающий дви¬ жение метод завершает свою работу и возвращает в вызывавший метод значение false, что означает «Бег или вращение не удались». Именно поэтому оба метода объявлены не как void, а как функции bool. Но если все рисунки из серии загружаются, выполнение соответствую¬ щей функции заканчивается возвращением значения: return true; Персонаж снова принимает исходное положение (если первоначально была нажата кнопка Появись, и благодаря этому есть имя рисунка): if (Illustration 1= '"') GetImage (Illustration); Для этого в метод ShowImage необходимо добавить еще одну команду присваивания: void fastcal3 TMovie::ShowImage (String File) { Show (); Picture->LoadFromFile (File); Illustration = Filc; } Мультфильм Замедляющей движение персонажей переменной Brake (Тормоз) в кон¬ структоре TMovie присваивается некоторое значение. В результате не¬ сколько меняется его описание: fastcall TMovie::TMovie(TComponent* Owner) : TImage(Owner) { Brake = 5000000; // проверь, какое значение наиболее подходит // для твоего компьютера } Мультфильм
♦ Введи все новые и модифицированные методы в файл модуля MOVIE2.CPP. ♦ Поскольку редактор кода уже открыт, добавь в заголовочном файле в блок объявления класса TMovie новые свойства и методы (MOVIE2.H): class TMovie : public TImage { private: int Nr; int Brake; String Illustration; String Path; void fastcall GetNr (String File, int x); void fastcall GetImage (String File); protected; public: void fastcall SetSize (TRect Rectangle); void fastcall ShowImage (String File); void fastcall HideImage (void); bool fastcall MoveImage (String File); bool fastcall TurnImage (String File); fastcall TMovie(TComponent* Owner); published; }; Полагаю, что пора немного поговорить о понятиях private и public, которые постоянно появляются в блоках объявления классов. Дол¬ жна же быть какая-то причина, по которой все свойства и одни ме¬ тоды TMovie объявлены в разделе private, а другие — в разделе public. Когда объявляется класс, все его элементы (свойства и методы) считаются объявленными в разделе private. К элементу данного раздела имеют доступ только методы класса, в котором он объявлен. 10. Собственные компоненты
Такой способ доступа называют закрытым. Для TMovie это означает, что другие объекты, например форма или расположенная на ней кнопка, не могут работать с переменными Nr, Brake, Illustration и Path. Да и к чему? Достаточно, что объект типа TMovie имеет доступ к ним. Этот способ доступа распростра¬ няется и на методы GetNr и GetImage. Они лишь помогают объекту Moviel выполнить работу. В этом случае достаточно, если право использования данных методов будет принадлежать только Moviel (или другому экземпляру класса TMovie). Совсем иначе дело обстоит с методами (свойствами), объявленны¬ ми в разделе public. Они открыты для любого объекта. Такой спо¬ соб доступа называют открытым. Для того чтобы наша программа могла работать, форма должна иметь доступ к некоторым методам класса TMovie. Они описыва¬ ются в разделе public. Поскольку в этой главе не будут использоваться два других спосо¬ ба доступа protected и published, мы вернемся к ним несколько позже. Для проверки работы новых методов нам не хватает запускающих бег и вращение кнопок и соответствующих им методов. ^Добавь на форму две кнопки. Надпиши их БЕГИ и ВрдщАйся, и присвой им соответственно имена MovEBurroN (Кнопка ПЕРЕДВИнуть) и TuRNBurroN (Кнопка ПовЕРНУТь). Выход Мультфильм
4 Добавь в файл HOPS2.CPP следующий исходный текст: void fastcall TForml::MoveButtonClick(TObject *Sender) { Moviel->MoveImage (Path+"Hops2.bmp"); } // void fastcall TForml::?urnButtonClick(TObject *Sender) { Moviel->TurnImage (Path+''Hops5.bmp''); } Значок для TMovie Новый класс TMovie еще не вполне совершенен. Если вдруг возникнет идея, как его доработать, ничто не помешает нам ее воплотить. Давай, попробуем разместить наш класс в палитре компонентов C++Builder. (Его можно в любой момент удалить из нее.) Сначала необходимо зарегистрировать его в качестве компонента C++Builder. Это выполняет следующая процедура: 10. Собственные компоненты Для того чтобы при движении изображение не мигало, необходимо при¬ вести в соответствие цвета панели и фона рисунка. Поскольку кролик изоб¬ ражен на черном фоне, добавим в метод FormCreate следующую строку: Panell->Color = clBlack; > Вставь эту строку в метод TForml:: FormCreate. Сохрани весь проект и запусти программу (OOP2.MAK, HOPS2.CPP, MOVIE2.CPP).
void fastcall Register() { TComponentClass classes[l] = { classid(T'Movie)); RegisterComponents("Additional", classes, 0); } В массиве типа TComponentClass класс идентифицируется как компонент (в результате TMovie получает опознавательный номер): TComponentClass classes[l] = { classid(TMovie)}; Функция RegisterComponents определяет, на какую страницу палитры C++Builder поместить компонент. Мы выбрали ту страницу, где нахо¬ дится родительский класс. Система C++Builder уже выполнила всю работу по регистрации, когда мы создали этот компонент в модуле Movie в последней главе. (У TMovie в TImage есть компонент-предок.) А второй шаг мы должны сделать самостоятельно. С помощью графи¬ ческой программы мы создадим для палитры компонентов значок. В системе разработки C++Builder имеется редактор изображений. 4 В MeHloT00LS укажи команду lMAGE EDITOR (РЕДАКТОР ИЗОБРА¬ ЖЕНИЙ). На рисунке представлено его главное окно. Значок для TMovie
^ Укажи в меню FiLE последовательно коман¬ ды NEW И RESOURCE FlLE (*.RES) (ФАЙЛ РЕСУР- сов). Появляется новая панель меню. 4 Укажи последовательно в меню RESOURCE команды NEW И BlTMAP (PACTPOBOE ИЗОБРАЖЕНИЕ). 4 В диалоговом окне BlTMAP PROPERTIES (Свой- ствА изовРАЖЕНия) установи размер 24 x 24. Подтверди установленные значения, нажав кнопку ОК. -> Дважды щелкни мышью в окне с названием UNTiTLEDl.RES (БЕЗЫМЯННЫЙ 1 .RES) ЗЭПИСЬ BlTMAPl. Появляется новое окно с названием BlTMAPl (UNT1TLED1.RES) и со смешным белым квадратом посереди¬ не, в котором должен расположиться значок компонента. Посмот¬ рим, можно ли рисо¬ вать в этом квадрати¬ ке без лупы. 10. Собственные компоненты
"^ В меню ViEw выбери команду ZooM lN (УвЕпичить). Повтори *,^J^^iL это действие несколько раз, пока квадрат не увеличится Sp*Sw настолько, чтобы в нем можно было работать (или несколь- ^я»- ^ ко раз нажми клавиши CTRL+I) *^ Окно с квадратом увеличь до размеров всей рабочей области редакто¬ ра изображений. На моем экране оно выглядит следующим образом: ^Теперь создай свой собственный рисунок. Если ты совершишь какую- нибудь ошибку, воспользуйся в меню Ешт командой UNDO. Нарисуй что-нибудь в квадрате. Значок для TMovie
♦ В меню WlNDOW (Окно) укажи команду UNTITLED1.RES, что¬ бы перейти в окно со списком имен. ♦ Щелкни правой кнопкой мыши имя BiTMAp1 в этом окне и в контекстном меню выбери команду RENAME (ПЕРЕИМЕНОВАТЬ). ♦ Присвой значку имя TMoviE. Итак, у значка есть рисунок и имя. ♦ В главном меню редактора изоб¬ ражений FiLE укажи команду SAVE As и присвой файлу ресурсов имя MOVIE2.RES. 10. Собственные компоненты
Что такое файл ресурсов? Ресурсы — это некоторые средства, пре¬ доставляемые, например, операционной системой Windows и ис¬ пользуемые в различных приложениях. В этом файле нет исполня¬ емых частей программы. В нем находятся только структуры, такие как меню, диалоговые окна или значки. r> В диалоговом окне lNSTALL COMPONENTS (УСТАНОВКА КОМПОНЕНТОВ) нажми кнопку ADD (ДОБАВИТЬ). 4 В диалоговом окне ADD MODULE (ДОБАВ- ЛЕНИЕ модуля) C++Builder предлагает ввести имя файла модуля, в котором расположены описания нового компо¬ нента. НаЖМИ КНОПКУ BROWSE. Установка компонента ^Z^%fc\ Установка компонента Давай, установим новый компонент Movie в палитру компонентов C++Builder. Закрой редактор изображений, так как он тебе больше не понадобится. В меню COMPONENT укажи команду lNSTALL (Устдновить).
^ В диалоговом окне ADD COMPONENT (ДОБАВЛЕНИЕ КОМПОНЕНТА) найди папку, в которой сохранен необходимый файл (MOVIE2.CPP). Он должен нахо¬ диться в той папке, в которой ты сохраняешь свой проект (и значок!). 4 В заключение нажми кнопку Открыть. Вернемся К диалоговому ОКНО lNSTALL COMPONENTS. В списке lNSTALLED COMPONENTS (УстдновлЕнныЕ компонЕнты) укажи строку MoviE2, а в списке COMPONENT CLASSES (КЛАССЫ КОМПОНЕНТОВ) - ИМЯ TMOVIE. ■> Нажми кнопку ОК. Тебе придется немного подождать. В случае положительного результата в диалоговом окне CoMPiLiNG (Компиля¬ ция) появляется сообщение DoNE (ВыполнЕно).ыц 10. Собственные компоненты
^ Нажми кнопку ОК. Компонент должен разместиться в палитре. Посмотрим, так ли это. ^ Щелкни мышью в палитре компонентов закладку A0DiTi0NAL. Видно, что значок появился в палитре. С этого момента твой собственный компонент будет размещаться на форме так же, как, например, кнопки. Таким образом, нет необходимо¬ сти вставлять в код исходный текст Movie, в котором определен компо¬ нент. Если в заголовочном файле такой программы ты захочешь найти объявление TMovie *Moviel, то с удивлением обнаружишь, что оно не стоит в разделе private, а перешло в раздел published: class TForml : public TForm { published: // управляемые компоненты ИСР TMovie *Moviel; private: // объявления пользователя Установка компонента
public: // объявления пользователя fastcall TFormi(TComponent* Owner); }; Однако нечему удивляться, так как экземпляры других компонен¬ тов палитры C++Builder объявляются в разделе published. Область действия published такая же, как и у public. Отличие зак¬ лючается в том, что в разделе published объявляются только ком¬ поненты. При попытке объявить другой объект, например типа ?Circle, C++Builder выдает сообщение об ошибке. 10. Собственные компоненты Объявление в разделе published приводит к тому, что свойства компонента отображаются в инспекторе объектов. В разделе published могут присутствовать только классы, созданные на ос¬ нове TObject. Кроме того. все методы свойств, объявленных в раз¬ деле published, должны быть дополнительно введены операто¬ ром fastcall. Для полного представления о способах доступа рассмотрим еще один, с которым мы пока еще не встречались. Элемент, объявленный в разделе protected, является закрытым, то есть с ним могут работать только методы того же класса. Не¬ смотря на то что доступ к объявленным в разделе private элемен¬ там для производимых классов запрещен, в них могут использо¬ ваться элементы, объявленные в разделе protected. protected является промежуточным способом доступа, так как он обладает свойствами private и public. Если ты всеже попытаешься объявить компонент published в одном из трех других разделов, то получишь сообщение о том, что класс TMovie не найден.
Так происходит потому, что C++Builder ищет компонент не в соб¬ ственной палитре, а в модуле, который для членов семьи компо¬ нентов не существует. Чтобы найти компонент в модуле, необхо¬ димо обратиться к старым файлам Movie и вставить их в проект. Не стоит пытаться менять способ объявления published. Выводы Наверное, нам стоит снова сделать перерыв, чтобы подготовиться к сле¬ дующему рывку. В предыдущей и в этой главах мы прошли тернистый путь создания собственного компонента. Постепенно мы узнали много нового о C++Builder. Ты познакомился соследующими классами, свойствами и методами си¬ стемы разработки C++Builder: ?Rect * Тип Данных для записи координат прямоугольной области Rect Функция, принимающая значения координат прямоугольной области Show Метод многих объектов (компонентов): объект становится видимым (отображается) Hide Метод многих объектов (компонентов): объект становится невидимым Bevel Свойство, например, TPaneI: определяет стиль рамки панели SubString Выделяет из строки часть Length Определяет длину строки Кроме того, ты узнал некоторые способы доступа к элементам классов: private Делает элемент класса доступным только для методов одного класса public Делает элемент класса доступным для объектов и функций/процедур других классов protected Делает элемент класса доступным для объектов и функций/процедур других классов, а также для созданных на его основе классов published Делает компонент C++Builder доступным для объектов и функций/процедур Выводы
Несколько вопросов... 1. Как объект делается видимым и невидимым? 2. Что произойдет, если метод SetSize объявится для TMovie в разделе private? ... и несколько задач 1. Создай с помощью компонента Movie (палитры C++Builder) тесто¬ вую программу для отображения рисунков в портретном формате. Прежде чем ты загрузишь графический файл, укажи его имя в диало¬ говом поле. 2. Добавь в TMovie метод для имитации танцевальных движений. Учти, что при этом файлы с рисунками загружаются случайным образом. Измени соответствующим образом тестовую программу ООП.
11. Приложение MDI Для создания проекта, которым мы займемся в этой главе, потребуется многое из того, чему мы научились. Похоже, он будет всеобъемлющим. Но мы не станем все конструировать самостоятельно, а воспользуемся помощью C++Builder. В этой главе ты узнаешь: • что такое приложение MDI; • как использовать компонент StringGrid ГГдБЛИЦА строк); • как комбинировать отображение текста, рисунка и таблицы; • что такое бит и байт.
Приложение для работы с несколькими документами Вместо того чтобы собирать вместе все элементы, позволим C++Builder сделать это за нас. В этой системе разработки уже существует несколь¬ ко готовых блюд, которые лишь остается приправить собственными спе¬ циями. ♦ Укажи в меню FiLE команду NEW. В закладках открывшегося диалогово¬ го окна NEW lTEMS (НовыЕ элЕМЕНТы) можно обнаружить множество шаб¬ лонов форм, компонентов и даже целых проектов. ♦ Щелкни мышью в диалоговом окне NEW lTEMS закладку PROJECTS (Про- ЕКТЫ). ♦ Найди значок MDI AppucATiON (ПриложЕННЕ MDI) и щелкни его мышью. Подтверди свой выбор, нажав кнопку ОК. 11. Приложение MDI
^ В диалоговом окне SELECT DlRECTORY (ВЫБОР ПАПКИ) выбери папку, в кото¬ рой будут сохраняться все относящиеся к проекту файлы. Рекомендую с этой целью использовать папку Тест. *^Для подтверждения выбра папки нажми кнопку ОК. На экране появляется новая форма. И, как ты видишь, C++Builder не скупится на многочисленные значки, расположенные на ней. Одновре¬ менно было создано несколько файлов, которые можно отнести к трем группам: MDIApp Имя проекта (= приложение MDI) Main Имя главного модуля и головной формы ChildWin Имя дочернего модуля и дочерней формы C++Builder приняла самостоятельное решение о том, какие имена при¬ сваивать файлам. Конечно, мы можем изменить их названия, но эти три имени имеют свой смысл. Они объясняют, как составлен новый проект. Приложение для работы с несколькими документами
Приложение MDI (Multiple Document Interface — интерфейс для работы с несколькими документами) одновременно управляет не¬ сколькими документами и представляет собой многооконный тек¬ стовый редактор. Под документом понимают текст, рисунок или таблицу. В главном окне приложения можно открыть несколько окон, на¬ пример, с текстом. У них есть панель заголовка, но нет меню. Все окна перемещаются и изменяются только в пределах главного окна. Из нескольких отображенных на экране окон в активном состоя¬ нии может находиться только одно. Примером MDI-приложения может служить Windows Explorer (про¬ водник Windows), а также многие текстовые и графические редак¬ торы. Редактор Windows или графическая программа Paint не явля¬ ются MDI-приложениями, так как в них текст или рисунок открываются и редактируются непосредственно в главном окне. Такие программы называют приложениями SDI (Single Document Interface — интерфейс для работы с одним документом). Весь проект называется MDIApp (MDI-Application). Файл модуля Main отвечает за главное окно, или главную форму. А что такое ChildWin? Это дочернее окно, а главное окно называют родительским, поскольку оно определяет границы для одной или нескольких дочерних форм. Его также называют граничной формой. Подходящая основа Проблема с заданными по умолчанию именами заключается в том, что C++Builder использует их и для следующего приложения MDI. Поэтому ты должен знать, как переименовывается проект. (Подобной операцией тебе уже пришлось заниматься в предыдущей главе.) ^ Открой файл CHILDWIN.CPP. Не удивляйся, что при этом появляется и новая форма. 11. Приложение MDI
Это так называемое дочернее окно MDI CmLD, или дочерняя форма. ♦ Сохрани все файлы в следующем порядке: 1 2 3 CHILDWIN CPP MAIN CPP MDIAPP МАК MCHILD1 CPP MMAIN1 CPP MULTI1 МАК Сохраняя файлы (меню FiLE, команда SAVE As), щелкни мышью в окне редактора кода сначала закладку CHILDWIN.CPP, затем — MAIN.CPP. В переименованный файл MMAIN1.H необходимо добавить директиву #include для MCHILD1.H (вместо #include "ChildWin.h"). Сохрани весь проект, указав в меню FiLE команду SAVE PROJECT As. Теперь ты знаешь, как проекту присваивается имя. Позже удали из операционной системы Windows все файлы MAIN, MDIAPP и CHILDWIN из папки, в которой сохранен новый проект ♦ Запусти программу и проверь, какие она предлагает меню и кнопки. Быстрые кнопки SpeedButtons позволяют произвести определенное дей¬ ствие, не входя в меню. При подведении курсора мыши к значку у каждой быстрой кнопки появляется небольшое информационное окно. А если провести курсором мыши по меню, то в нижней части формы можно на¬ блюдать, как изменяется содержание текста справки. Нижнюю часть фор¬ мы называют строкой состояния или панелью состояния. Как видишь, предлагаемый C++Builder готовый проект довольно хоро¬ шо оснащен. MDI. Подходящая основа
■^Укажи в меню FiLE команду OPEN или щелкни мышью первый значок па¬ нели инструментов. Открой любой файл. Появляется дочернее окно. Оно пусто, и содержания файла не видно. Родительская и дочерняя формы Для начала необходимо решить, какие файлы мы станем открывать и просматривать с помощью нашей программы. Я предлагаю следующие типы файлов: |Тип файла Расширение/Filter | Текстовый файл *.тхт Rich Text Format *.RTF С/С++-файлы с исходными текстами *CPP;*C;*HPP;*H Графические файлы *BMP Таблицы *TAB;*TBL 11. Приложение MDI
Эти данные используются для организации фильтра для компонента OpenDialog. ^ Щелкни мышью на форме значок OPENDiALOG. шв\ “3 4 Щелкни мышью в инспекторе объектов кнопку свойства FiLTER. Открывается окно редактора фильтра FiLTER EDiTOR. 4 В левой части окна вводится обозначение файла. В правой части указа¬ ны расширения файлов, которые используются для создания фильтра. Таким образом, в диалоговом окне OPEN отображаются файлы только с указанным расширением. Работа с текстами Попробуем организовать работу с первым типом документов и выберем подходящий компонент для отображения текста. Для этого нам понадо¬ бится дочерняя форма. Она появляется на экране автоматически, как только открывается файл модуля MCHILD1.CPP (или CHILDWIN.CPP). Работа с текстами
♦ В диалоговом окне ViEW FoRM (Формы) щелкни мы¬ шью форму MDICHiLD, а затем нажми кнопку ОК. Объект типа TMDIChild представляет собой дочернюю форму. (Между прочим, это не что иное, как дочерний объект класса TForm. В этом ты можешь убедиться, заглянув в заголовочный файл MCHILD1.H.) Для возможности отображения форме MDIChild потребуется помощь одного из компонентов. В главе 7 мы уже имели дело с объектом типа TRichEdit, который великолепно справляется с этой задачей, так как он работает не только с простыми текстовыми файлами, но и с файлами в формате RTF (поддерживаемым многими текстовыми редакторами). Кроме того, с его помощью могут отображаться и файлы исходных тек¬ стов на С++. Значок этого многострочного окна редактирования находится в палит¬ ре компонентов на закладке WiN95. *^ Щелкни мышью значок RicHEDiT. ♦ Если форма закрыта, открой ее. Выбери в меню ViEW команду FoRMS. 11. Приложение MDI
+ Перенеси компонент на форму MDIChild. Для того чтобы окно редактирования RTF занимало всю предоставлен¬ ную формой площадь, следует увеличить его размер. Если при запуске программы размер формы меняется, размер окна редактирования RichEdit остется прежним. Существует и другое решение. 4 Выбери в инспекторе объектов свойство AuGN и за¬ дай параметр ALCuENT (Автоподстройкд). В результате размертекстовогоокнабудет менятьсявсоответствии с изменениями величины формы. 4 Если ты захочешь загружать довольно длинные тек¬ сты, которые полностью не отображаются в окне, в инспекторе объектов задай свойству ScROLLBARS зна¬ чение ssBoTH (Овд). Благодаря этому параметру ты сможешь листать в окне страницы текста. Для того чтобы компонент RichEdit получил необходимый текст, в фай¬ ле MCHILD1.CPP определяется соответствующий метод: void fastcall TMDIChild::GetText (String FileName) { RichEditl->Lines->LoadFromFile (FileName); } Работа с текстами
Вероятно, ты помнишь из главы 7, что у объекта TRichEdit есть свой¬ ство Text. Его можно использовать для загрузки текста из файла, так как Text представляет собой очень длинную строку, не имеющую мето¬ да. Благодаря свойству Lines (Строки) у объекта TRichEdit есть и мас¬ сив строк. Новый метод GetText принимается только в том случае, если класс TMDIChild знает о его существовании. Добавим объявление метода GetText (MCHILD1.H): class TMDTChild : public TForm { published: TRichEdit *RichEdit.l; void fastcall FormClose (TObject *Sender, TCloseAction &Action); private: public: virtual void fastcall GetText (String FileName); virtual fasccalI TMDIChild (TComponent *Owner); } ; В первый раз в тексте встречается слово virtual. В следующей главе мы рассмотрим это понятие. В методе не хватает одной строки, которая занималась бы созданием и отображением формы. Эта строка расположена в файле модуля голов¬ ной формы — MMAIN1.CPP: void fastcall TMainFonr.::CreateMDIChild(String Name) ; 1 TMDIChild *Child; Child = new TMDIChild(Application); Child->Caption - Name; Child->GetText (OpenDialog->FileName); \ 11. Приложение MDI
Что происходит в методе CreateMDIChild? Во-первых, Child объявляет новую форму (типа TMDIChild) и инициализирует ее. Затем она получает название. До этого момента форма остается пустой. С помощью метода она выбирает файл, имя которого указано в диалоговом окне OpenDialog. ♦ Дополни файлы MMAIN1.CPP, MCHILD1.CPP и MCHILD1.H объяв¬ лением и вызовом метода GetText. Сохрани весь проект и запусти про¬ грамму. Работа с текстами Я попытался загрузить файлы с исходными текстами этого проекта. Фактически в этой программе могут одновременно открываться все файлы проекта Multi1 с расширением CPP или H. Для просмотраодного из файлов следует перейти с помощью меню WiNDOW в соответствую¬ щее дочернее окно. Чтобы текст занял всю рабо¬ чую поверхность головной формы, требуется щелкнуть мышью соответствующую кнопку в правом верхнем углу окна.
Работа с графическими изображениями Еще один тип файла в списке диалогового окна OPEN — рисунки с расши¬ рением BMP. Для их отображения вместо компонента RichText нам понадобится объект типа Tlmage. Поскольку в главе 10 мы научились создавать на его основе классы, я хотел бы использовать в работе эк¬ земпляр TMovie. (В этом случае было бы вполне достаточно также ком¬ понента Image). Но как быть с дочерними окнами, которые отображают только тексто¬ вые файлы? Так происходит потому, что окна оснащены компонентом типа TRichEdit. Мы снабдим форму MDIChild другим компонентом. С помощью методов Hide и Show его можно будет спрятать или отобра¬ зить. ■^Добавь к форме MDIChild один из следующих компонентов палитры C++Builder: если в качестве компонента установлен TMovie, то щелкни мышью объект Movie, в противном случае — объект типа TImage. Чтобы размер объекта автоматически подгонялся под размер формы, задай для свойства AuGN значение ALCuENT. (Если ты хочешь, чтобы ри¬ сунок занимал всю площадь, то установи для свойства ЭтяЕтсн значение true.) Поскольку для объекта RichEdit1 задана автоматическая подгонка раз¬ мера, почти невозможно активировать ни тот, ни другой компонент щел¬ чком мыши при изменении соответствующего свойства. Поэтому для объекта Moviel я оставил значение дьЫоыЕ (ОтсутствУЕт). Рекомендую также установить этот параметр и для объекта RichEdit1. 11. Приложение MDI
При выборе данных параметров можно расположить оба компонента на одной форме MDIChild: Лучше изменить стандартные установки метода FormCreate. Этим мы сразу же и займемся. ^ Дважды щелкни мышью форму MDIChild и введи в окне редактора кода следующие строки (MCHILD1.CPP): void fastcall TMDIChild::FormCreate(TObject *Sender) { RichEditl->Align = alClient; RichEditl->ScrollBars = ssBoth; Moviel->Align = alClient; Moviel->Center = true; } В тексте собраны все параметры двух имеющихся на форме MDIChild компонентов. При запуске программы их размер подгоняется в соответ¬ ствии с дочерней формой. Кроме того, объект RichEdit1 получает по¬ лосы прокрутки. А свойство CENTER служит для того, чтобы в методе Moviel все рисунки располагались по центру канвы. Как и для текстового окна, для Moviel необходимо определить соответ¬ ствующий метод в том же файле модуля (MCHILD1.CPP). При этом ме¬ тод RichEditl несколько расширяется: void fastcall TMDIChild::GetImage (String FileName) { RichEditl->Hide (); Работа с графическими изображениями
Moviel->Show (); Moviel->Picture->LoadFromFile (FileName); } П void fastcall TMDIChild::GetTexr (String FileName) { Moviel->Hide(); RichEditl->Show(); RicnEditl->Lines->LoadFromFile (FileName); } Технология работы двух методов похожа. Сначала закрывается преды¬ дущий компонент, затем отображается следующий. В завершение в со¬ ответствии с именем файла загружается и отображается рисунок или текст. Для формы TMDIChild необходимо объявить метод GetImage (MCHILD1.H): class TMDIChild : public TForm { published: TRichEdit *RichEdiLl; TMovie *Moviel; void fastcall FormClose (TObject *Sender, TCloseAction &Action); void fastcall FormCreate(TObject *Sender); private: public: virtual void fastcail GetText (String FileName); virtual void fastcall GetImage (String FileNane); virtual fasccall TMDIChild (TComponent *Owner); } ; ♦ Добавь в файлы MCHILD1.CPP и MCHILD1.H объявление и вызов GetImage. 11. Приложение MDI
^Добавь в метод CreateMDIChild следующий исходный текст (MMAIN1.CPP): void fastcall TMainForm: :CreateMDIChild(String Name) { TMDIChild *Child; String Extension; Child = new TMDIChild(Application) ; Child->Caption = Name; Extension = UpperCase (ExtractFileExt (OpenDialog->FileName)) ; if (Extension== ".BMP") Child->GetDnage (OpenDialog->FileNane); else Child->GetText (OpenDialog->FileName); } В этом случае важно, какой тип файла загружается. Поэтому команда UpperCase (ExtractFileExt (OpenDialog->FileName)); передает расширение файла. Эту задачу можно было бы решить и с по¬ мощью функции SubString, но функция ExtractFileExt справляется с ней лучше. Поскольку не имеет значения, какие буквы — прописные или строчные, — используются в обозначении расширения файла, мы пре¬ вращаем их все в прописные функцией UpperCase (с таким же успехом все буквы преобразуются в строчные функцией LowerCase). Поскольку графические файлы имеют расширение BMP, вполне доста¬ точно одного оператора передачи управления if: if (Extension== ".BMP") Child->GetImage (OpenDialog->FileName); else Child->GetText (OpenDialog->FileName); ■^Сохрани весь проект еще раз и запусти программу. Попробуй от¬ крыть какие-нибудь тексты и рисунки, которые есть на твоем жест¬ ком диске. Работа с графическими изображениями 10-4109
Текст и рисунок А как быть с таблицами? Давай, добавим в наш волшебный сундук нестандартный тип данных. Присвоим ему расширение TAB (если хочешь, дай этому типу файлов расширение TBL). Строки таблицы расположены друг под другом, как в списке. Метод определяет каждую строку в определенную ячейку таб¬ лицы. Для его программирования нам сначала понадобится объект, который может отображать таблицы. На закладке ADDiTioNAL палитры компонен¬ тов найди значок таблицы строк STRiNGGRiD. ^ ЩеЛКНИ МЫШЬЮ ЗНаЧОК STRiNGGRID. ■^ Размести этот компонент на форме MDIChild. Если уменьшить его, на форме хватит места для всех трех компонентов. (При запуске програм¬ мы размер трех компонентов автоматически подгоняется под размер формы). Или размести новый элемент поверх двух предыдущих так, что¬ бы они не перекрывали друг друга полностью. 11. Приложение MDI
^Установи в инспекторе объектов следующие значения: Свойства/Properties Значение Описание Defau!tColWidth DefaultRowHeight FixedCoIs FixedRows 0 100 25 0 Ширина столбца Высота строки Число фиксированных непрокручиваемых столбцов (например, для порядкового номера/времени) Число фиксированных непрокручиваемых строк (например, для дней) Для дальнейших изменений компонента StringGrid вставим в метод FormCreate соответствующие описания: ■^Введи в файл MCHILD1.CPP следующие команды: А как быть с таблицами?
void fastcall TMDIChild::FormCreate(TObject *Sender) { RichEditl->Align = alClient; RichEditl->ScrollBars = ssBoth; Moviel->Align = alClient; Moviel->Center = true; StringGridl->Align = alClient; StringGridl->Options « goEditing; } Так же, как и у других компонентов, размер StringGridl подгоняется в соответствии с размером текущей формы (Align = alClient). Для того чтобы в таблицу вводить данные и изменять их, существует свойство OPTiONS, которое позволяет редактировать строки: StringGridl->Options « goEditing; Сейчас ты познакомишься с новым оператором, свойством которого мы ранее уже пользовались. В этом примере он добавляет некоторый эле¬ мент. Назначение обоих операторов такого рода схематично представ¬ ляется в следующем виде: Поток « элемент // поместить элемент в поток Поток » элемент // взять элемент из потока Свойство OPTiONS объявляет поток, в который, например с помощью goEditing, помещается новый параметр. Он может быть взят из потока обратной операцией: StringGridl->Options » goEditing; Оба оператора » и « служат для того, чтобы перемещать со¬ держимое ячеек на одну ячейку памяти. Если тебе знакомы поня¬ тия бит и байт, ты, наверное, уже понимаешь, о чем идет речь. Содержимое ячейки оперативной памяти называется байтом. Его можно разделить на восемь частей, называемых битами. Бит — 11. Приложение MDI
сокращение от английского словосочетания Binary Digit (двоичный разряд). Бит — наименьшая ячейка памяти персонального компьютера. В него записывается либо 1, либо 0. \ Бит принимает одно из двух значений, поэтому соответствующая система счисления называется двоичной. Язык С++ позволяет пе¬ ремещать значения отдельных битов из одной ячейки в другую. Оператор « перемещает значение влево,аоператор » — впра¬ во. С++ располагает целым рядом операторов, которые целенап¬ равленно изменяют содержимое отдельных ячеек. Из списка в поле таблицы На следующем этапе займемся созданием метода для открытия и ото¬ бражения таблицы (MCHILD1.CPP). Для этого необходимо несколько больше, чем просто спрятать другие компоненты и загрузить данные в таблицу (MCHILD.CPP): void fastcall TMDIChild::GetTable (String FileName) { int Quantity, Rows, Columns; /7 спрятать текстовое и графическое окна, // отобразить окно таблицы RichEditl->Hide (); Moviel->Hide (); StringGridl->Show (); // создать список строк TStringList *Table; Table = new TStringList; // загрузить содержимое таблицы Table->LoadFromFile (FileName); Quantity = Table->Count; // сообщить число строк/столбцов Rows = StrToInt (Table->Strings[Quantity-2]); Columns = StrToInt (Table->Strings[Quantity-l]); StringGridl->RowCount = Rows; Из списка в поле таблицы
StringGridl->ColCount = Columns; // строки по столбцам считать из таблицы в таблицу строк for (int i=0; i<Rows; i++) for (int j=0; j<Columns; j++) { StringGridl->Cells[j][i] = Table->Strings[i*Columns+j]; } Поскольку компонент StringGrid не умеет самостоятельно загружать данные, ему должен помочь другой объект, к чьим услугам мы уже один раз обращались: TStringList *Table; Table = new TStringList; Объявив таблицу Table, мы получили список строк, в котором собира¬ ются отдельные текстовые строки файла таблицы: Table->LoadFromFile (FileName); Quantity = Table->Count; Для того чтобы преобразовать созданный в текстовом редакторе Windows тестовый файл (слева) в таблицу (справа), список строк дол¬ жен передать данные компоненту StringGrid. Из списка в таблицу строк Для этого самодеятельного формата в двух последних строках я указал число строк и столбцов, из которых состоит отображаемая таблица. Эти два значения присваиваются двум переменным, после того как из строк они преобразуются в целые числа: 11. Приложение MDI
Rows = StrToInt (Table->Strings[Quantity-2]); Columns = StrToInt (Table->Strings[Quantity-l]); Затем эти значения присваиваются свойствам таблицы строк RowCount и ColCount: StringGridl->RowCount = Rows; StringGridl->ColCount = Columns; Свойство RowCount определяет число строк (слово Row по-английски означает «строка»), а ColCount — число столбцов в таблице строк (Column означает «столбец»). Для того чтобы мы видели не только число столбцов и строк, но и со¬ держимое таблицы, необходимо считывать ячейку за ячейкой. Опера¬ ция считывания выполняется в двух циклах: один предназначен для строк, другой — для столбцов: for (int i=0; i<Rows; i++) for (int j=0; j<Columns; j++) { StringGridl->Cells[j][i] = Table->Strings[i*Columns+j]; ) Cells — свойство StringGridl, которое нельзя найти в инспекторе объек¬ тов. Оно становится доступным только после запуска программы, чтобы заполнить или изменить содержимое ячеек таблицы. ^Добавь в файл MCHILD1.CPP метод GetTable. Все три компонента Два других метода Get тоже расширились, так как механизм Hide-Show должен работать для трех компонентов. ^Добавь в методы GetText и GetImage соответствующие строки (MCHILD1.CPP): void fastcall TMDIChild::GetImage (String FileName) { // текстовое поле и поле таблицы скрыть, показать // графическое поле Все три компонента
RichSditl->Hide (); StringGridl->Hide (); Moviel->Show (); Moviel->Picture->LoadFromFile (FileName); } // void fastcall TMDIChild::GetText (String FileName) { // графическое окно и окно таблицы скрыть, // текстовое окно показать StringGridl->Hide (); Moviel->Hide (); RichEditl->Show (); RichEditl->Lines->LoadFromFile (FileName); } И, наконец, вфайл MCHILD1.H необходимо добавить объявление TMDIChi ld. Кроме того, в файле MMAIN1.CPP в методе CreateMDlChiid не хватает блока, который отвечал бы за создание таблицы с расширением TAB. ^ Введи объявление метода GetTable (MCHILD1.H): class TMDIChild : public TForm { published: TRichEdit *RichEditl; TKovie *Moviel; TStringGrid *StringGridl; void fastcall FormClose (TObject *Sender, TCloseAction&Action); void fastcall FormCreate(TObject *Sender); private: public: virtual void fastcall GetText (String FileName); virtual void fastcall GetImage (String FileName); virtual void fastcall GetTable (String FileName); ' virtual fastcall TMDIChild(TComponent *Owner); }; 11. Приложение MDI
^Добавь в метод TMainForm: :CreateMDIChild следующий исходный текст (MMAIN1.CPP): void fastcall TMainForm: :CreateMDIChild(String Narae) { TMDIChild *Child; String Extension; Child = new TMDIChild{Application) ; Child->Caption = Name; Extension = UpperCase (ExtractFileExt (OpenDialog->FileName)) ; if (Extension== ".BMP") Child->GetImage (OpenDialog->FileNarr;e) ; else if (Extension == ''.TAB") Child->GetTable(OpenDialog->FileName); else Child->GetText (OpenDialog->FileName); } Я немного упростил задачу, так как в примере опрашивается, имеет ли файл расширение BMP (для рисунков) или TAB (для таблиц). Во всех остальных случаях считается, что речь идет о текстовом файле. Если в диалоговом окне ОткрытиЕ ФАйлд в текстовом поле Имя ФАйлд ты введешь имя файла с каким-либо другим расширением, появятся какие- то непонятные данные. Чтобы избежать такой ситуации, приведи все допускаемые расширения текстовых файлов в структуре if: elseif ((Extension==".TXT") II (Extension==".RTF") II (Extension==".CPP") II (Extension==''.C") II (Extension==''.HPP") N (Extension==".H'')) Child->GetText (OpenDialog->FileName); Ты, наверное, помнишь, что оператор (! I) означает логическое ИЛИ. В результате условие имеет следующий смысл: Если расширение TXT или RTF или ..., то открой файл и отобрази его как текст. Это означает, что файлы с другими расширениями не могут быть от¬ крыты. Все три компонента
■^Сохрани весь проект еще раз и запусти программу. Открой какие-ни¬ будь тексты и рисунки, которые хранятся на жестком диске. Пустой график Скромное приложение Итак, чем мы располагаем? Приложением, которое позволяет просмат¬ ривать тексты, рисунки и простые таблицы. В тексте и таблице ты мо¬ жешь перемещать курсор, выделять фрагменты или изменять их. Пос¬ ледняя операция не изменяет содержимого файла, так как в приложении у нас нет возможности его сохранять. Если ты просмотришь меню этой мультисистемы, то обнаружишь мно¬ жество не выполняющих никаких действий команд, несмотря на то что они активируются при открытии дочернего окна. Мы не станем превращать все команды меню в выполняемые, так как мы не программируем полное приложение. Однако у тебя есть возмож¬ ность самостоятельно познакомиться со всеми возможностями прило¬ жения MDI. Если ты захочешь удалить невыполняемые команды меню, то проделай следующие операции. *> Щелкни мышью на головной форме значок МдшМЕыи. 4 Щелкни мышью в инспекторе объектов в свойстве lTEMS небольшую кноп¬ ку с многоточием. 11. Приложение MDI
♦ Удали в файле MCHILD1.CPP следующую строку (или вставь перед ней знак комментария (//)): StringGridl->Options « goEditing; •♦Установи в инспекторе объектов для свойства READONLY (Только для чтЕ- ния) объекта RichEdit1 значение true. Выводы Ты слегка огорчен, что нам не хватило времени на создание полного приложения? Ты недоволен тем, что в большинстве случаев система Выводы ► Или открой редактор меню, дважды щелкнув мышью соответствующий значок меню на форме. ► Выбери удаляемую команду. Нажми клавишу Del. Если ты удалишь все неработающие команды меню, то после этой операции останутся толь¬ ко два меню — FiLE и WiNDOw.
разработки все делала сама, да? Но, мне кажется, и мы немало потру¬ дились. Приложение MDI само по себе не предлагает инструментов, ко¬ торые позволили бы одним махом создать текстовый редактор. Твой словарный запас C++Builder пополнился следующими понятиями: MDIChild Форма MDI (тип TMDIChild^ вкладывается в головную форму RichEdit Многострочное окно редактирования (тип TRichEdit) для обычных текстов и текстов в формате RTF StringGrid Таблица строк (тип TStringGrid) для отображения текста в виде строк и столбцов Cells Свойство TStringGrid: двумерное поле для приема строк (содержимого ячеек) ColCount Свойство TStringGrid: числостолбцов RowCount Свойство TStringGrid: число строк StringList Список для приема строк « Оператор для приема параметров » Оператор для выдачи параметров LowerCase Преобразование строки в строку из строчных букв UpperCase Преобразование строки в строку из прописных букв Только один вопрос. 1. Система разработки C++Builder является приложением типа MDI или SDI? ... и одна задача 1. Попробуй создать с помощью компонента RichEdit небольшую про¬ грамму, которая позволяла бы вводить, загружать, редактировать и сохранять текст. 11. Приложение MDI
12. Виртуальные методы Мы достаточно продуктивно поработали с компонентами, научились объявлять собственные классы и даже создали одно приложение MDI. Мы познакомились с объединением и наследованием. Но объектно-ори¬ ентированное программирование позволяет делать нечто большее. Давай, постараемся проникнуть несколько глубже в иерархию классов C++Builder. Мы сделаем попытку обогатить свои возможности. Кроме того, предлагаю познакомиться с монстрами, которые предстанут перед нами в разных образах. В этой главе ты узнаешь: • как работать с несколькими новыми компонентами семейства TControi; • об объявлениях внутри класса; • как сделать события открытыми вне класса; • чем могут быть полезны монстры; • что такое виртуальные методы; • что понимают под полиморфизмом; • что означает понятие перегрузки.
TObject, TControl и кое-что еще? На самой верхней ступени иерархической лестницы объектов системы C++Builder находится класс TObject. Чем он примечателен? Почему он является родителем или прародителем всех создаваемых и объявляе¬ мых классов? Постараемся понять эту удивительную особенность класса TObject, заг¬ лянув в справку C++Builder. Поищем, например, с помощью меню HELP справочную информацию об объекте TObject, введя соответствующее слово в текстовое поле закладки lNDEX (УклзлтЕль). Может быть, тебе не все понятно, что написано об этом классе. Вопросы о том, как создается объект, как резер¬ вируется необходимое ему место в оперативной памяти, как им управ¬ ляют и удаляют его, тебя волновать не должны. Объект, произведенный от TObject, прекрасно вписывается в окружение C++Builder. 12. Виртуальные методы
Собственно говоря, обойти это семейство классов нельзя, так как если при объявлении нового класса не указывается родитель, C++Builder ав¬ томатически присваивает ему в качестве родительского класса TObject, тем самым заботясь о том, чтобы на свет не появлялись сироты. (Так что первый созданный нами класс TCircle вовсе не был самостоятельным, а являлся наследником TObject!) Эта особенность может проявиться как недостаток, так как все созда¬ ваемое в C++Builder сразу же встраивается в общую структуру. С другой стороны, у тебя есть возможность использовать богатый набор предла¬ гаемых C++Builder инструментов. При наследовании часто бывает целесообразнее использовать члены семейства, которые обладают несколько большими возможностями, чем TObject. Так например, в главе 9 в качестве родительского класса для создаваемого TMovie мы выбрали Tlmage. При этом нам пришлось разра¬ батывать механизм загрузки, сохранения и отображения рисунков. Отправившись в путешествие по иерархии классов, поставим себе цель найти для TMovie брата, сестру или какого-нибудь родственника. В палитре компонентов C++Builder мне не хватает кнопки круглой фор¬ мы, так как обычно кнопки не квадратные, а круглые (по крайней мере, на одежде). При проектировании нового компонента мы вспоминаем о TButton — классе, который может стать родительским для создаваемого нами по¬ томка круглой формы. Но как задать округлый вид наследнику кнопки? Поскольку объект типа TButton управляется операционной системой Windows, к сожалению, многого мы сделать не сможем. Помочь нам смогут следующие два класса: TGraphicControl Базовый класс для управляющих элементов, которые реагируют иначе, чем стандартные компоненты Windows. Одним из свойств является Canvas для создания сложных графических изображений управляющих элементов. Timage ~ класс-наследник TCustomControl Базовый класс для управляющих элементов, которые разрабатываются не на основе компонентов Windows. Созданным на его основе компонентам на форме может быть присвоен фокус. Свойство Canvas позволяет создавать сложные графические изображения управляющих элементов TObject, TControl и кое-что еще?
На первый взгляд, достаточно было бы использовать в качестве базово¬ го класса TGraphicControl, так как мы желаем нарисовать круглую кноп¬ ку. Сделать это может любой объект, имеющий свойство TCanvas. Так, класс Tlmage предлагает даже больше возможностей, чем нам необхо¬ димо, обладая, например, свойством TPicture. Но зачем наследовать ненужные свойства и таскать их за собой? Еще более интересным представляется класс TCustomControl. Объекты такого типа реагируют не только на щелчок мыши, но и на события, свя¬ занные с клавиатурой. Кроме того, можно присвоить фокус объекту CustomControl. Он напоминает мне объект класса TButton, который мы не будем использовать, поскольку хотим создать круглую кнопку. 12. Виртуальные методы Как для TCustomControl, так и для TButton прародителем является TControl. Класс TControl — базовый класс всех управляющих элементов, то есть объектов, которые, располагаясь на форме, управляют процессом работы. Но TControl не умеет редактировать графические объекты. Если бы мы решились создать экземпляр непосредственно на основе класса TCustomControl, мы бы его не увидели. Метод, занимающийся графическим отображением, мы предварительно опишем самостоя¬ тельно. Поскольку новый класс станет компонентом, сначала необходимо по¬ вторить все, что мы делали в главах 9 и 10, создавая компонент TMovie. ■ Создай новый проект, указав в меню FiLE команду №w Аррисдтюы. Со¬ храни проект в файлах POLY1.CPP и ООРЗ.МАК.
■> Укажи в меню COMPONENT команду №w. ■^Зарегистрируй новый класс в диало¬ говом окне COMPONENT WizARD. В тек- стовое поле CLASS ЫдмЕ введи TOButton, а в текстовое поле ANCESTOR TYPE введи TCustomControl. Для списка PALETTE PAGE введи Additional, затем нажми кнопку ОК. ^Сохрани новый модуль в файле OBUTTON1.CPP (меню FiLE, команда SAVE As). А C++Builder создаст для тебя следующий исходный текст (OBUTTON1.CPP): #include <vcl\vcl.h> #pragma hdrstop #include "Obuttonl.h" // static inline TOButton *ValidCtrCheck() return new TOButton(NULL); // fastcall TOButton::TOButton(TComponent* Owner) : TCustomControl(Owner) / / namespace Obuttonl { void _fastcall Register() { TComponentClass classes[l] = { classid(TOButton)}; RegisterComponents("Additional", classes, 0); } TObject, TControl и кое-что еще?
Почему кнопка называется OButton? Мне хотелось, чтобы имя отражало особенность создаваемой кнопки, то есть ее круглую форму. Именно поэтому в названии компонента появилась буква «0». Круглая кнопка Первый метод, который мы опишем для новой кнопки, называется Paint. Он унаследован от ?CustomControl и заново отображает кнопку, если появляются какие-либо изменения. В унаследованном методе нет ни одной графической команды. Мы самостоятельно должны определить в нем, как будет выглядеть новая кнопка. Воспользуемся командой Ellipse. Для того чтобы вид кнопки в нерабо¬ чем состоянии отличался от ее вида, когда она нажата, следует опреде¬ ленным образом отметить контур эллипса. Предлагаю воспользоваться предопределенными цветовыми констан¬ тами, с которыми мы уже работали в главе 8. Возьмем четыре оттенка серого и объединим их в одном массиве: TCoior 0Color[4] = {clBlack, clGray, clSilver, clWhite}; Если мы поочередно обведем эллипс двумя линиями разного цвета, то сымитируем вид кнопки в нажатом и нерабочем состояниях. Поскольку речь идет только о контуре эллипса, я применю метод Arc, который объект Canvas использует для рисования дуг: Arc (xl, yl, x2, y2, fromX, fromY, toX, toY); Первые четыре параметра имеют то же назначение, что и при рисовании эллипса. Они задают прямоугольную рамку, в которой располагается дуга. Следующие четыре значения обозначают координаты ее началь¬ ной и конечной точек. 12. Виртуальные методы
Для того чтобы найти их, необходимо провести от центра прямоуголь¬ ника линию к точкам, заданным координатами fromX/fromY и toX/toY. Мои объяснения станут понятнее, когда ты попробуешь начертить дугу самостоятельно. Например, следующие команды создают дуги в четы¬ рех углах прямоугольной области (дуги чертятся в направлении против часовой стрелки): xM = (х1 + x2) / 2; yM = (у1 + y2) / 2; Canvas->Arc (xl,yl,x2,y2, x2,yM,xl,yM); // сверху Canvas->Arc (xl,yl,x2,y2, xl,yM,x2,yM); // снизу Canvas->Arc' (xl,yl,x2,y2, xM,yl,xM,y2); // слева Canvas->Arc (xl,yl,x2,y2, xM,y2,xM,yl); // справа В нерабочем состоянии край создаваемой нами кнопки становится свет¬ лее по направлению к центру (цвет должен переходить от черного к бе¬ лому): for (int i=0; i<4; i++) { SetColor (OColor[i], OColor[i]); Canvas->Arc (i, i, Width-i, Height-i, i, i, i, i); } Поскольку мы хотим начертить целые дуги (незакрашенный эллипс), а не их половинки, начальная и конечная точки совпадают. По этой причи¬ не четыре раза в качестве координаты используется переменная i. Край кнопки должен стать темнее, если она нажимается в результате щелчка мышью. Это можно обеспечить следующими командами: for (int i=0; i<4; i++) { SetColor (OColor[i/2], 0Color[i/2]); Canvas->Arc (i, i, Width-i, Height-i, i, i, i, i); } Метод SetColor не наследуется от TCustomControl и его необходимо опи¬ сать: Круглая кнопка
void fastcall TOButton::SetColor(TColor OBrush, TColor OPen) { Canvas->Brush->Color = OBrush; Canvas->Pen->Color = OPen; } Этот метод определяет цвет, которым чертится дуга эллипса, и цвет, закрашивающий ограниченную фигурой область. Таким образом, мы переходим к новому методу Paint для кнопки TOButton. ♦ Введи следующий исходный текст в файле модуля ,OBUTTON1.CPP: void fastcall TOButton::Paint (void) { // толщина линии Canvas->Pen->Width = 2; // кнопка OButton нажата if (Pressed) { for (int i=l; i<5; i++) { SetColor (0Color[(i-l)/2], OColor[(i-1)/2]); Canvas->Arc (i, i, Width-i, Height-i, i, i, i, i); } } // кнопка OButton не нажата else { for (int i=l; i<5; i++) { SetColor (OColor[i-1], OColor[i-l]); Canvas->Arc (i, i, Width-i, Height-i, i, i, i, i); } } // поверхность кнопки OButton SetColor (Color, Color); Canvas->Ellipse (5, 5, Width-5, Height-5); 12. Виртуальные методы
Как видишь, изменилось содержание циклов for. Для того чтобы огра¬ ничивающая кнопку линия была более заметна, ее толщина равна 2 (прежде это значение было равным 1): Canvas->Pen->Width = 2; Счетчик / соответственно получает другое значение. Сначала с помо¬ щью функции Arc чертится край кнопки, а Eliipse позволяет нарисовать ее поверхность. Мента сбывается Кроме метода SetColor созданный нами класс получает еще два новых свойства: bool Pressed; TColor Color; Свойство Pressed принимает значение true, если кнопка нажата, в про¬ тивном случае — false. Свойство Color присваивает ей цвет. Этим двум свойствам в конструкторе присваиваются начальные значе¬ ния. Необходимо также задать размер кнопки. Следуя этим условиям, конструктор приобретает следующий вид: fastcall TOButton::?OButton(?Component* Owner) : TCustomControl(Owner) { Color = clBtnFace; SetSize (Rect (0, 0, 50, 50)); Pressed = false; } Свойство Color сначала получает стандартное значение Windows для цвета кнопки. Поскольку кнопка еще не нажата, свойству Pressed при¬ сваивается значение false. Команда SetSize вызывает метод, который размещает круглую кнопку в левом верхнем углу формы. Как и SetColor, этот метод необходимо описать: void fastcall TOButton::SetSize (TRect Rectangle) { Left = Rectangle.Left; Мечта сбывается
Top = Rectangle.Top; Width = Rectangle.Right - Rectangle.Left; Height = Rectangle.Bottom - Rectangle.Top; } Мы уже видели что-то подобное. У TMovie был такой же метод. И, как и в нем, в классе TOButton метод SetSize объявляется в разделе public, что¬ бы можно было из других модулей менять размер и положение кнопки. ♦ Введи исходный текст метода SetSize в файл модуля OBUTTON1.CPP. ♦ Дополни блок объявлений класса TOButton соответствующими коман¬ дами (OBUTTON1.H): class TOButton : public TCustomControl { private: bool Pressed; TColor Color; void fastcall SetColor(TColor OBrush, TColor OPen); protected: virtual void fastcall Paint(void); public: void fastcall SetSize (TRect Rectangle); fastcall TOButton(TComponent* Owner); published: }; Благодаря объявлению в разделе public конструктор и метод опреде¬ ления размера кнопки стали открытыми. Поскольку свойства Pressed, Color, а также SetColor не имеют значения за пределами метода OButton, они объявлены в разделе private. Метод Paint, напротив, должен быть частично открыт (protected), чтобы созданный класс мог им воспользоваться. Paint не может быть активирован из формы. (К сто¬ ящему перед объявлением метода слову virtual я вернусь позже.) После того как новый класс объявлен, нам необходимо для формы создать его экземпляр. Для этого обратимся к файлам POLY1.H и POLY1.CPP. 12. Виртуальные методы
^Открой заголовочный файл POLY1.H. Дополни TForml следующим эк¬ земпляром класса TOButton: class TForml : public TForm { published: // используемые ИСР компоненты void fastcall FomCreate(TObject *Sender); private: // объявления пользователя TOButton *Obuttonl; public: // объявления пользователя fastcall TForml(TComponent* Owner); } ; 4 Дважды щелкни мышью рабочую область формы и введи соответствую¬ щие команды в метод FormCreate (POLY1.CPP): void fastcall TForml::FormCreate(TObject *Sender) { Obuttonl = new TOButton (this); Obuttonl->Parent = this; Obuttonl->SetSize (Rect(120,60,ClientWidth-120,ClientHeight-60)); } 4 После сохранения (OOP3, POLY1, OBUTTON1) запусти программу, что¬ бы провести первую проверку. MouseDown и MouseUp На форме появилась круглая кнопка. Но сколько бы ты ее ни нажимал, ничего не происходит. Откуда ей знать, что она должна вести себя как нажатая кнопка? До сих пор мы занимались только ее внешним видом. Теперь пришло время сделать так, чтобы при щелчке мышью менялось значение свой¬ ства Pressed. MouseDown и MouseUp
Метод Click для нашего случая не подходит, поскольку реакция должна следовать после того, как кнопка мыши нажата или отпущена. Методы, которые активируются в результате какого-либо события, нам предос¬ тавляет TCustomControl. Необходимо только немного изменить их в со¬ ответствии с заданными условиями. Событие Метод Pressed = Кнопка мыши нажата MouseDown true Кнопка мыши отпущена MouseUp false Так выглядит исходный текст методов (OBUTTON1.CPP): void fastcall TOButton::MouseDown (TMouseButton Button, Classes::TShiftState Shift, int X, int Y) { TCustomControl::MouseDown (Button, Shift, X, Y); if (Button == mbLeft) { Pressed = true; Paint (); } // void fastcall TOButton::MouseUp (TMouseButton Button, Classes::TShiftState Shift, int X, int Y) { TCustomControl::MouseUp (Button, Shift, X, Y); Pressed = false; Paint (); } В качестве параметра оба метода присваивают.переменной Button код нажатой кнопки (левой, средней, правой), а в переменную Shift зано¬ сится информация о том, нажата ли одновременно на клавиатуре какая- либо клавиша (Shift, Ctrl или Alt). Переменные X и Y соответствуют положению курсора мыши. 12. Виртуальные методы
Странно, но сначала еще раз вызываются методы MouseDown и MouseUp, и именно в том виде, в каком класс TOButton унаследовал их от TCustomControl: TCustomControl::MouseDown (Button, Shift, X, Y); TCustomControl::MouseUp (Button, Shift, X, Y); С чем-то похожим мы встречались в конструкторе. Только вызов стоял в верхней строке, сразу за именем созданного метода: TOButton::TOButton(TComponent* Owner) : TCustomControl(Owner) Таким образом, мы впервые воспользовались всеми унаследованными от родительского класса возможностями и вставили дополнительные ко¬ манды. В методе MouseDown в цикле if опрашивается, какая кнопка мыши нажа- та. Если задействована левая кнопка мыши (Button == mbLeft), то проис¬ ходит нажатие круглой кнопки. ♦ Введи исходный текст двух методов управления кнопкой мыши в файл модуля OBUTTON1.CPP. ♦ Объяви два метода в заголовочном файле OBUTTONt.H в разделе protected: class TOButton : public TCustomControl { private: bool Pressed; TColor Color; void fastcall SetColor(TColor OBrush, TColor OPen); protected: virtual void fastcall Paint(void); virtual void fastcall HouseDown (TMouseButton Button, Classes::TShiftState Shift, int X, int Y); virtual void fastcall MouseUp (TMouseButton Button, Ciasses::T'ShiftState Shift, int X, int Y); public: void fastcall SetSize (TRect Rectangle); fastcall TOButton(TComponent* Owner); published: }; MouseDown и MouseUp
^Сохрани весь проект еще раз и запусти программу (ООРЗ.МАК). Кнопка считается нажатой, если ее щелкают мышью. Как только кнопка мыши отпускается, круглая кнопка принимает свое обычное состояние. Открытые события После размещения круглой кнопки в палитре компонентов C++Builder тебя постигнет разочарование. Тебе удастся расположить этот компо¬ нент с помощью мыши на форме, даже изменить его размер, но не бо¬ лее того. В инспекторе объектов ты увидишь некоторые свойства ново¬ го компонента, но на закладке событий тебя встретит холодная пустота. Новая кнопка OButton почти ничего не умеет. Она только реагирует на события, связанные с мышью, несмотря на то что родительский метод TCustomControl обладает большими возможностями. Все методы ново¬ го класса объявлены в разделе protected. Это значит, что методы рабо¬ тают только внутри созданного класса, а вне его ими управлять нельзя. Нам необходимо все события, которые могут понадобиться вне со¬ зданного класса, сделать открытыми. Оператор public для этой цели 12. Виртуальные методы
не годится. Поскольку мы хотим видеть описанные события и в инспек¬ торе объектов, следует объявить их в разделе published (OBUTTON1.H): class TOButton : public TCustomControl { private: bool Pressed; TColor Color; void fastcall SetColor(TColor OBrush, TColor OPen); protected: virtual void fastcall Paint(void); virtual void fastcall MouseDown (TMouseButton Button, Classes::TShiftState Shift, int X, int Y); virtual void fastcall MouseUp (TMouseButton Button, Classes::TShiftState Shift, int X, int Y); public: void fastcall SetSize (TRect Rectangle); fastcall TOButton(TComponent* Owner); published: property OnClick; property OnMouseDown; property OnMouseUp; property OnEnter; property OnExit; property OnKeyPress; property OnXeyDown; property OnKeyUp; }; В этом коде встречается новое слово property, и снова используются два знака подчеркивания. Таким образом, вводится объявление, а название события начинается с 0n. Из всех возможных событий я выбрал следующие (хотя можно было бы обойтись одним — самым первым): property OnClick; // левой кнопкой мыши щелкают компонент property OnMouseDown; // кнопку мыши нажимают в момент указания / / ее курсора на объект property OnMouseUp; // кнопку мыши отпускают в момент указания // ее курсора на объект property OnEnter; // элемент получает фокус Открытые события
property OnExit; // элемент теряет фокус property OnKeyPress; // нажимают клавишу символа property OnKeyDown; // нажимают (любую) клавишу property OnKeyUp; // отпускают (любую) клавишу Теперь ничто не сможет помешать размещению компонента в палитре компонентов. (Как это делается, см. в главе 10 пример размещения ком¬ понента Movie). Тебя интересует, почему для класса TMovie нам не понадобился оператор объявления property? Очень просто: TMovie создан на основе Tlmage, а события этого класса опубликованы. События класса TCustomControl объявлены, к сожалению, в другом разде¬ ле. Поэтому для преемников TCustomControl необходимо сначала объявить свойства как опубликованные, а для наследников Tlmage в этом необходимости нет. Как узнать, является ли событие опубликованным или нет? При уста¬ новке нового компонента закладка с событиями в инспекторе объектов остается пустой, если они не опубликованы. В против¬ ном случае они отображаются в нем, и их можно присоединить к методам. О старых и новых методах Нет ничего нового в том, что мы применяем методы, которые один класс наследует у другого. Так например, не задумываясь, мы пользовались в TMovie методами Hide и Show, хотя и не знали, из чего они состоят. Нам достаточно было того, что первый прячет объект, а второй его отобра¬ жает. Унаследованные методы брались в том виде, в каком они приме¬ нялись в родительском классе. 12. Виртуальные методы
С конструктором все было немного иначе. Несмотря на то что старый метод уже предлагал в качестве наследства конструктор, для нового класса мы описали свой конструктор. У него было другое имя. Удивительно, что прежде чем начать действовать, он вызывал имя конструктора родителя. Каждый раз описываемым нами методам присваивались имена, отлич¬ ные от унаследованных. Однако на методы Paint, MouseDown и MouseUp, которые TOButton получил от TCustomControl, это правило не распрост¬ ранилось. Если мы хотим начертить (или нарисовать) что-либо, нам следует при¬ своить методу Paint другое имя, а не оставлять его прежним, так как оно занято родительским классом. Тем не менее, метод Paint работает в новом классе, несмотря на нару¬ шение правила наименования. Если бы мы его удалили и вызвали при¬ надлежащий классу TCustomControl метод, то не увидели бы на экране результатов его работы. У нас не было другого выбора, кроме как объя¬ вить свой собственный метод Paint! Почему же система C++Builder не заметила, что мы использовали одно и то же имя? C++Builder должна была бы сказать «Уже занято» или «Имя уже определено». В C++Builder у классов-наследников могут быть методы с именами, ис¬ пользуемыми в родительском классе. Дело в том, что при вызове, например, Paint, MouseDown или MouseUp используется метод текущего объекта. Так в чем же проблема, спросишь ты? Как видно по методам Mouse, су¬ ществует особый способ обращения к старым родительским методам. В нем следует указать имя родительского класса и через двойное двое¬ точие имена вызываемых методов: ТРодитель::Метод (Параметр); А как из родительского метода вызвать наследника? Возможно ли это? Рассмотрим небольшой пример. Совершим прыжок во времени, отправившись на несколько веков на¬ зад. Мы посетим лабораторию доктора Франкенштейна, чтобы посмот¬ реть, как он работает. Он создал примерно такой класс: О старых и новых методах
class TMonster { private: String Name; String Gist; public: void Appear(void); TMonster (String N, String W); } ; Очень простой монстр, у которого есть имя и одно-единственное свой¬ ство. Тем не менее, он полон сил. Доктор Франкенштейн описал следу¬ ющие методы: TMonster::TMonster (StringN, StringW) { Name = N; Gist = W; } // void TMonster::Appear (void) { Forml->Labell->Caption = "Имя: '' + Name; Forml->Label2->Caption = "Суть: " + Gist; } Небольшое семейство монстров У доктора Франкенштейна не было такого удобного инстру¬ мента, как у нас. В нашем тесте все будет не так ужасно. Для те¬ стовой формы нам понадобятся три кнопки и три метки. 12. Виртуальные методы
Как видно по надписям, доктор Франкенштейн собирается создать еще двух детишек TMonster. Мы последуем за ним: class TGMonster : public TMonster { public: StringTyp (void) {return ''GeniusMonster";}; TGMonster (String N, String W) ; }; // class TSMonster : public TMonster { public: StringTyp (void) {return "SoulMonster";}; TSMonster (StringN, StringW); }; Новые создания пока не располагают большими возможностями. Док¬ тор Франкенштейн был слишком оптимистичен в своих взглядах на дух (Genius) и душу (Soul). Но для наших целей достаточно созданных отпрысков класса TMonster. Им, кажется, известно, из какого классаони родом. 06 этом позаботился метод Тур: StringTyp (void) {return "Умныймонстр";}; String Тур (void) {return “Добрый монстр";} ; Нет ничего особенного в том, что наследники класса TMonster возвращают строку. Необычно то, что они полностью определены в объявлении класса. Такой способ называют внутренним методом или описанием inline. В принципе, данный способ объявления возможен, но, как правило, им не пользуются, так как очень сильно «раздувается» описание класса. Однако для короткого операторного блока необязательно создавать отдельное описание. Оно могло бы выглядеть следующим образом: String TGMonster::Typ (void) { return "Умный монстр"; } Небольшое семейство монстров
String TSMonster::Typ (void) { return "Добрый монстр"; } Конечно, TMonster мог бы выдержать и такой метод: StringTyp (void) {return "Монстр";}; В метод появления монстра необходимо добавить следующие команды: void TMonster::Appear (void) { Forml->Labell->Caption = "Имя: '' + Name; Forml->Label2->Caption = "Суть: " + Gist; Forml->Label3->Caption = "Тип: } " + Тур(); Нельзя забывать и о конструкторах новых классов: TGMonster::TGMonster (String N, String W) : TMonster (N, W) { } II TSMonster::TSMonster (String N, String W) : TMonster (N, W) { } Все классы организованы таким образом, что нам остается только со¬ здать три экземпляра: TMonster *Frank; TGMonster *Albert; TSMonster *Sigmund; Инициализация, как всегда, осуществляется в методе FormCreate: 12. Виртуальные методы
void fastcall TForrnl::FormCreate(TObject *Sender) { Frank = new TMonster ("Франки", "необычный"); Albert = newTGKonszer ("Берти", "решительный"); Sigmund = new TSMonster ("Зиги", "сочувствующий"); } А методы трех кнопок соответственно организуют появление монстров: void fastcall TForml::ButtonlClick(TObject *Sender) { Frank->Appear (); } // void fastcall TForml::Button2Click(T0bject *Sender) { Albert->Appear (); } // ' void fastcall TForml::Button3Click(T0bject *Sender) { Sigmund->Appear (); } ■^ Создай новое приложение и введи весь исходный текст в файл модуля. (Объявления классов и экземпляров располагаются перед строкой TForml *Forml;, описания методов даются ниже.) ^Сохрани весь проект в файлах FSTEIN1.MAK и MONSTER1.CPP. Запу¬ сти программу и последовательно нажми три кнопки. Много слов, мало дел Итак, монстры могут сказать, как их зовут, и кое-что сообщить о своем нраве. Но о методе Тур наследники TMonster знают не больше, чем их родитель. Сложилась такая ситуация, будто мы вообще не объявляли методы Тур для TGMonster и TSMonster. Много слов, мало дел 11 -4109
Метод Тур вызывается в методе Appear (Появление). Последний из них получен от TMonster в качестве наследства, то есть является так называ¬ емым старым методом. Три монстра должны появиться по следующей схеме: Это значит, что поскольку переменные Albertи Sigmundиспользуют ме¬ тоды Appear класса TMonster, класс TMonster: :Appear использует также и метод Тур. Откуда программа знает, что в настоящий момент работает другой метод — Тур? Вызов нового метода из старого, похоже, невозможен. Вызываться могут только те методы, которые имеются в собственном классе. Следователь¬ но, объявления TGMonster: :Typ и TSMonster: :?yp не имеют смысла? Неужели система C++Builder настолько несовершенна, что не мо¬ жет определить, какой метод работает? Что происходит, когда мы запускаем программу? Весь проект вместе с формой и исходным текстом превращается в приложение. Его содержимое ты вряд ли разберешь, так как оно излагается на понятном компьютеру языке. Перевод осуществля¬ ет компилятор C++Builder (переводчик с языка программирова¬ ния С++ на язык компьютера). Во время компиляции (процесса перевода) сначала происходит обращение к месту, в котором описан метод Appear. В этом месте он указываетадрес, по которому находится TMonster: :Typ, посколь¬ ку такая последовательность действий была запрограммирована в исходном тексте. void TMonster::Appear (void) { Frank Albert Sigmund TMonster..Appear () TMonster::Appear () TMonster::Appear' () TMonster::Typ {) TMonster::Typ () TMonster::Typ () 12. Виртуальные методы
// остальные команды Forml->Label3->Caption = "Тур: " + Typ(); // имеется в виду TMonster::Typ{) ! } Далее компилятор обращается к тому месту, где вызывается метод Appear, и вставляет адрес, по которому можно найти TMonster:: Appear. То же самое он выполняет каждый раз, когда встречает мётод Appear. Поскольку адрес TMonster: :Typ уже определен, то другие два ме¬ тода Тур не учитываются. C++Builder предлагает для этой задачи свое решение. Необходимо толь¬ ко перед соответствующим методом вставить одно небольшое слово, чтобы в определенный момент вызывался нужный метод. 4 Дополни объявление классов от TMonster до TSMonster следующим об¬ разом: class TMonster { private: String Name; String Gist; public: void Appear (void) ; virtual StringTyp (void) {return "Монстр";}; TMonster (String N, String W) ; } ; class TGMonster : public TMonster { public: virtual StringTyp (void) {return "Умныймонстр";}; TGMonster (String M, String W); }; class TSMonster : public TMonster { Много слов, мало дел
public: virtual StringTyp (void) {return "Добрыймонстр";}; TSMonster (String N, String W) ; } ; 4 Сохрани проект и запусти программу (FSTEIN2.MAK, MONSTER2.CPP). Обрати внимание, как монстры сообщают о своем типе на этот раз. Полиморфизм Слово virtual (виртуальный, или возможный) все расставляет на свои места и улаживает ситуацию. Таким образом, существует возможность вызывать описанный этим чудо-словом элемент как из старого, так и из нового методов. Появление трех монстров происходит по следующей схеме: Объект Метод Appear Метод Тур Frank TMonster::Appear о TMonster::Typ {) Albert TMonster::Appear () TGMonster::Typ () Sigmund TMonster::Appear () TSMonster::Typ () В С++ метод вызывается следующими двумя способами. • При переводе исходного текста компилятор вставляет в то мес¬ то, откуда вызывается метод, его адрес. В результате опреде¬ ляется используемый метод. Этот способ называется ранним связыванием (early binding). Такой объявляемый обычным спо¬ собом метод называют статическим. • При переводе исходного текста компилятор выделяет место (ос¬ вобождая его), где вызывается метод. Адрес соответствующе¬ го метода вставляется на этапе выполнения программы. Такой способ называют динамическим связыванием (late binding). Он объявляется оператором virtual, а вызываемые этим спо¬ собом методы называются виртуальными. Две версии нашего проекта Монстр отличаются только одним словом, которое в последней из них встречается три раза. Вообще говоря, мы могли бы не использовать oбъявлeниevirtual в производимых классах 12. Виртуальные методы
TGMonster и TSMonster. Если метод объявлен виртуальным, то методы производимых классов тоже виртуальны, при условии что они полнос¬ тью соответствуют друг другу по типу и параметрам. Но оператор virtual все же нужен. Благодаря ему проверяется, дей¬ ствительно ли производимый метод является виртуальным. Что произойдет, если все методы описать как виртуальные? Это не так уж плохо, но следует принять во внимание следующее. При каждом виртуальном объявлении соответствующий метод по¬ лучает от C++Builder дополнительную память. Поскольку осуществляется динамическое (более позднее) связы¬ вание (то есть только в процессе выполнения программы сообща¬ ется, какой метод должен быть вызван), требуется дополнитель¬ ное время. (Так как при раннем связывании метод определяется уже при запуске программы, к моменту ее выполнения он уже из¬ вестен и может быть непосредственно активирован.) Это обстоятельство может иметь огромное значение, так как большие проекты часто состоят из тысяч методов. Итак, стоит тщательно продумать, какие методы объявлять вирту¬ альными. Если на основе TMonster не будет произведено других классов (TGMonster/TSMonster), то не имеет смысла объявлять ме¬ тод Appear как virtual. Однако при необходимости не составит большого труда сделать этот метод виртуальным. Мы располагаем тремя имеющими одинаковые имена методами, каж¬ дый из которых выполняет то, что требует обладающий им класс. Такую возможность называют полиморфизмом. (Слово «полиморфный» обо¬ значает «многосторонний», «принимающий разные образы».) Полиморфизм представляетсобой способность программы выбирать в про¬ цессе выполнения различные методы. Таким образом, при вызове метода TMonster: :Appear появляется именно тот метод, который необходим, — Тур. Полиморфизм
Виртуальный метод также можно назвать полиморфным. Если класс обладает хотя бы одним виртуальным методом, его называ¬ ют полиморфным. Виртуальные методы мы уже использовали в самом начале этой главы. Только таким образом можно было обеспечить беспрепятственную ра¬ боту старых методов класса TCustomControl с новыми методами класса TOButton. Кроме того, в самом начале книги мы применяли, не догадыва¬ ясь об этом, виртуальные методы размещаемых на формах компонентов. Если бы программа не обладала полиморфизмом, нам пришлось бы, например, метод Appear определять дважды таким образом: voidTGMonster::Appear (void) { TMonster::Appear (); Forml->Label3->Caption = "Тип: “ + Тур(); } // void TSMonster::Appear (void) { TMonster::Appear (); Forml->Label3->Caption = "Тип: " + Typ(); } Жалкую попытку обойтись без виртуальных методов ты можешь уви¬ деть в проекте FSTEIN1A.MAK (см. приложение D), схема появления в котором показывает, что даже в нем методы Тур вызываются в нужный момент. 12. Виртуальные методы
Frank Albert Sigmund TMonster::Appear () TMonster::Typ (} TGMonster::Appear {) TGMonster::Typ () TSMonster::Appear () TSMonster::Typ () Таким образом, данная программа работала бы так же, как если бы в ней были виртуальные методы. При этом она содержала бы больше ко¬ дов и была избыточной. В рассматриваемом случае избыточность не так и плоха Но представь, что будет, если метод станет вызываться сразу в нескольких местах? (Во всяком случае, я бы не хотел участвовать в создании такого монстра.) Когда появляется много методов с именем Тур, возникает еще один воп¬ рос: может ли в одном классе быть несколько методов с одним и тем же именем? Да, в С++ возможно и это. Такая ситуация называется пере¬ грузкой (overloading): virtual String Тур (void) {return "Умныймонстр";}; virtual void Тур (String Character) {Forrr.l->Label3->Caption = "У меня '' + Character;}; Эти два метода отличаются тем, что один является функцией без пара¬ метров (void), другой представляет собой процедуру со строкой в каче¬ стве параметра. Как можно использовать эти два метода одновремен¬ но, ты можешь увидеть в проекте FSTEIN2A.MAK (см. приложение D). Даже если методы Тур объявлены как виртуальные, полиморфны¬ ми являются только те из них, у которых одинаковый список пара¬ метров и одинаковый тип! Конструктор может быть перегружен, если списки параметров отличаются: TMonster (void); TMonster (String N, String W); TMonster (String N, String W, String T); Перегрузка методов Перегрузка методов
Все три конструктора служат для того, чтобы инициализировать свой¬ ства TMonster: TMonster::TMonster (void) { Name = "Никто"; Gist = "чудовищный"; Тур = "Монстр"; } // TMonster::TMonster (String N, String W) { Name = N; Gist = W; Тур = "Монстр"; // TMonster::TMonster (String N, String W, String T) { Каше = N; Gist = W; Тур = T; } При выполнении программы мы получили бы такой же результат, если бы третий конструктор вызывался с соответствующими значениями, учи¬ тывая, что Тур на этот раз объявлен как свойство, а не как метод. Выводы Пришло время покинуть лабораторию доктора Франкенштейна, чтобы сде¬ лать перерыв. В следующей главе ты расширишь знания в области програм¬ мирования. Но прежде подытожим все, чему ты научился в этой главе: Arc Eilipse TObject TControl TCustomControl TGraphicControl Метод класса TCanvas: рисует дугу Метод класса TCanvas: рисует эллипс Патриарх иерархии классов C++Builder Базовый класс управляющих элементов Базовый класс графических управляющих элементов, которые реагируют на события, связанные с мышью или клавиатурой. Этим элементам может быть присвоен фокус Базовый класс графических управляющих элементов, которые реагируют на события, связанные с мышью 12. Виртуальные методы
Paint MouseDown MouseUp property virtual Метод (в том числе классов TCustomControl и TGraphicControl) для рисования (отображения) объекта (должен быть самостоятельно описан) Метод (в том числе класса TControl): выполняется, когда кнопку мыши нажимают Метод (в том числе класса Tcontrol): выполняется, когда кнопку мыши отпускают Обозначение для события (OnXXX) Обозначение для виртуальных объявлений методов В таблице представлены три основных свойства объектно-ориентиро- ванного программирования: Инкапсуляция (Объединение) Наследование Полиморфизм Свойства (массивы данных) и методы (функции элементов) могут быть объединены в один модуль, класс Новые классы могут создаваться на основе уже существующих и перенимать их свойства и методы В зависимости от потребности, можно использовать одновременно несколько методов с одинаковыми названиями. Для этого базовый метод должен быть виртуальным Несколько вопросов 1. Можноли заменить MeTOflMouseDown наСНск и таким образом не при¬ менять MouseUp? 2. Что такое раннее и позднее (динамическое) связывание? 3. Можно ли конструктор описать в блоке объявления класса? и несколько задач 1. Расширь проект Монстр таким образом, чтобы для всех трех объек¬ тов отображались рисунки. 2. Определи, какие методы класса TMovie можно сделать виртуальными для создания новых классов, и измени соответствующим образом заголовочный файл Movie. Несколько вопросов..
3. В проекте OOP3 с помощью случайного значения создается цветная кнопка OButton. Дополни объявление TOButton конструктором, кото¬ рый в качестве параметра принимает значение цвета (Color). Затем вставь конструктор в FormCreate. 4. Создай на основе TOButton новую кнопку, которая включается и вык¬ лючается щелчком мыши. 5. Добавь в палитру компонентов C++Builder кнопку OButton. Создай проект, в котором при нажатии этой кнопки изменялось бы случай¬ ным образом изображение на панели. 12. Виртуальные методы
13. Полиморфные классы Программисту число 13 еще ни разу не приносило несчастья. Пожалуй, мы снова отправимся в лабораторию доктора Франкенштейна. В этой главе речь пойдет о нескольких особенностях классов С++, которые используются не очень часто. Но познакомиться с ними стоит, так как они иллюстрируют разнообразие языка программирования, что являет¬ ся одним из важнейших его достоинств. В этой главе ты узнаешь: • как работать с деструктором; • что такое множественное наследование; • что представляют собой виртуальные классы; • что подразумевают под чистыми виртуальными методами; • кое-что об абстрактных классах.
Шоу монстров Если тебе удалось выполнить первое задание предыдущей главы, то пе¬ ред тобой предстали три экземпляра семейства монстров. Если не уда¬ лось, сейчас у тебя появится возможность посмотреть на них. Сначала необходимо указать путь, чтобы C++Builder смог найти рисун¬ ки, например: const String Path = "c:\\cpp\\buch\\"; Кроме того, TMonster получает еще одно свойство типа String с именем Illustration. В нем будут запоминаться имена файлов с изображением соответствующего монстра. А теперь основательно займемся переделкой проекта FSTEIN2.MAK. Изменим расположение компонентов на форме, что приведет к измене¬ нию модуля MONSTER2.CPP. Форма выглядит следующим образом: Ниже приводится таблица компонентов: GroupBox1 Caption = Рисунок GroupBox2 Caption = Информация tmage1 Stretch = true, размещается в групповом окне GroupBox1 Label1 Label2 Размещается в групповом окне Groupbox2 Размещается в групповом окне Groupbox2 Label3 Размещается в групповом окне Groupbox2 Button1 Caption = Появись ■^ Когдаформабудет готова, дополни объявление класса (MONSTER4.CPP): 13. Полиморфные классы
class TMonster { private: String Name; String Gist; String Illustration; public: virtual void Appear (void) ; virtual StringTyp (void) {return "Монстр";}; TMonster (StringN, StringW, String File); }; class TGMonster : public TMonster { public: virtual StringTyp (void) {return "Умныймонстр";}; TGMonster (String N, String W, String File) ; }; class TSMonster : public TMonster { public: virtual StringTyp (void) {return "Добрыймонстр”;}; TSMonster (String N, String W, String File); }; Как видишь, в конструкторах на один параметр больше. Они уже во вре¬ мя инициализации принимают имя файла с рисунком. ♦ Добавь в объявление конструктора следующие строки (MONSTER4.CPP): TMonster::TMonster (String N, String W, String File) { Name = N; Gist = W; Illustration = File; } // TGMonster::TGMonster (String N, String W, String File) : TMonster (N, W, File) { Шоу монстров
} // TSMonster::TSMonster (String N, String W, String File) : 'TMonster (N, W, File) { } 4 Для метода TMonster:: Appear введи следующие команды (MONSTER4.CPP): void TMonster::Appear (void) { Foml->Labell->Caption = "Имя: "+ Name; Forml->Label2->Caption = "Суть: " + Gist; Foml->Label3->Caption = "Тип: " + Тур() ; Forml->Imagel->Picture->LoadFromFile (Path+Illustration); Forml->Imagel->Show (); } Метод Appear не ограничивается выводом на экран текста, компонент Image позволяет отображать и рисунок. Сначала для этого загружается файл с расширением BMP, затем Imagel делается видимым. (Эта команда понадобится нам позже, поскольку компонент Imagel впоследствии должен будет исчезнуть.) *> Сохрани весь проект в новых файлах (FSTEIN4.MAK, MONSTER4.CPP, 3 — порядковый номер проекта, создаваемого в задаче 1 главы 12). Еще один вид полиморфизма Прежде чем ты попробуешь запустить программу, учти, что мы работа¬ ем только с одной кнопкой, и поэтому нам необходим лишь один метод ButtonClick. Текущая версия программы будет случайным образом создавать мон¬ стров, отображать на экране и удалять их, используя одну и ту же кнопку. Для этого объявим экземпляр не трижды, а всего лишь один раз: TMonster *Who; 13. Полиморфные классы
Добавляются еще три переменные. Одна принимает значение случайного числа, которое соответствует номеру файласизображением появляюще¬ гося монстра, а другая определяет его возникновение и исчезновение: bool Modus; int Chance; Метод FormCreate запускает генератор случайных чисел и присваивает значение переменной Modus на true (на сей раз для появления чудища, так как на кнопке указано именно это действие). ♦ Дважды щелкни мышью рабочую область формы и введи эти команды (MONSTER4.CPP): void fastcall TForml::FormCreate(TObject *Sender) { randomize (); Modus = true; } ♦ Основную работу снова выполняет метод ButtonClick. Это означает, что тебе придется потрудиться и ввести соответствующие команды (MONSTER4.CPP): void fastcall TForml::ButtonlClick(TObject *Sender) { if (Modus) { Chance = random(3); switch (Chance) { case 0: Who = new TMonster ("Франки", "необычный", "Frank.bmp"); break; case 1: Who = new TGMonster ("Берти", "решительный'', “Albert. bmp”) ; break; Еже один вид полиморфизма
case 2: Who = new TSMonster ("Зиги", "сочувствующий", "Sigmund.bmp"); } Who->Appear (); Buttonl->Caption = "Исчезни"; } else { delete Who; Imagel->Hide (); Buttonl->Caption = "Появись"; } Modus = !Modus; } 4 Сохрани исправленную версию еще раз и запусти программу (FSTEIN4.MAK, MONSTER4.CPP). В результате на экране должны друг за другом появляться три различ¬ ных изображения. Как работает твоя программа? Когда Modus присвоено значение trae, выдается случайное число от 0 до 2 (в нашем распоряжении только три типа монстров): if (Modus) Chance = random(3); В структуре switch принимается решение о том, когда и какого монстра создает доктор Франкенштейн: switch (Chance) { case 0: Who = new TMonster ('' Франки", " необычный", " Frank. bmp"); break; case 1: Who = new TGMonster (" Берти", " решите льный'', " Albert. bmp") ; break; 13. Полиморфные классы
case 2: Who = new TSMonster (" Зиги'', " сочувствующий'', " Sigmund. bmp") ; } Данная операция должна происходить без проблем, несмотря на то что Who мы объявили как простой класс TMonster (точнее, как указатель на экземпляр TMonster). Это означает, что Who в состоянии принимать раз¬ личные образы, что тоже является проявлением полиморфизма. В случайно созданном образе должна появиться переменная Who: Who->Appear (); Тогда надпись кнопки соответствующим образом меняется: But.tonl->Caption = “Исчезни"; В самом конце метода значение Modus меняется на противоположное: // Modus = НЕ Modus превращает true в false, и наоборот Modus - !Modus; Деструкторы Не хватаеттолько варианта, когда значение Modus задается равным false. В этом случае соответствующий монстр должен исчезнуть, чтобы осво¬ бодить место для следующего (Who может одновременно принимать толь¬ ко один образ): else // if (!Modus) { delete Who; Imagel->Hide (); Buttonl->Caption = “Появись"; } Монстр становится невидимым, и на кнопке снова появляется надпись «Появись». (Наконец, значение Modus меняется на противоположное.) Оператор delete ты, наверное, еще помнишь по главе 6. Если оператор new служит для резервирования новому объекту места в оперативной памяти, оператор delete освобождает это место (и таким образом удаляет объект). Деструкторы
Но так безрассудно мы не станем применять оператор delete. Оператор new вызывает конструктор класса, который обеспечивает резервирова¬ ние соответствующего места в памяти и правильную инициализацию те¬ кущего объекта. Но что происходит при обратном процессе? При вызове delete этот опе¬ ратор наилучшим образом освобождает место, которое занималось объектом типа TMonster, поскольку Who объявлен как TMonster. Это значит, что освобождение памяти для других типов монстров не про¬ изойдет должным образом! Данные удалятся не полностью. В резуль¬ тате существенно уменьшится размер доступной памяти. Представь себе, что ты работаешь с большими объектами, которые за¬ нимают очень много места в оперативной памяти. Если после их удале¬ ния остается огромное количества мусора, то в какой-то момент может перестать выполняться вся программа (и Windows тоже). (Немало даже профессиональных программ страдает от этого недостатка.) Итак, мы хотим, чтобы после завершения небольшого шоу монстров ни один удаленный объект не оставлял за собой следов. Но как это сделать? По счастливой случайности в языке С++ для такого случая имеется свой метод, в противоположность конструктору называемый деструктором. Чем отличаются друг от друга эти методы? Конструктор отвечает за так называемое рождение объекта. Если ты не объявляешь конструктор в классе, то в С++ создается стан¬ дартный конструктор. В этом случае он выглядит следующим об¬ разом: ИмяКласса::ИмяКласса (void) {} Деструктор выполняет работу по «уборке», проводимую после удаления объекта. И в этом случае С++ по умолчанию автомати¬ чески создает стандартный деструктор, если ты не объявляешь его сам. Он выглядит следующим образом: 13. Полиморфные классы
ИмяКласса::~ИмяКласса (void) {} Если назвать конструктора акушером, то деструктор является мо¬ гильщиком. (Или это название слишком мрачное?) Внешнее отличие между ними заключается лишь в небольшой вол¬ нистой черте, которую называют тильдой. К тому же список пара¬ метров деструктора всегда пуст (= void). Так же, как и конструктор, деструктор не возвращает значения. Они оба не являются функциями. При использовании операторов new и delete язык С++ обеспечи¬ вает автоматический вызов и конструктора, и деструктора. Для деструктора достаточно следующего объявления: TMonster::~TMonster (void) Поскольку это стандартный деструктор, не нужно вносить в программу каких-либо изменений. Для того чтобы ты лучше уяснил сказанное, все же добавим в деструктор TMonster несколько команд. ♦ Дополни проект следующим объявлением (MONSTER5.CPP): TMonster::-TMonster (void) { Forml->Labell->Caption = "" Forml->Label2->Caption = "" Forml->Label3->Caption = "" Forml->Imagel->Hide(); } // TGMonster::~TGMonster (void) // TSMonster::~TSMonster (void) Деструкторы
Обоим деструкторам для TGMonster и TSMonster, кажется, нечем заняться. В действительности, они принимают такое же активное участие в про¬ грамме, как и методы их предков. В этом ты убедишься позже. Поскольку команда Hide теперь указана в деструкторе TMonster Forml->Imagel->Hide(); из блока else метода FormCreate ее можно убрать: else { delete Who; Buttonl->Caption = "Появись"; } Конструктивно или деструктивно? Всем трем классам монстров необходимо сообщить о том, что мы ре¬ шили определить для них собственные деструкторы: class TMonster { private: String Name; String Gist; String Illustration; public: virtual void Appear(void); virtual String Тур (void) {return "Монстр”;}; TMonster (String N, String W, String File); virtual ~TMonster (void) ; } ; class TGMonster : public TMonster { public: virtual String Тур (void) {return “Умный монстр";}; TGMonster (String N, String W, String File); virtual -TGMonster (void); }; 13. Полиморфные классы
class TSMonster : public TMonster { public: virtual String Typ (void) {return "Добрый монстр";}; TSMonster (String N, String W, String File); virtual ~TSMonster (void); }; ■^Дополни каждый из трех классов объявлением конструктора (MONSTER5.CPP). Как видишь, и деструкторы могут быть виртуальными. Это очень важное и почти всегда оправданное действие, которое позволяет перегружать деструктор при создании классов. Только таким образом в процессе выполнения программы может быть принято решение, какой из дест¬ рукторов должен быть использован. Могут ли конструкторы быть виртуальными? В С++ это невозмож¬ но, так как если бы конструктор стал виртуальным методом, его адрес вызова передавался бы только в процессе выполнения про¬ граммы. Но адрес, по которому можно найти конструктор, опре¬ деляется при инициализации. Таким образом, адрес конструктора предоставляется в распоряжение в момент вызова. Это означает, что виртуального конструктора быть не может. Тем не менее, С++ВиНс1егдопускает исключение. Если объявляет¬ ся и регистрируется класс компонента, то виртуальный конструк¬ тор возможен. Об этом особом случае я хотел бы лишь упомянуть, но не углубляться в объяснения. *^A теперь сохрани все результаты как новый проект (FSTEIN5.MAK, MONSTER5.CPP) и запусти программу. Интересно, что каждый раз при уходе монстра исчезает рисунок и текст, несмотря на то что деструкторы наследников монстров пусты. И нигде в исходном тексте не видно, чтобы вызывался деструктор •предка. Совершенно очевидно, что программа работает следующим образом: Конструктивно или деструктивно?
Monster GMonster SMonster "TMonster (} ~TGMonster () ~TSMonster () ~TMonster () ~TMonster () Создается ощущение, что ~TMonster вызывается автоматически. Может быть, теперь ты решишь, что оба деструктора монстров-наследников вообще не активируются. Это легко проверяется при добавлении в оба класса следующих строк: TGMonster::"TGMonster (void) { ShowMessage ("Я думаю о тебе!"); TSMonster::~TSMonster (void) { ShowMessage ("Я скучаю по тебе!”); } С этого момента дети вежливо прощаются, в то время как монстр-роди¬ тель исчезает. Если ты решишь вставить в деструктор TMonster метод ShowMessage, тебя ожидает сюрприз. Деструктор родителя вызывается только в самом конце (в отличие от конструктора). Следующие действия в конструкторе и деструкторе выполняются по-разному: • Конструктор родителя вызывается дополнительно и сразу же выполняется. (Затем только подходит очередь наследника.) • Деструктор родителя не нужно вызывать дополнительно, и он выполняется в последнюю очередь. (Сначала выполняет свою работу деструктор наследника.) Разнообразим шоу монстров несколькими дополнительными картинка¬ ми. Для этого попросим доктора Франкенштейна создать еще одного //■ Множественное наследование 13. Полиморфные классы
наследника. На сей раз он создаст существо, объединяющее свойства двух произведенных на свет монстров. Чудище будет добрым и умным. Очередной наследник выглядит следующим образом: class TXMonster : public TGMonster, public TSMonster { public: virtual String Тур (void) {return "Психомонстр";}; TXMonster (String N, String W, String File); virtual ~TXMonster (void); }; У нового монстра нет никаких особых свойств? Разве это нормально, что у него два родителя? Возможно ли существование двух мам или двух пап? Множественное наследование в С++ разрешает наличие двух ма¬ терей или двух отцов. У нового класса TXMonster есть все, что имеется у его предков (родите¬ лей и прародителя TMonster). Правильную инициализацию обеспечивает конструктор, чей список вызовов стал несколько длиннее, поскольку теперь у наследника два родителя: TXMonster::TXMonster (String N, String W, String File) : TGMonster (N, W, File), TSMonster (N, W, File) { } Напротив, деструктор имеет такой же, как у родительских классов, вид: TXMonster::~TXMonster (void) { } Прежде чем мы приступим к использованию нового монстра, следует пояснить возможное недоразумение: Множественное наследование
Проблема заключается в том, что новый класс TXMonster наследовал своего прародителя дважды: один раз от TGMonster, другой — от TSMonster. В обычной жизни это не так уж и плохо, особенно если наследство богатое. Но в нашем случае конструктор TMonster вызывается сразу два раза (один раз через TGMonster, второй — через TSMonster). Конфликт возникает при выполнении метода Appear. Откуда C++Builder узнает, по какой линии наследования ей использовать этот метод? Выйти из тупика нам поможет виртуальное объявление. На этот раз речь идет не о методе, а о классе, который необходимо сделать виртуаль¬ ным. Выбери, какой класс обозначается как virtual, например: class TGMonster : virtual public TKonster { public: , virtual String Тур (void) {return "Умныймонстр";}; 'TGMonster (StringN, StringW, String File); 13. Полиморфные классы
virtual "TGMonster (void); }; class TSMonster : virtual public TMonster { public: virtual String Typ (void) {return "Добрыймонстр";}; TSMonster (String N, String W, String File); virtual "TSMonster (void) ; }; Ho C++Builder на этом не останавливается. Поскольку TMonster стал лишь виртуальным классом, вызов конструктора TXMonster необходи¬ мо расширить. Таким образом, TXMonster обращается непосредственно к конструктору своего прародителя: TXMonster::TXMonster (String N, String W, String File) : TGMonster (N, W, File), TSMonster (N, W, File), TMonster (N, W, File) { } Этот процесс называется также виртуальным наследованием. Понятие «виртуальный класс» может привести к недоразумению, если объявить его непосредственно: virtual class TMonster // Ошибка !!! Эта попытка приведет к выдаче сообщения об ошибке. Кто следующий? Для того чтобы творения доктора Франкенштейна могли воспользоваться переменной Who, следует расширить метод ButtonClick (MONSTER6.CPP): void fastcall TForml::ButtonlClick(TObject *Sender) { if (Modus) Кто следующий?
Chance = random(5); switch (Chance) { case 0: Who = new TMonster (" Франки", 0 необычный'', ” Frank. bmp") ; break; case 1: Who = new TGMonster ("Берти *, "решительный”, "Albert.bmp”); break; case 2: Who = new TSMonster ("Зиги", "сочувствующий", "Sigmund.bmp"); break; case 3: Who = new TXMonster ("Jekyll”,."нерешительный", "Jekyll.bmp"); break; case 4: Who = new TXMonster (" Hyde", " своенравный", '' Hyde. bmp “) ; } Who->Appear (); Buttonl->Caption = "Исчезни"; } else { delete Who; Buttonl->Caption = “Появись"; Modus = !Modus; } } 4 Введи объявления для нового класса и соответствующие изменения в код метода. Дополни метод ButtonClick двумя (или несколькими) бло¬ ками case. Не забудь соответствующим образом увеличить значение для 13. Полиморфные классы
random. Сохрани все результаты в новом проекте (FSTEIN6.MAK, MONSTER6.CPP) и запусти программу. Друг за другом появляются члены семейства монстров (ты можешь за¬ менить их на свои картинки). Чистый и абстрактный Когдадоктор Франкенштейн создавал своего первого монстра, он имел неважное представление о творении, которое собирался оживить. В пе¬ реводе на язык нашей структуры классов его первые проекты выгляде¬ ли приблизительно так: class TThing { public: virtual String Тур (void) - 0; }; Наряду с созданными системой C++Builder стандартными конструк¬ торами и деструкторами у данного класса есть метод Тур. Доктор был убежден, что его семейке TThing этот метод когда-нибудь обязатель¬ но понадобится, но зачем, — он не знал. Поэтому метод ничего не объявлял. Чистый и абстрактный
В языке С++ нельзя не сделать объявления. Однако поскольку на дан¬ ной стадии программирования готового метода Тур еще не существует, об этом надо дать знать C++Builder. Для того чтобы показать системе разработки, что метод пока отсутствует, ему присваивают нулевое зна¬ чение: virtual Тур ИмяМетода (Параметр) = 0; Такой метод называют чистым виртуальным. Поскольку он ничем не за¬ нимается, на основе класса TThing нельзя создать новый объект. Но против использования следующего объявления C++Builder не возражает: TThing *Thing; Впрочем, как и против попытки инициализировать объект такого типа: Thing = new TThing; Класс, у которого есть только один чистый виртуальный метод, называ¬ ется абстрактным. (Поэтому данный метод называют также и абстрак¬ тно виртуальным.) Чем примечателен абстрактный класс? Он позволяет доктору Франкен¬ штейну использовать 7Th:ng для реализации творческих планов. Но чем нам может быть полезен этот класс? Он предоставляет базовую струк¬ туру, используемую для создания других классов, для которых опреде¬ лены не все методы. Итак, абстрактный класс представляет собой тот самый скелет, на ос¬ нове которого проектируются классы. Например, TControl является абстрактным классом, используемым в качестве основы для создания всех управляющих элементов. Он рас¬ полагает целым рядом свойств и методов. Но для того чтобы создать 13. Полиморфные классы
управляющий элемент, многие чистые виртуальные методы необходи¬ мо определить заново. Класс TCustomControl тоже является абстрактным. Только произведен¬ ный класс, в который в результате нового описания перегружаются все чисто виртуальные методы, подходит для создания объектов. (Приме¬ ром является созданный нами объект OButton.) Для доктора Франкенштейна TThing представляет собой основу для со¬ здания TMonster: class TMonster : public TThing { // метод Тур описывается заново! }; Класс TMonster можно было бы объявить абстрактным, если бы не монстр Frank, который появляется в нашем последнем проекте. Достаточно только знать, что собой представляет абстрактный класс, поскольку тебе вряд ли понадобится создавать его. C++Builder предла¬ гает такое разнообразие классов, что всегда найдется что-нибудь под¬ ходящее для твоих проектов. Выводы Тебе захотелось остановиться на обычном способе наследования? Ты решил не пользоваться виртуальными классами? Поскольку C++Builder предлагает достаточное количество абстрактных классов, вовсе не обя¬ зательно дополнять их семейство своими собственными разработками. Описанные в этой главе деструкторы ты сможешь использовать при не¬ обходимости. Мы узнали не так уж и много новых слов из лексикона С++ и системы разработки C++Builder: ShowMessage Отображает диалоговое окно с сообщением new Резервирует место в памяти для объекта delete Освобождает место в памяти, занимаемое объектом Обозначение деструктора (тильда) Выводы ж
Вопросов нет... ... но есть одна задача 1. Рассмотри блоки объявления классов в старых проектах и подумай над тем, имеет ли смысл вставить в них собственный деструктор? 13. Полиморфные классы
14. Всякая всячина Эта глава завершает наше путешествие. В ней будут представлены еще некоторые возможности системы разработки C++Builder. Надеюсь, тебе удастся из предложенных в данной главе блюд выбрать наиболее лакомый кусочек, который ты сможешь использовать в разработках. В этой главе ты научишься: • работать с компонентом TiMER (ТдйМЕР); • перегружать операторы; • использовать новые операторы для адресации и указателей; • отличать входные и ссылочные параметры; • работать с шаблонами.
Парад монстров Давай, в последний раз насладимся шоу монстров. Они будут появлять¬ ся непрерывно, как при просмотре диапозитивов, пока мы их не остано¬ вим нажатием кнопки. Для этого с разрешения доктора Франкенштейна мы проведем над клас¬ сом TMonster одну операцию. Koe^rro придется выбросить из программы. ■^Сократи для начала объявление класса следующим образом (MONSTER7.CPP): class TMonster { public: virtual void Appear(String Illustration) ; }; Для решения поставленной задачи мы довольствуемся стандартными конструктором и деструктором. В методе Appear нам потребуется ука¬ зать лишь один параметр: void TMonster::Appear (String Illustration) ! i StringName = Hlustration.SubString(l, Illustration.Length()-4); Forml->Imagel->Picture->LoadFromFile (Path+Illustration) ; Forml->Panell->Caption = Name; } В качестве параметра передается только имя содержащего картинку файла. Имя этого файла без расширения (.BMP) становится именем монстра: Name = Illustration.SubString(l, Illustration.Length()-4); Рядом появляются картинка и имя: Forml->Imagel->Picture->LoadFromFile (Path+Illustration); Forml->Panell->Caption = Name; 14. Всякая всячина
В этом проекте мы откажемся от использования всех групповых окон GroupBox и компонентов Label. Мы разместим другой объект на Panel1 рядом с lmage1. С этим компонентом мы пока еще не знакомы. Для того чтобы картинки появлялись автоматически через определенные промежутки времени, цикл (с while или do-while) не подойдет. Было бы удобнее, если бы спо¬ соб отображения картинок на экране находился не в методе ButtonCl ick. И на этот раз на помощь приходит C++Builder. В палитре компонентов есть компонент Timer, который мы и разместим на нашей форме: ♦ Создай изображенную на рисунке форму. ^ Перейди в палитре компонентов на закладку SYSTEM (СистЕМА) и щелкни мышью значок компонента TiMER. ♦ Расположи компонент на форме. Компонент Timer Компоненты, с которыми мы до сих пор имели дело, были видимыми. Объект типа TTimer устроен несколько иначе. В инспекторе объектов ты увидишь, что у этого компонента сравнительно мало свойств. Нас могут заинтересовать два из них: Enabled определяет, включен (true) или выключен таймер (false) Interval определяет временной интервал, через который вступает в действие таймер (в миллисекундах) Компонент Timer 12-4109
■^ Дважды щелкни на форме значок TiMER. Перепиши в методе TimerlTimer следующую конструкцию switch (MONSTER7.CPP): void fastcall TForml::TimerlTimer(TObject *Sender) { Chance = random(5); switch (Chance) { case 0: Who->Appear break; case 1: ("Frank.bmp”); Who->Appear break; case 2: ("Albert.bmp"); Who->Appear break; case 3: (''Sigmund.bmp") ; Who->Appear ("Jekyll.bmp"); break; case 4: Who->Appear } } ("Hyde.bmp''); Тебе знакома эта конструкция по другим проектам? Поскольку мы ра¬ ботаем только с одним классом TMonster (остальные члены семейства остаются дома), мы можем отказаться от постоянного использования операторов new и delete. В проекте будут только загружаться и отобра¬ жаться файлы с рисунками. Инициализация монстра (в этом случае только одного) осуществляется в методе FormCreate следующим образом: void fastcall TForml::FormCreate(TObject *Sender) { randomize (); Who = new TMonster; 14. Всякая всячина
Timerl->Interval = 500; Timerl->Enabled = false; Modus = true; } Интервал для метода TimerlTimer устанавливается равным половине секунды (500 миллисекунд). А сам таймер выключается только один раз (Enabled = false). Для запуска и завершения шоу монстров нам не хватает только метода ButtonClick: void fastcall TForml::ButtonlClick(TObject *Sender) { if (Modus) // = Начало { Timerl->Enabled = true; Buttonl->Caption = "Стоп"; } else // = Стоп { Timerl->Enabled = false; Buttonl->Caption = "Начало"; ) Modus = !Modus; } При нажатии кнопки НдчАло таймер включается (true), а надпись кнопки меняется на Стоп. Если же нажимается кнопка Стоп, то таймер выклю¬ чается, и надпись кнопки меняется на НАЧАЛО. Если свойству Timerl->Enabled задано значение true, то метод TimerlTimer каждые полсекунды активирует появление одного монстра и его имени. ^Дополни этот метод и сохрани все результаты в новом проекте (FSTEIN7.MAK, MONSTER7.CPP). Запусти программу. Компонент Timer
Перегрузка операторов Раз уж в языке С++ так много операторов, давай, воспользуемся этим обстоятельством. Напомню на всякий случай, что в С++ двойной плюс (++) и двойной минус ( ) соответственно увеличивают и уменьшают значение числа. Эти операторы мне понадобятся для последней версии проекта Монстр. Для того чтобы каждый раз не вызывать монстра нажатием кнопки, да¬ вай, вставим в текст кода двойной знак. Ты, наверное, догадываешься, что в С++ существует возможность за¬ ново определять операторы. Мы не будем, конечно, менять значение плюса на минус (хотя в принципе возможно и это). Я займусь переопределением двойного знака («) (два знака «мень¬ ше»). Так же, как и знак (») (два знака «больше»), данный оператор в С++ уже неоднократно переопределялся. Пример этого ты найдешь в главе 11. Давай, используем оператор («) для того, чтобы выводить данные о монстре (картинку и имя). Заменим в объявлении класса метод Appear оператором объявления: class TMonster { public: virtual void operator << (String Illustration); } ; 14. Всякая всячина
Данную операцию называют перегрузкой. Это понятие тебе знакомо по предыдущей главе. В ней мы перегружали методы, а теперь мы займем¬ ся перегрузкой операторов. Операция очень похожа на объявление методов и отличается от него лишь словом operator. Оно необходимо, так как С++ не примет следу¬ ющее объявление: virtual void « (String Illustration); // Ошибка! Если ты решишь использовать собственные знаки, их обязательно надо будет объявить как операторы С++. (Хотя не все они могут быть перегружены!) Посмотрим на описание нового оператора: void TMonster::operator « (String Illustration) { String Name = Illustration.SubString(l, Illustration.Length()-4); Forml->Imagel->Picture->LoadFromFile (Path+Illustration); Forml->Panell->C&pcion = Name; } Блок команд остался без изменения, только в заголовке вместо имени методаАрреаг появились два знака «больше». Следует также заменить все вызовы этого метода описанным операто¬ ром. В итоге программа будет работать так же, как ее предшественница (MONSTER8.CPP): void fastcall TForml::TimerlTimer(TObject *Sender) { Chance = random(5); switch (Chance) { case 0: *Who « "Frank.bmp"; Перегрузка операторов
break; case 1: *Who « "Albert.bmp"; break; case 2: *Who « "Sigmund.bmp"; break; case 3: *Who « "Jekyll.bmp"; break; case 4: *Who « "Hyde.bmp''; } } ♦ Замени объявление и вызов метода Appear в файле модуля MONSTER7.CPP на перегруженный оператор («), и сохрани резуль¬ таты в новом проекте (FSTEIN8.MAK, MONSTER8.CPP). Проверь, как работает программа. Доступ и выбор Я упустил небольшую деталь: звездочку (*) перед именем who. Если ты пропустишь этот знак, то C++Builder выдаст сообщение об ошибке. Думаю, ты помнишь, что указатель Who был объявлен со звездочкой. Знак (*) мы уже использовали неоднократно и ты знаешь, что он называется оператором косвенной адресации. (Не забывай о том, что иногда этот знак используется для обозначения операции умножения.) 14. Всякая всячина
С помощью Who мы определили указатель на объект. Эту же операцию выполняет оператор для компонентов и даже для самой формы. Нет ни одного проекта, где бы ни встречался данный оператор: TForml *Forml; Долгое время мы работали с указателями. Мы писали стрелку, когда обращались к методу: Who - >Appear ('' Frank. bmp") ; Определенные трудности появляются, когда мы отступаем от этого пра¬ вила. Почему, собственно говоря, необходимо объявлять Who как указа¬ тель? Что произойдет, если я опущу звездочку при объявлении Who? TMonster Who; В этом случае Who превратится из указателя на объект в сам объект. По¬ этому при попытке запустить программу C++Builder выдаст сообщение об ошибке. Эта строка вдруг станет лишней: Who = new TMonster; Фактически, наш объект уже существует. Вероятно, он был активирован при объявлении стандартного конструктора. Таким образом, указатель Who объявлен полностью. Следующее сообщение об ошибке настигает нас, когда мы пытаемся добраться до метода Appear с помощью оператора (->): Who->Appear ("Frank.bmp"); Who->Appear ("Albert.bmp") ; Who->Appear (“Sigmund.bmp") ; Who->Appear (\Tekyll.bmp"); Who - >Appear (" Hyde. bmp'') ; Неужели оператор доступа Who недостаточно хорош? Что обозначает стрелка? Она указывает на метод или свойство, то есть перед ней должен стоять указатель: Указатель_на_объект->Метод (параметр); Указатель_на_о6ьект->Свойство - значение; Доступ и выбор шт
Поскольку Who объявлен не как указатель, а как объект, то оператор доступадолжен выглядеть иначе. Его называютоператором выбора, и он состоит из точки (.): Who.Appear ("Frank.bmp"); Who. Appear (" Albert.. bmp'') ; Who. Appear (" Sigmund. bmp'') ; Who.Appear {"Jekyll.bmp"); Who. Appear (" Hyde. bmp'') ; В этом случае точка соединяет объект непосредственно с методом или свойством. Перед ним не должен ставиться указатель: Объект.Метод (Параметр); Объект.Свойство = Значение; Ты наверняка спросишь, для чего нам вообще нужны указатели, если проще обойтись без них? Встречаются ситуации, когда указатели не нужны (или даже не имеют смысла). Многие переменные, которые мы объявляли в процессе рабо¬ ты в системе C++Builder, объявлялись без помощи указателей. Объекты тоже необязательно инициализировать, используя указатели. С другой стороны, в системе разработки существует правило, согласно которому все объекты ее семьи классов объявляются только с помо¬ щью указателей и дополнительно инициализируются оператором new и конструктором. Попытка объявить форму или компонент, таким обра¬ зом, не удастся: TFoml Fonrti; TButton Buttonl; Управление объектами с помощью указателей более гибкое, чем при объявлении без использования указателя. А поскольку C++Builder весь¬ ма великодушно принимает произведенные нами классы в семью ком¬ понентов, нет препятствий для продолжения этой традиции. Что касает¬ ся меня, то я объявляю в основном все объекты через указатели, например TMovie, TOButton или TMonster. Вернемся к проблеме, которая стала причиной использования объявле¬ ния без указателей. В двух последних версиях проекта Монстр вызов метода мы заменили перегруженным оператором: 14. Всякая всячина
Who->Appear ("Frank.bmp"); *Who « ''Frank.bmp"; Конечно, С++ позволяет применить указатель. Но в этом случае объект должен сам связаться с именем файла, что напоминает команду: Answer = "хорошо”; // Переменная Оператор Константа Amount = Number; // Переменная Оператор Переменная2 Конечно, в С++ имеется возможность присваивать и указатель, или ис¬ пользовать другие операции с ним. Но я не хотел бы углубляться в эту тему. В данном случае указатель был бы лишним, о чем нам и напомина¬ ет C++Builder. И на этот раз нам помогает небольшая звездочка , так как она опреде¬ ляет уже не указатель, а указываемый объект: Who // Объявленный указатель на объект "Монстр" *Who // Сам объект "Монстр" Таким образом, с помощью оператора косвенной адресации можно об¬ ратиться непосредственно к объекту. Значение или ссылка Подумай о том, можно ли кроме методов и операторов перегружать процедуры и функции. Такая простая функция, как random, не указывает ни на один класс, но ее может использовать любой объект. Не имеет значения то, что описан¬ ные процедуры не принадлежат ни одному классу: void ExChange (String &x, String &y) { String z = x; x = у; у = z; } void ExChange (double &x, double &y) { double z = X; x = у; у = z; } Значение или ссылка
Обе версии процедуры ExChange обмениваются двумя строками или двумя десятичными числами. Как и float, оператор double является типом деся¬ тичных чисел, только у него больше знаков после запятой, чем у float. Обращает на себя внимание новый знак (&), указанный в списке пара¬ метров. Его называют амперсантом или логическим И. Если опустить этот знак, то при обмене возникнут некоторые проблемы. Проследим, что же происходит с двумя строками, участвующими в про¬ цедуре обмена. Сначала рассмотрим случай с обычными параметрами (без знака (&)). Объявление процедуры выглядит в этом случае следую¬ щим образом: void ExChange (String x, String у) 1-я строка 2-я строка До вызова ExChange Привет Приветик Во время вызова ExChange Приветик Привет После вызова ExChango Привет Приветик При передаче параметров процедура ExChange получает значение обеих строк. Во время ее выполнения строки меняются местами. За предела¬ ми процедуры никаких изменений не происходит, потому что строки толь¬ ко принимают новые значения и ничего не возвращают. Мы имеем дело с процедурой, а не с функцией. Кроме того, функция может возвращать только одно значение. Мы рассматриваем вызов по назначению (call by value). Парамет¬ ры такого вида называются входными. При использовании знака (&) процедура имеет следующий вид: void ExChange (String &x, String &y) До вызова ExChange Привет Приветик Во время вызова ExChange Приветик Привет После вызова ExChange Приветик Привет Вызывается эта процедура как обычно: ExChange (Textl, Text2); // объявление как String ExChange (Numberl, Number2); // объявление как double
Процедура ExChange получает не значения двух строк (или двух чисел), а их адреса в оперативной памяти компьютера. Во время выполнения про¬ цедуры обмениваются найденные ею по указанным адресам значения. Неудивительно, что это изменение обнаруживается и за пределами про¬ цедуры. Эту операцию называют вызовом по ссылке (call by reference), а используемые в данной операции параметры — ссылочными. Что позволяет превратить значение в адрес? Программисты С++ назы¬ вают знак (&) оператором адресации, поскольку он указывает на пе¬ ременную. Существует три возможности его расположения (я предпо¬ читаю последнюю из приведенных ниже): String & x; // выглядит как "Фирма & Со" String& x; // таким образом строка приобретает адресный тип String &x; // x становится адресной переменной int *Pointer; int. Numberl ; int Number2 ; Pointer = &Numberl; Number2 = *Pointer; // указатель на число // числовая переменная // еще одна числовая переменная // указатель содержит адрес Numberl // Number2 содержит значение, // расположение которого определяет указатель Этот оператор напоминает оператор косвенной адресации (*). Указа¬ тель содержит адрес, где хранится переменная или объект. Ниже приве¬ дена небольшая игра, в которой операторы (*) и (&) «перебрасываются мячом»: Таким образом, Number2 косвенным образом получает значение Number1. Поскольку в этом действии принимает участие оператор (*), программисты С++ называют его оператором косвенной адресации. Прежде чем окончательно запутать тебя, приведу таблицу соответствия объектов: Название Указатель Объект Название &Объект *Указатель Назначение Адрес объекта Сам объект Для этого случая у меня есть небольшой проект TAUSCH1.MAK (см. приложение D). В нем ты можешь по желанию вводить два числа или две Значение или ссылка
строки, которые затем после нажатия одноименной кнопки меняются местами, а на форме отображается полученный результат. Приветик Привет Строка Число Поменять Нет необходимости заново вводить весь исходный текст, но поскольку снова используется незнакомая тебе функция C++Builder, я хочу пред¬ ложить краткие пояснения: Textl = InputBox ('' 1. String", "Подтверждение или повторение", "Привет") ; Text2 = InputBox ("2. 8^1пд",''Подтверждение или повторение", "Приветик"); Это команды для ввода двух строк. InputBox выводит небольшое диа¬ логовое окно. Ниже приводится следующее назначение параметров функции: InputBox (Название, Информационный текст, Шаблон для ввода строки); Причина подробного представления обеих процедур обмена не только в том, чтобы воспользоваться случаем и поговорить о типах параметров, но и попытаться применить оператор адресации. Если мы решим сделать процедуру ExChange универсальной, онадолжна перегружаться для любого типа данных. Это условие можно выполнить для небольшого приложения, но для разнообразных процедур и функ¬ ций необходимо дважды (и более) повторять коды программ. Зачем утруждать себя, если у С++ есть кое-что в запасе и на этот случай? Рассмотрим данную особенность языка подробнее. Шаблоны 14. Всякая всячина
Процедура обмена одинакова для всех элементов. Она принимает два параметра одного типа и меняет их значения. Таким образом, использо¬ вание шаблона было бы самым лучшим (и удобным) решением этой за¬ дачи. Он выглядит следующим образом: template <class AllTyp> void ExChange (AllTyp &x, AllTyp &y) { AllTyp z = x; x = у; у = z; } AllTyp принимает строки или десятичные числа. Кроме того, возможна передача целых чисел или даже строк известного нам типа char*. Важ¬ ную роль при этом играет первая строка: template <class AllTyp> В ней без подробногоописания объявляется классАПТур. Как видно из имени template (шаблон), он может иметь любой тип, и даже быть объектом. Шаблоны классов называются также родовыми классами или генера¬ торами классов. Обрати внимание на скобки, в которых указано имя общего типа, и на знакомое слово class. Если достаточно места, то в одной строке можно указать полное объявление, например: template <class идентификатор_типа> Тур Name (Тур Parameter) Проверим, что может этот шаблон, выбросив из TAUSCH1.MAK ста¬ рые процедуры и заменив их новым объявлением ExChange. В остальном исходный текст не меняется. (За исключением того, что появляется вто¬ рая процедурная группа, которая служит для отображения значения. Она также заменится на соответствующий шаблон.) Этот проект ты най¬ дешь в файле TAUSCH2.MAK. Вставим в исходный текст относящиеся к теме указатели и адреса, и еще раз представим метод ExChange: Шаблоны
template <class AllTyp> void ExChange (AllTyp *x, AllTyp *y) { AllTyp z = *X; *x = *y; *y = z; } В нем в качестве параметров заданы сразу два указателя. В пределах процедуры значения переменных, указывающих на x и у, обрабатыва¬ ются непосредственно. Для этого при вызове процедуры следует вместо значений сообщить адреса: ExChange (&Textl, &Text2); // объявлено как String ExChange (ШлпЬег1, &Number2); // объявлено как double Эту версию ты можешь рассмотреть в проекте TAUSCH3.MAK. Для того чтобы увидеть, что шаблоны могут принимать не только отдель¬ ные переменные, но и массивы параметров, я хотел бы привести еще один пример: template <class AllTyp> AllTyp BubbleSort (AllTyp *Element, int Мах) { for (int x=0; x<Max; x++) for (int y=x; y<Max; у++) if (Element[x] > Element[y]) ExChange (Element[x], Element[у]); return Element[0]; } Это — небольшая функция для сортировки строк и чисел. По окончании работы она возвращает исходные элементы. Интересно, что в таком шаблоне, очевидно, беспрепятственно может быть использован другой шаблон (в нашем случае функция ExChange). Все элементы должны быть переданы в пакете с максимальным значе¬ нием. Например, можно использовать следующие объявления: 14. Всякая всячина
const int Max - 30; String Chain[MaxJ ; int Number[Max]; Пример выборочной сортировки строк или чисел предлагает проект SORT1.MAK. В языке С++ даже классы можно объявлять как шаблоны. Такие клас¬ сы называют шаблонами или генераторами классов. Ниже приведен при¬ мер очень простой программы: template <class AllThing> class TThing { . private: All'Thing Data; public: virtual AllThing Тур (void) {return Data;}; TThing (AllThing D); virtual ~TThing (void) {}; }; Переменная Data передает только одно свойство, значение которого сообщается методом Тур. В то время как описание конструктора до¬ вольно просто, заголовок выглядит весьма странно: template <class AllThing> TThing<AilThing>::TThing(AllThing D) { Data = D; }; И на этот раз описание template использует то же название метода. Выводы Пришло время попрощаться с C++Builder, а также пожелать всего доб¬ рого доктору Франкенштейну (хотя, может быть, у тебя есть желание продолжить работу над проектом Монстры). Выводы
Ниже представлены новые возможности языка С++ и системы разра¬ ботки C++Builder, с которыми ты познакомился в этой главе: Tiroer Объект для определения интервалов времени (тип TTimer) TimerlTimer Метод объекта TTimer: вызывается через определенные промежутки времени Enabled Свойство объекта TTimer: включает и выключает таймер Interval Свойство объекта TTimer: устанавливает интервалы времени ttputBox Небольшое диалоговое окно с полем для ввода tenplate Оператор, объявляющий шаблоны (Templates) operator Оператор, перегружающий операторы €++ (определяет новое назначение) double Оператор, объявляющий переменные типа десятичных чисел с более высокой точностью « Оператор, используемый в том числе для вывода » Оператор, используемый в том числе и для ввода * Оператор косвенной адресации & Оператор адресации Оператор доступа к элементам (объектам, которые не описаны указателем) <> Скобки для объявления шаблонов Заключение Ты прочитал последнюю главу о языке С++ и системе разработки C++Builder. Конечно, в книге описано далеко не все, чем располагает эта мощная система программирования. Если ты решишь и далее изу¬ чать программирование на С++, тебе поможет справка C++Builder. К справке можно обратиться через меню HELP. Оно расположено справа на панели меню. • Первый вариант. Укажи в меню HELP команду KEYWORD SEARCH (Поиск по ключЕвому слову). Откроется диалоговое окно, в кото¬ ром задается понятие или слово C++Builder. 14. Всякая всячина
• Второй вариант. Укажи в меню HELP команду CoNTENTS (СодЕР- ждниЕ). Откроется диалоговое окно со списком тем, представ¬ ленных в справочной системе. На закладках 1ыоЕХ (УкдзАТЕЛь) и SEARCH (Поиск) выбери интересующий тебя раздел. • Третий вариант. Нажми клавишу F1. На экране появляется спра¬ вочная информация к слову С++, на которое указывает тексто¬ вый курсор. Заключение
Несколько вопросов... 1. Чем отличаются входные и ссылочные параметры? 2. Объясни назначение операторов Name, *Name и &Name. ... и несколько задач 1. В проекте Ioy монстровизглавы 13замениметодАрреагперегруженным оператором (++). (Указание: при вызове следует поэкспериментиро¬ вать со звездочкой и скобками.) 2. В последнем представленном в этой главе проекте Ioy монстров ис¬ пользуй вместо обычных кнопки OButton, описанные в главе 12, с метками НдчАло и Стоп. Для этого добавь OButton в палитру компонентов. 3. Разработай шаблон для функции between, в которой определяется, расположено ли значение в заданном интервале. Встрой ее в новую версию программы Оценка из главы 3. 14. Всякая всячина
Приложение А Для родителей... Писать приложения? Некоторые рады только тому, что умеют хотя бы запускать программы и работать с ними. Да и завидовать программис¬ там особенно не в чем, хотя они и зарабатывают немало. Но, вполне возможно, что на первых порах при чтении этой книги ваше¬ му ребенку потребуется помощь. Для первого знакомства с программированием рекомендую вам прочесть какую-нибудь книгу для начинающих пользователей. Она поможет вам разобраться с компьютером. Если ваше любопытство окажется сильнее страха перед поначалу трудно усваиваемым материалом, прочтите книги для начинающих программистов по Turbo Pascal и Delphi. Так или иначе, вы сможете помочь ребенку установить систему разра¬ ботки C++Builder. Подробнее процесс установки описан в Приложе¬ нии В. На компакт-диске с копией системы разработки C++Builder имеется программа SETUP (программа установки), осуществляющая перенос информации на жесткий диск компьютера. Попробуйте вдвоем с ребен¬ ком установить C++Builder. Дискеты для упражнений Вашему ребенку необязательно пользоваться дискетами для сохране¬ ния результатов своих экспериментов no программированию. При копи¬ ровании файлов на жесткий диск создается папка с именем C:\CPP\TEST. В ней найдется место для всех программ. Но для боль¬ шей надежности можно сохранять всю информацию еще и на дискете. Если Ваш ребенокзахочетсохранитьфайлы надискете или в папке TEST, помогите ему. В книге я исхожу из того, что А: — это дисковод для гиб¬ ких дисков, С: — жесткий диск, а D: — дисковод для компакт-дисков.
... и учителей Эту книгу можно использовать как пособие науроках информатики в шко¬ ле. Конечно, каждый учитель выбирает то, что считает нужным. Если вы уже пользуетесь какими-либо специально выпущенными для школ пособиями, то данную книгу можно применять как дополнительный материал. Если вы хотите научиться работать с системой разработки C++Builder, книга вполне подойдет для этих целей, поскольку объяснения в ней на¬ чинаются с «нуля». Замечу, что тем, кто умеет работать с Delphi, гораздо легче освоить C++Builder и язык С++. Важной темой, рассматриваемой в этой книге, являются особенности объектно-ориентированного программирования (ООП). В ней под¬ робно описаны три важнейших свойства ООП: инкапсуляция, наследо¬ вание, полиморфизм. В приведенных примерах использованы все важнейшие компоненты C++Builder. Кроме того, в книге описано, как создавать собственные компоненты и записывать их в библиотеку компонентов. В решениях к задачам представлены разные варианты программирования. Дискеты для упражнений Для уроков информатики у каждого ученика должно быть по одной или две дискеты, чтобы сохранять на них созданные программы. Таким об¬ разом, вы избежите скопления на диске школьного компьютера боль¬ шого количества лишней информации. Кроме того, собственная дис¬ кета служит для защиты данных. Только ее хозяин сможет работать с сохраненными на ней файлами. Регулярное сохранение Целесообразно в процессе работы каждые десять минут сохранять по¬ лученные результаты на дискете, поскольку компьютер имеет особен¬ ность «зависать» именно в тот момент, когда данные длительное время не сохранялись. Как правило, C++Builder предоставляет возможность создавать резер¬ вные копии. Это свойство задается в меню OPTioNS. Приложение А
Приложение В Установка системы разработки C++Builder Для того чтобы установить систему разработки C++Builder, тебе по¬ требуется приобрести установочный компакт-диск. Если при выполнении какого-либо действия у тебя возникнут вопросы, обратись к родителям или учителям. Вставьдисквдисковод. Программа поустановке называется SETUP.EXE или SETUP.BAT. Ее запуск осуществляется под управлением операци¬ онной системы Windows. Не забывай, что C++Builder работает только в системах, начиная с Windows 95. ■^ В меню Пуск укажи команду Выполнить. 4 Введи в текстовое поле диалогового окна D:\SETUP или D:\SETUP\ CBUILDER\SETUP, а затем нажми кнопку ОК. • Если эти действия выполнить не удается, найди файл SETUP.EXE с помощью кнопки Овзор. • Если твой дисковод для компакт-дисков обозначается другой буквой, например E: или F:, вставь эту букву перед словом SETUP вместо D:. Программа Setup Подожди немного, пока программа SETUP выполнит некоторые дей¬ ствия. На экране появляется следующий рисунок:
T* Нажми кнопку NEXT (ДАЛЕЕ). Появляется окно с лицензионным договором. Если ты не подтвердишь свое согласие, процесс установки прервется. т^ Поэтому нажми кнопку YES (Дд). Предлагается описание способа установки, которое читать необяза¬ тельно. ■^ Нажми кнопку NEXT. На экране вновь появлется диалоговое окно. Предлагается сделать выбор из следующих вариантов установки: • в варианте Full (Полный) все предлагаемые системой C++Builder файлы копируются на жесткий диск компьютера; Приложение В
• при выборе Compact (Оптимальный) копируются только те фай¬ лы, без которых нельзя обойтись при изучении C++Builder с помощью этой книги; • вариант Custom (Пользовательский) позволяет самостоятель¬ но выбрать, в каком объеме будет установлена система C++Builder. В этом случае устанавливается только то, что ука¬ зывает пользователь, то есть ты. Для установки минимальной версии требуется около 64 МБ, для макси¬ мальной — свыше 100 МБ памяти на жестком диске компьютера. 4 Выбери вариант установки Сомрдст. Для нашей работы достаточно этого варианта. Но если система разра¬ ботки C++Builder помещается на твоем компьютере полностью, уста¬ нови ее полную версию. (При необходимлости обратись за помощью к родителям или учителям.) ^ Нажми кнопку №хт. В следующем диалоговом окне указывается папка, в которую копирует¬ ся C++Builder. ■^ Введи В текстовое поле C++BuiLDER DlRECTORY (ПАПКА C++BuitDER) текст C:\CPP и нажми кнопку NEXT. (Можно задать и другую папку, но в рас¬ сматриваемых в книге проектах указывается этот путь.) В следующем диалоговом окне требуется выбрать папку для меню за¬ пуска C++Builder. Программа Setup
^ Введи, например, C++Builder (или оставь предложенное в окне имя). Нажми кнопку NEXT. Наконец-то, мы можем приступить к непосредственной установке! ■^ Нажми кнопку lNSTALL (Устдновить). Теперь наберись терпения и подожди некоторое время. Наблюдай за ходом процесса установки. Приложение В
Приложение С Способы поиска ошибок во многих системах разработки весьмаразно- образны. Поскольку самые коварные ошибки имеют обыкновение тща¬ тельно маскироваться, появляется сомнение, можно ли их найти. Для облегчения нелегкой жизни программиста в C++Builder предусмотрен целый ряд эффективных инструментов для поиска ошибок Краткий список подсказок Ниже приведены подсказки по поиску ошибок: • Не забыта ли какая-нибудь мелочь при составлении програм¬ мы? Не пропущена ли запятая, точка или точка с запятой? • Проверь, все ли блоки программных модулей заключены в фи¬ гурные скобки (например, после операторов if, while, do, а так¬ же в функциях и методах)? • Заключены ли в скобки условия, поставлены ли скобки (даже если они пусты) в функциях и методах? • Стоит ли после каждой команды точка с запятой? • Могут ли быть выполнены условия, указанные в операторах пе¬ редачи управления if или while? • Все ли переменные, объекты, функции, методы объявлены (пра¬ вильно)? • Присвоены ли переменным и параметрам, которые обрабаты¬ ваются в программе, (осмысленные) значения? • Соответствуют ли в операторе присваивания (=) по типу левая и правая части? Совпадают ли при передаче тип и количество па¬ раметров?
• Не перепутаны ли в условии оператор присваивания (=) и опе¬ ратор равенства (==)? • Соответствуют ли операторы, например звездочка (*), ампер¬ сант (&), стрелка (->) или точка (.), объявленному объекту? По следам ошибки В C++Builder существует средство, которое поможет тебе при поиске ошибок. Средство поиска ошибок называют отладчиком (по-английски Debugger). Отладчик помогает C++Builder обнаружить ошибки. Слово bug, которым программисты называют ту или иную ошибку, по-английски означает «жук». Отладчик позволяет заставить программу работать по отдельным командам. Более подробную информацию об отладчике см. в справке C++Builder. Пошаговый запуск программы Для того чтобы проследить за действиями приложения, следует посмот¬ реть, что происходит в результате выполнения отдельных команд. На¬ жимая определенную клавишу, ты наблюдаешь, как шаг за шагом оно выполняется. • Запусти программу не как обычно, а нажав «горячую» клавишу F8. После этого каждая команда начинает выполняться отдельно, и ты можешь наблюдать, что происходит на каждом шаге выполнения про¬ граммы. (Нажатие клавиши F8 соответствует выбору команды STEP OVER (ПО ШАГАМ БЕЗ ЗАХОДА В ФУНКЦИИ) МвНЮ RUN.) • Поскольку вызванный клавишей F8 режим отладки рассматривает как команды объявленные пользователем функции (методы), они вы¬ полняются на одном шаге. Клавиша F7 позволяет выполнять по ша¬ гам и отдельные команды функции. (Нажатие клавиши F7 соответ¬ ствует выбору команды TRACE lNTO (ТРАССИРОВКА С ЗАХОДОМ В ФУНКЦИИ) меню RUN.) Приложение С
• Чтобы выйти из режима пошагового выполнения программы и вер¬ нуться в ее исходную точку, следует нажать клавиши Ctrl+F2. (Тот же результат достигается при указании команды PROGRAM RESET (ПЕ- РЕЗАГРУЗИТЬ ПРОГРАММУ) меню RUN.) Значения за значениями переменных Иногда важно знать, принимают ли переменные соответствующие зна¬ чения, или может ли быть выполнено некоторое условие. Значения пе¬ ременных и условий отображаются в дополнительном окне. • Установи текстовый курсор на переменную, значение которой необ¬ ходимо проверить. • В МеНЮ RUN укаЖИ КОМаНДу ADD WATCH (УСТАНОВИТЬ НАБЛЮДЕНИЕ). • В текстовом поле диалогового окна отображается имя выбранной переменной. Если в нем ничего не указано, необходимо ввести со¬ ответствующее имя и подтвердить выбор, нажав клавишу ОК. В небольшом дополнительном окне ты можешь наблюдать за тем, ка¬ кие значения принимает выбранные переменная или условие. Прерывание работы приложения Иногда возникает необходимость остановить выполнение программы в определенном месте, чтобы посмотреть, что происходит до момента ее останова. Для этого используются так называемые точки прерывания. • Установи курсор на строку, на которой должно остановиться выпол¬ нение программы. • Нажми клавишу F5. Соответствующая строка выделяется красным цветом. После запуска программа выполняется до отмеченной точки и останавливается. • Указав в меню RuN команду RuN или нажав клавишу F9, запусти про¬ грамму до следующей точки прерывания (если она отмечена) или до конца. • Для отмены точки прерывания установи на ней текстовый курсор и нажми клавишу F5 (как и при включении). По следам ошибки
Приложение D Проект FSTEIN1A.MAK: class TMonster { private: String Name; String Gist; public: void Appear(void); StringTyp (void) {return "Monster";}; TMonster (StringN, StringW); }; class TGMonster : public TMonster { public: void Appear(void); StringTyp (void) {return "Умныймонстр";}; TGMonster (StringN, StringW); }; class TSMonster : public TMonster { public: void Appear(void); String Typ (void) {return "Добрый монстр";}; TSMonster (String N, String W); }; // TMonster *Frank; TGMonster *Albert; TSMonster *Sigmund; TForml *Forml; // fastcall TForml: :TForml(TComponent* Owner) : TForm(Owner) Приложение D
// TMonster::TMonster (String N, String W) { Name = N; Gist = W; } // TGMonster::TGMonster (StringN, StringW) : TMonster (N, W) // TSMonster::TSMonster (String N, String W) : TMonster (N, W) // voidTMonster::Appear (void) { Forml->Labell->Caption = "Имя:" + Name; Forml->Label2->Caption = "Суть: " + Gist; Forml->Label3->Caption = "Тип:" + Typ(); } // void TGMonster::Appear (void) { TMonster::Appear (); Forml->Label3->Caption = "Тип:" + Typ(); } // void TSMonster::Appear (void) { TMonster::Appear (); Forml->Label3->Caption = "Тип:" + Typ(); } // void fastcall TForml::FormCreate(TObject *Sender) { Frank =newTMonster ("Франки", "необычный”); Albert = newTGMonster ("Берти", "решительный"); Проект FSTEIN1A.MAK:
Sigmund = new TSMonster (“Зиги", "сочувствующий"); 1 ; // void fastcall TForml::ButtonlClick(TObject *Sender) { Frank->Appear (); } // void fastcall TForml::Button2Click(T0bject *Sender) { Albert->Appear (); } // void fastcall TForml: :Button3Click(T0bject *Sender) { Sigmund->Appear (); Проект FSTEIN2A.MAK: // #include <vcl\vcl.h> #pragma hdrstop #include "Monstr2a.h" // #pragma resource ''*.dfm" class TMonster { private: String Name; String Gist; public: void Appear(void); virtual String Typ (void) {return "Монстр";}; TMonster (String N, String W) ; }; class TGMonster : public TMonster { Приложение D
public: virtual StringTyp (void) {return "Умныймонстр";}; virtual void Typ (String Character) {Forml->Label3->Caption = "У меня" + Character;}; TGMonster (String N, String W); }; class TSMonster : public TMonster { public: virtual String Typ (void) {return "Добрый монстр";}; virtual void Typ (String State) {Forml->Label3->Caption = "Я живу" + State;}; TSMonster (String N, String W); }; // TMonster *Frank; TGMonster *Albert; TSMonster *Sigmund; TForml *Forml; // fastcall TForml: :TForml(TComponent* Owner) : TForm(Owner) // TMonster::TMonster (String N, String W) { Name = N; Gist = W; ) // TGMonster::TGMonster (String N, String W) : TMonster (N, W) // TSMonster::TSMonster (StringN, StringW) : TMonster (N, W) /■ Проект FSTEiN2A.MAK:
void TMonster::Appear (void) { Forml->Labell->Caption = "Имя:" + Name; Forml->Label2->Caption = "Суть: " + Gist; Forml->Label3->Caption = "Тип:" + Typ(); } П void fastcall TForml::FormCreate(TObject *Sender) { Frank = newTMonster ("Франки", “необычный"); Albert = new TGMonster ("Берти", "решительный"); Sigmund = new TSMonster ("Зиги", "сочувствующий”); } // void fastcall TForml::ButtonlClick(TObject *Sender) { Frank->Appear (); } // void fastcall TForml::Button2Click(T0bject *Sender) { Albert->Appear (); Albert->Typ ("Ум")-; } // void fastcall TForml::Button3Click(T0bject *Sender) { Sigmund->Appear (); Sigmund->Typ ("хорошо"); } // Проект TAUSCH1 .MAK: void ExChange (String &x, String &y) { String z = x; x = у; у = z; } Приложение D
void ExChange (double &x, double &y) { double z = x; x = y; y = z; } void ShowIt (String x, String y) { Forml->Panell->Caption = x; Forml->Panel2->Caption = y; } void ShowIt (double x, double y) { Forml->Panell->Caption = String (x); Forml->Panel2->Caption = String- (y); } I/ String Textl, Text2; double Numberl, Number2; bool Modus; TFoml *Forml; fastcall TForml; :TForml (TComponent* Owner) : TForm(Owner) { } // void fastcall TForml::ButtonlClick(TObject *Sender) { Textl = InputBox ("Первая строка",''Лодтвердить или повторить действие'1, "Привет") ; Text2 = InputBox ("Вторая строка", "Подтвердить или повторить действие", "Приветик") ; ShowIt (Text1, Text2); Modus = true; } // void fastcall TForml::Button2Click(T0bject *Sender) II- Проект TAUSCH1.MAK: 13-4109
{ Numberl = StrToFloat (InputBox ("Первое число","Подтвердить или повторить действие","0")); Number2 = StrToFloat (InputBox ("Второе число", "Подтвердить или повторить действие", "1")) ; ShowIt (Numberl, Number2); Modus = false; } // void fastcall TForml::Button3Ciick(T0bject *Sender) { if (Modus) { ExChange (Textl, Text2); ShowIt (Textl, Text2); } else { ExChange (Numberl, Number2); ShowIt (Numberl, Number2)-; } Приложение D
Глоссарий А Абстрактно-виртуальная функция Чистая виртуальная функция Абстрактный класс Класс, который содержит хотя бы одну виртуальную функцию. На осно¬ ве таких классов нельзя создавать экземпляры. Абстрактный класс, как правило, является базовым. Базовый класс Класс, на основе которого можно создать новый класс. Базовый класс является родительским для производного класса. Виртуальный класс Если класс создается на основе нескольких классов, у которых один базовый родительский класс, необходимо ввести однозначное соответ¬ ствие. В таких случаях необходим виртуальный базовый класс, который обозначается словом virtual: class ИмяНаследника : virtual Доступ ИмяПредка { ... } Виртуальный метод Если адрес метода связывается с программой только при вызове этого метода после ее запуска, он является виртуальным. Благодаря этому для различных объектов одного семейства можно использовать одни и Б В 13*
те же имя метода и синтаксис. Если вызываемые методы объявлены вир¬ туальными, в процессе работы программы текущему объекту присваива¬ ется и вызывается соответствующий метод. Объявление осуществляет¬ ся дополнительным ключевым словом virtual: virtual ИмяФункции (СписокПараметров) ; Деструктор Метод, выполняющий работы при удалении объекта. Если деструктор не объявляется специальной командой, С++ создает деструктор стан¬ дартной формы: ИмяКласса::-ИмяКласса (void); Динамическое связывание(позднее связывание) При вызове в программе виртуально объявленного метода его адрес привязывается к ней в процессе выполнения (см. также раннее связы¬ вание) Динамический объект Объявленный с помощью указателя объект, для которого зарезервиро¬ вано место оператором new. Дружественный класс Этот механизм позволяет чужим классам и функциям иметь доступ к необъявленным в разделе public элементам. Объявление таких друже¬ ственных классов осуществляется с помощью оператора friend: Г Генератор функций Шаблон функции (см. Шаблон) Генератор класса Шаблон класса (см. Шаблон) д Глоссарий
friend ИмяКласса friend ИмяФункции И Иерархия классов Взаимосвязь классов одного семейства, связанных механизмом насле¬ дования. Иерархия имеет форму дерева: обычно корень образует один- единственный класс, объявляемый как class Имя { ... } Все остальные узлы объявляются как class ИмяНаследника : СписокПредков { ... } Любой узел этого дерева может иметь несколько предков и наследни¬ ков. В С++ и в Delphi корневым классом является TObject. Инкапсуляция Объединение свойств и соответствующих им методов (функций) в одном модуле. Созданная в результате объединения структура является классом. К Класс Тип, структура которого имеет не только свойства, но и методы. Конструктор Этот метод служит для создания (так называемого рождения) объекта. Если конструктор не объявлен программистом, то С++ создает стан¬ дартный конструктор следующего вида: ИмяКласса::ИмяКласса (void); При объявлении объекта автоматически вызывается конструктор: ИмяКласса ИмяЭкземпляра (СписокПараметров); Глоссарий
м Метод Если функции объявлены внутри класса, они называются методами или функциями-элементами. Они, как правило, описываются за пределами класса (особенно если в операторном блоке много команд), однако в этом случае необходимо указать имя класса: Тур ClassName: :MethodName (ParameterList) { ... } Методы могут объявляться статичными и виртуальными. Они вызывают¬ ся не как обычные функции, а в результате наступления некоторого со¬ бытия или обращения к объекту. Множественное наследование Класс может быть произведен не только от одного, а от нескольких ба¬ зовых классов. В этом случае он наследует свойства и методы всех ро¬ дительских классов. Для того чтобы не возникало недоразумений, часто возникает необходимость виртуально объявить родительские классы. н Наследник Наследник объекта или класса. Наследует свойства родительского эле¬ мента или элементов: class ИмяНаследника : Список Предков { ... } У наследника может быть несколько предков. Наследование Свойство класса в качестве преемника наследовать все элементы базо¬ вого класса. Для этого наследник в объявлении должен быть связан с родительским классом: class ИмяНаследника : Доступ ИмяПредка { ... } Глоссарий
В свою очередь, наследник может произвести преемников и передать в качестве наследства свои свойства. Благодаря наследованию появляется возможность, не изучая объявле¬ ний методов-источников, создавать новый класс на основе уже суще¬ ствующего. о Объект Экземпляр класса. Объект располагаетсвойствами (элементами данных) и соответствующими методами обработки событий (функциями-элемен¬ тами). Объект связывает данные и функции (см. Инкапсуляция). ООП Объектно-ориентированное программирование, основными свойствами которого являются инкапсуляция, наследование и полиморфизм. п Перегрузка Функции, методы и операторы могут быть объявлены несколько раз с одним и тем же именем или идентификатором, если возможно одно¬ значное различение по списку параметров или возвращаемому типу. Полиморфизм Если один и тотже метод выполняет разные роли, соответствующие клас¬ су семейства, речь идет о полиморфизме (разносторонности). Эта воз¬ можность реализуется с помощью виртуальных методов (ключевое слово virtual). Классы тоже могут быть виртуальными. Полиморфный класс . Класс, имеющий хотя бы один виртуальный метод. Глоссарий
Полиморфный объект Объект, которому приписан эеземпляр одного наследника, или объект, объявленный как параметр и принимающий экземпляр наследника. Поскольку точный тип и размер полиморфного объекта к моменту ком¬ пиляции еще неизвестен, необходимо к моменту запуска программы передать данные о точном размере текущего экземпляра для выполне¬ ния корректного удаления. За это отвечает (виртуальный) деструктор. Производный класс Наследник родительского класса. Предок Базовый класс производного класса (см. Родительский класс). P Раннеесвязывание Если статически объявленный метод вызывается в программе, его ад¬ рес к этому моменту уже известен (см. Динамическое связывание). Родительский класс Предок объекта или класса. Производные классы наследуют все свой¬ ства и методы родительского класса. Он может иметь любое количе¬ ство детей (см. Наследник). С Свойство Элемент класса. В качестве свойств используются все предопределен¬ ные в С++ и определенные пользователем типы данных или структур. К частным свойствам объекта обращаются не напрямую, а только исполь¬ зуя имена опубликованных методов, то есть объявленных в разделе public. Глоссарий
Связывание Передача адреса метода при его вызове. Передача может осуществ¬ ляться сразу при компилировании (раннее связывание) или в процессе выполнения программы (динамическое связывание). Семейство Дерево классов, в которое входят родительские и производные классы. Каждый класс (прямой предок) может иметь любое количество наслед¬ ников. Производный класс может иметь много (!) родительских классов и таким образом наследовать все свойства и методы их предков. Спо¬ соб доступа к элементам зависит от способа наследования (public, protected, private). Статичное связывание Раннее связывание Статичный метод Если адрес для вызова метода определяется уже при компиляции, то есть до запуска программы, такой метод называется статичным. Его объявление осуществляется без использования специального ключевого слова. Иначе обстоит дело с виртуальными методами. Статичный объект Объект, объявляемый не через указатель, а как обычная переменная. т Типобъекта Класс Ф Функция-элемент Метод Глоссарий
ч Чистый виртуальный метод Виртуальной функции присваивается нулевое значение: virtual ИмяФункции (Список Параметров) = 0; В таком виде функция невыполнима. Класс, который ее получает, назы¬ вается абстрактным. На его основе невозможно создать какой-либо экземпляр. ш Шаблон Класс или функция с еще не определенным типом (элементом или пара¬ метром) служит шаблоном для конкретных экземпляров или вызовов. Объявление осуществляется ключевым словом template: template <class ИмяТипа> Тур ИмяФункции (ИмяТипа'Параметр...) { ... } template <class ИмяТипа> class ИмяКласса {ИмяТипа свойство/метод ... } э Элемент данных Свойство Экземпляр Если объявляется (и инициализируется в конструкторе) переменная, ее называют также экземпляром класса, имея в виду объект. Глоссарий
с Class Ключевое слово для объявления классов: class ИмяКласса { ... } class ИмяНаследника : Доступ ИмяПредка { ... } Все элементы объявленного оператором class класса считаются объяв¬ ленными в разделе private, если нет других объявлений. Delete Оператор, освобождающий занимавшееся ранее (динамическим) объек¬ том место в памяти. delete ObjektName; При выполнении этого действия дополнительно вызывается деструктор объекта, который выполняет корректноеудаление полиморфных объек¬ тов из памяти. E Early binding Раннее связывание Encapsulation Инкапсуляция Обычно метод описывается внутри, а объявляется за пределами своего класса. Однако существует способ полного объявления функции внут¬ ри класса. D I Inline Глоссарий
Inheritance Наследование Late Binding Динамическое связывание N new Этот оператор резервирует место в памяти для динамического объекта. ИмяОбъекта = new ИмяКласса (Список Параметров) ; Дополнительно вызывается конструктор объекта, который обеспечива¬ ет соответствующую инициализацию объекта. operator Так же, как функции и методы, это ключевое слово заново описывает операторы и таким образом перегружает старые описания. Объявле¬ ние нового оператора осуществляется следующим образом: Тип operator Идентификатор (Параметр) { ... } Overloading Перегрузка private С помощью этого ключевого слова объявленные элементы класса за¬ щищены от доступа снаружи. Доступ возможен только внутри базового класса, но не в производных классах. Глоссарий
'private private private public protected private невозможен'! невозможен! невозможен!' protected Это ключевое слово защищает элементы класса от доступа извне. Он возможен в базовом классе и во всех производных классах. Возмож¬ ность доступа зависит от способа объявления производного класса. К объявленному этим ключевым словом классу возможен доступ извне. Способ доступа зависит от объявления производного класса. Ключевое слово, используемое для объявления структур классов: struct Имя { ... } struct ИмяНаследника : Доступ ИмяПредка { ... } Если не указано ничего иного, все элементы объявленного ключевым словом struct классы считаются по умолчаниюриЬНс. protected protected protected public protected private protected protected private public public public public• public protected private public protected private s struct Глоссарий
т template Если при объявлении неизвестны типы параметров, функций, классов или других элементов, объявляется шаблон. Это осуществляется при помощи ключевого слова template: template <class ИмяТипа> this Невидимый указатель на экземпляр класса. Этот указатель автомати¬ чески создается при инициализации. и union Ключевое слово для описания структур и классов: union Имя { ... } union ИмяНаследника : Доступ ИмяПредка { ... } Если не указано ничего иного, то все элементы объявленного с помо¬ щью ключевого слова union класса считаются по умолчанию объявлен¬ ными в разделе public. V virtual С помощью этого ключевого слова объявляются виртуальные методы и классы: virtual ИмяФункции (Список Параметров); Глоссарий
Алфавитный указатель А Абстрактый класс 348 Амперсант 362 Б Байт 292 Бесконечный цикл 137 Бит 292 Буфер 77 Быстрая кнопка 279 В Ветвь 92 Виртуальное наследование 345 Виртуальный деструктор 341 конструктор 341 Всплывающееменю 21 Вызов по назначению 362 ссылке 363 Выравнивание 23, 110, 145 Г Генераторкласса 365 Главнаяформа 278 Граничнаяформа 278 Г рафика линии 190 прямоугольник 190 цвет 191 эллипс 190 Графическийадаптер 187 Группа переключателей 124 Групповоеокно 128 д Данные 218 Данные-элементы 204, 205 Двоеточие 116, 226, 234 Двоичная система 293 Двойное двоеточие 26 Деструктор 338 виртуальный 341 Директива #include 243 Документ 278 Дочерняяформа 279 3 Заголовок 224 Заголовочный файл 224 3anycKC++Builder 18 Звездочка 139 И Идентификатор 58 Индекс 133, 191 Инициализация 207
Инкапсуляция 218, 329 Инспектор объектов 23 Исключение 66 Исходный код 218 К Канва копирование 201 Перо 192 цвет 192 Кисть 197 Класс 24, 204, 218 абстрактный 348 виртуальный 344 объявление 204 поле 205 полиморфный 326, 331 прототип 205 экземпляр 204 элемент 205 Кнопка 21 быстрая 279 Код 218 Комментарии 62 Компилятор 12 Компиляция 322 Компонент 21 значок 265 размеры 43 расположение 43 регистрация 241, 264 создание 238, 250 установка 269 Константа 58, 91 Конструктор 207, 338 виртуальный 341 меню 166 Контекстно-ориентированное меню 23 Косая черта 246 Л Логическое И 362 Локальнаяпеременная 137 M Массив 199 многомерный 199 одномерный 199 Меню HELP 32 конструктора 166 контекстное 109 Метка 49 Метод 26, 48, 204 виртуальный 324 внешнееописание 319 внутреннееописание 319 полиморфный 326 статический 324 точкасзапятой 214 чистый виртуальный 348 Многострочное окно редактирования 17 7 Множественное наследование 343 Модуль 222 H Наследование 226, 329 виртуальное 345 множественное 343 О Объект 24 свойства 23 Алфавитный указатель
событие 48, 161, 183 тип 24 Объявление 214 глобальное 137 класса 204 локальное 137 Окно RTF 177, 282 групповое 145 для ввода чисел 7 5 рамочное 145 редакторакода 25 сообщений 28 списка 112 формы 21 ООП 217 Оператор —;# 90 ! 182 != 84 & 362 && 84 * 358, 359 * (указатель) 140 ++ 90 += 90 -= 90 -> 30 . 360 : 207 < 84 « 292, 356 = 84 > 84 » 292, 356 II 84 delete 162 new 155 адресации 363 выбора 360 доступа 30, 59, 211, 358, 359 И 84 ИЛИ 84 косвенной адресации 140, 155, 209, 363 множественноговыбора 115 перегрузки 356 присваивания 59 разрешения области действия 26, 207 реверсивный 182 соединительный 84 сравнения 78, 84 Операторный блок 78, 102, 136 Операция присваивания 59 Описание 214 Основные арифметические действия 65 Оси 187, 188 Отладчик 378 Ошибка поиск 81 Палитракомпонентов 21, 49, 111 Панель 148 состояния 279 Параметр 31 входной 362 ссылочный 363 Перегрузка метод 327 оператора 356 Переменная 58 глобальная 137 локальная 137 массив 133 Пиксел 187 Полиморфизм 325, 329, 337 Полиморфный класс 326, 331 метод 326 Алфавитный указатель
Полосапрокрутки 148, 153 Преобразование строки в тип char 141 строки в число 69 числа в строку 69 Приложение 30 MDI 278 SDI 278 Присваивание 59, 89 Программа завершение 29 разработка 29 создание 28 Программирование 11 Проект загрузка 40 новый 74 открытие 40 переименоваие 279 сохранение 33 сохранение как... 250 Прототип 214 Процедура 206, 213 Процесс разработки 28 Разветвление 92 Размершрифта 195 Размерность 198 Разрешение 187 Рамочноеокно 145 Реверсивный оператор 182 Редактируемыйсписок 117 Редактор 12 спискастрок 113 фильтров 171, 281 С++ 12 C++Builder завершение работы 29 меню 20 открытие окон 29 установка 373 Свойство 23, 204 Системаразработки 12 Скобки 83 <> 365 круглые 57, 78 прямоугольные 133, 199 фигурные 81 Событие 47, 48, 314 в инспекторе объектов 314 открытое 314 Сообщение об ошибке 27, 60 Списокстрок 155 Способ доступа 263, 272 Справка 32, 368 Стандартный деструктор 338 Статический метод 324 Строка 59 пустая 31 состояния 279 Структура for 136 if 79 while 102 вложенная 95 данных 199 передачиуправления 79, 115 управляющая 102 Алфавитный указатель
Текст исходный 35 Тильда ~ 339 Тип данных 60 шрифта 50 Точка прерывания 32 сзапятой 57 Шаблон 239, 276, 365 класс 367 Шрифт 127 Экземпляр 24, 204 Эллипс 306 Управляющий индикатор сфлажком 126 Условие 78, 102, 136 Установка 373 Язык программирования 12 Ячейка памяти 292 Файл заголовочный 224 закрытие 36 ресурсов 268, 269 сохранение 33 Фигурные скобки 26 Фильтр 280 Фокус 86, 152 Форма 21 дочерняя 278, 279 Формат Rich Text Format 177 Функции-элементы 205 Функция 57, 206, 213 Цикл 103 бесконечный 137 ActiveControl 153 Add 159 Align 283 Application 30 Arc 306 Bevel 150, 252 BMP 246 bool 133 break 117 Brush 197 Button 21 ButtonClick 26, 151 Алфавитный указатель
C++Builder 13, 21 c_str 141 Canvas 190, 239 Brush 197 Font 195 Pixels 198 TextOut 195 Caption 24 case 115 catch 81, 159 Center 287 char 131, 138 char* 139 char[] 139 CheckBox 126 CheckBoxClick 135 Checked 134 class 204 ClientHeight 109, 190 ClientWidth 109, 190 Close 96 ColCount 295 Color 192 ComboBox 117 ComboBoxChange 118 const 91, 156 CopyRect 201 Count 157 default 237 delete 162, 337 Delphi 222 do-while 103 double 362 Ellipse 190 else 92, 237 Enabled Timer 353 Esception 66 Events 48 Execute 173 false 133 fastcall 206, 229, 272 File 74 FileName 173 Filter 171 FilterEditor 171 float 68 FloatToStr 69 FloatToStrF 69, 233 Font 50, 127 for 135 FormClose 161 FormCreate 88, 150 GroupBox 128, 145 if 76 include 243 Алфавитный указатель
InputBox 364 int 61 integer 61 lntervalTimer 353 IntToStr 69 ltemlndex 114 Items 112, 120 Label 49 LabelClick 56, 63 Left 43, 108 Length 257 LineTo 190 ListBox 112 ListBoxClick 113 LoadFromFile 156, 245 LowerCase 289 MainMenu 166, 168 MDI 278 MDIChild 282 Menultem 167 MessageBox 30 MouseDown 312 MouseUp 312 MoveTo 190 new 155, 209, 337 not 182 On 315 OnClick 47 OnCreate 88 OpenDialog 170 operator 356 Options 292 Owner 245 Paint 306 Panel 148 Parent 245 Pascal 222 Pen 192 Picture 239 Print 179 PrintDialog 176 private 262 property 315 protected 272 public 205, 226, 263 published 272, 315 RadioButton 125 RadioGroup 124 RadioGroupClick 129 random 57 randomize 57 Rect 202, 254 Rectangle 190 Refresh 203 RegisterComponents 265 return 213, 233 RichEdit 282 RowCount 295 RTF 177 Алфавитный указатель
SaveDialog 170 SaveToFile 162 ScrollBar 149 ScrollBarChange 153 ScrollBars 283 SetColor 307 SetFocus 86, 152 Setup 18 Setup.exe 373 ShowMessage 342 strcpy 139 Stretch 245 String 131, 139 StringGrid 290 Cells 295 StringList 294 StrToFloat 69 StrToInt 69, 76 SubString 256 switch 115, 237 TButton 24 TCanvas 190, 214 TCheckBox 142 TColor 192 TComboBox 121 TComponentClass 265 TControl 304 TCustomControl 304 template 365 TextOut 195 TForm 25 TGraphicControl 304 TGroupBox 142 this 244 Tlmage 286 Timer 353 Timer1Timer 354 TLabel 49 TListBox 121 TMainMenu 168 TMovie 239 TObject 229, 302 Top 43, 108 TPanel 163 TPicture 247 TRadioButton 142 TRadioGroup 142 TRect 202, 252 TRichEdit 177 true 133 try 81, 159 TScrollBar 163 TStringGrid 290 TStringList 155 TTimer 353 U Unit 222 UpperCase 289 V virtual 324 void 205 W while 101 Width 43, 108
Что может быть увлекательней путешествия по Интернету? Если ты однажды соберешься в такое странствие, эта книга поможет тебе не потеряться в лабиринте информации! Автор книги не только объяснит тебе, как функционирует глобальная сеть и что необ¬ ходимо для уверенной ориентации в ней, но и расскажет, что интересного предлагает Интернетдля учебы в школе и проведения досуга. Наряду со множеством примеров, а также советами по организации поиска на страницах издания приведены ссылки для навигации по Сети. Книга раскроет и другие возможности Интернета. На чат-страницах и с помощью элек¬ тронной почты ты сможешь обмениваться мнениями с новыми друзьями. Ты будешь всегда в курсе последних событий, научившись работать с группами новостей. И если решишь однажды создать собственную домашнюю вебстраницу, то получишь для этого все самые важные рекомендации. По вопросам заказа и покупки издания обращайтесь по тел./факсу: (095) 179 4971 E-mail: iexpert@user.iiat.ru URL: http://www.interexpert.ru
Эта книга, несомненно, поможет тебе научиться программировать. Шаг за шагом ты освоишь азы языка Visual Basic. Начав с самого простого, ты постепенно станешь про- фессионаломвобласти программногообеспечения. Благодаря многочисленным при¬ мерам, упражнениям и доступным пояснениям на страницах этой книги ты легко овла¬ деешь ее материалом. Она поможет тебе добиться первых успехов на пути постижения тайн программирования на fl3biKeVisual Basic. По вопросам заказа и покупки издания обращайтесь по тел./факсу: (095) 179 4971 E-rrtail: iexpert@user.iiat.ru URL: http://www.interexpert.ru
Компьютер — родной и привычный атрибут рабочего места или квартиры. Но многие начинают его осваивать впервые. Эта книжка — для них! Другие освоили компьютер давно и успешно на нем работают. Однако все ли его воз¬ можности они раскрыли? В каждом ПК сокрыто немало тайн, которые готовы поко¬ риться умелому пользователю. Эта книжка и для них! Но прежде всего она — для детей. Именно им жить в мире, существование которого трудно представить себе без вычислительных машин. Ведь даже уборка улиц сегодня бурно компьютеризируется. Освоить компьютер от «а» до «я» с помощью множества интересных примеров, поучи¬ тельных советов и полезных упражнений несомненно поможет книга «Компьютер для детей»! По вопросам заказа и покупки издания обращайтесь по тел./факсу: (095) 179 4971 E-mail: iexpert@user.iiat.ru URL: http://www.interexpert.ru
Большинство предпринимателей и не подозревает о действительных возможностях рекламы... Как показывают исследования, 85 % рекламных объявлений не задерживают внима¬ ние читателей более, чем на 2 секунды каждое. Аналогичная ситуация и с использова¬ нием других рекламных средств. Но как превратить две секунды внимания в двести секунд? Какую избрать концепцию и образную систему? Об этом читайте в книге Вальтера Шёнерта «Грядущая реклама». На примере удачной и неудачной рекламы автор только что переизданного бестселле¬ ра рассказывает, как отобрать для нее новые перспективные идеи, как создавать рек¬ ламные тексты, которые будут непременно прочитаны, как соотнести изображение и текст, чтобы их совместное воздействие многократно усиливалось. Вальтер Шёнерт знает, о чём говорит: он — один из самых известных специалистов в данной области, имеющий более чем двадцатилетний стаж практической работы в качестве директора рекламного агентства международного класса. Словом, 199 пра¬ вил и примеров достижения успеха в области рекламы ~ настоящее подспорье в этом бизнесе и для новичка, и для профессионала. По вопросам заказа и покупки издания обращайтесь по тел./факсу: (095) 179 4971 E-mail: iexpert@user.iiat.ru URL: http://www.interexpert.ru
Периодические издания Издательство «Интерэксперт» выпускает два периодических издания. Это ежемесячные ньюслеттеры (информационные бюллетени) под названием «Идеи для шефа» и «Бизнес-Практикум» исефл» Тематика: рецепты менеджмента, тенден¬ ции в развитии бизне¬ са, лучшие бизнес- технологии, методика и практикаупраапения предприятием и персоналом, наилучшие приемы рекламыит.д. Предназначение: руководители пред¬ приятий, высокопоставленныеменед- жеры, руководители самостоятельных служб и отделов, бизнесмены. Информация: базируется на практичес¬ ких и аналитическихданных и матери¬ алах германского издательства «Модер¬ не индустри», атакже на последних новостях мира зарубежного и отече¬ ственного бизнеса. Объем: 12полос Периодичность: ежемесячно Рассылка: В И П -рассыпка, адресная рассылка, «Роспечать», Центральный коллектор научных библиотек РФ и др. Распространение: Россия, СНГ, Балтия «Бизнес- Практикум» Тематика: рецепты лучших продаж, луч¬ шего поиска поку¬ пателя, методика обработкииудержания клиента, наилучшиебизнес-техно- логии в области услуг, реклама и т.д. Предназначение: высший и средний менеджмент, руководители фирм по продажам и услугам, руководители отделов сбыта и маркетинга, лица, работающие непосредственно с клиентами, бизнесмены. Информация: базируется на материалах германского издательства «Модерне индустри», а также на новостях мира бизнеса в области сбыта. Объем: 12полос Периодичность: ежемесячно Рассылка: ВИП-рассылка, адресная рассьшка, «Роспечать», Центральный коллектор научных библиотек РФ и др. Распространение: Россия, СНГ, Балтия « (095) 179-4971
Издательство «Интерэксперт» представляет актуальное издание по теме рекламы Ваша реклама обретет новое качество! 99 лучших чеклмстот для вашей рекламы Это практическое пособие подго¬ товлено ведущими специалиста¬ ми Института прикладных мар¬ кетинговых исследований (Гер¬ мания, Дюссельдорф). Процесс подготовки, финансирования и прочих этапов рекламных акций и кампаний скрупулезно проана¬ лизирован. Детально описаны этапы состав¬ ления сметы и расписания рек¬ ламной кампании; разработки и внедрения стратегии рекламы; создания макета; учета морально¬ го, психологического и правового факторов; текстового и образного представления рекламы; контро¬ ля ее эффективности. Авторы обобщили и развили бо¬ гатый опыт маркетинга и рекламы, накопленный предприятиями различных отраслей и рядом ведущих рекламных агентств. Конкретные советы по лучшей организации дела систематизиро¬ ваны в виде контрольных вопросов и опорных пунктов к каждой теме. В целом набор таких обучающих тестов позволяет создать эффективную рекламу. Пособие является незаменимым инструментом активизации творческого подхода и экономии средств при достижении опти¬ мального результата. По вопросам заказа и покупки издания обращайтесь по тел./факсу: (095) 179 4971 E-mail: iexpert@userAiat.ru URL: http://www.interexpert.ru
Издательство «Интерэксперт» представляет уникальное издание по теме директ-маркетинга «Большое руководство, кок осуществить успешный ди- рект-моркетинг» - уникаль¬ ный источник сведений в этой достаточно новой для России сфере организации бизнеса. Конечно же, в нашей стране существуют издания по этой теме - но никто не делал книги «живой», книги, посто¬ янно обновляемой новой, полезной, актуальной ин¬ формацией. Книга сама по себе - насто¬ ящая энциклопедия директ- маркетинга. Но это такая эн¬ циклопедия, которая никог¬ да не устареет. Ибо регу¬ лярно обновляется и попол¬ няется новыми данными. Как только выйдет очеред¬ ная глава с актулизированной или дополненной информацией, вы по¬ лучаете ее по почте и просто вставляете в «Руководство», вынимая главу устаревшую. И таким образом вы всегда в курсе новейших дости¬ жений и мирового опыта в директ-маркетинге. По сути, это не книга - это постоянная информационная услуга, fl такая информация - очень важное орудие достижения успеха в этой весьма динамично развивающейся части бизнеса. Эмануэль Цехегбауэр БОЛЬШОЕ РУКОВОДСТВО, КАК ОСУЩЕСТВИТЬ УСПЕШНЫЙ — ДИРЕКТ — МАРКЕТИНГ Актуальные и проверенные практикой способы найти и удержать клиента По вопросам заказа и покупки издания обращайтесь по тел./факсу: (095) 179 4971 E-mail: iexpert@userAiat.ru URL: http://www.interexpert.ru
Издательство «Интерэксперт» представляет только что вышедшее издание по теме директ-маркетинга Зигфрид Фёгеле Эта книга не могла не стать бестселлером. Зигфрид Фёгеле, один из лучших мировых специалистов в своей области, рассказывает в своей книге «Директ-маркетинг. 99 секретов успеха», как организовать кампанию по ди- рект-маркетингу, как сделать прямую почто¬ вую рассылку, составить и опубликовать рек¬ ламные объявления, выпустить приложения к газетам, организовать успешную радио- и те¬ лерекламу. Книга предназначена для тех, кто занимается про¬ движением товаров и yc- лугнарынок, ивеёосно- ве лежат вопросы, чаще всего задаваемые практи¬ ческими работниками в сфере маркетинга. Отве¬ ты на 99 самых животре¬ пещущих и важных из та¬ ких вопросов и содержат¬ ся в издании. секретов успеха Третье издание По вопросам заказа и покупки издания обращайтесь по тел./факсу: (095) 179 4971 E-mail: iexpert@user.iiat.ru URL: http://www.interexpert.ru
Ханс-Георг Шуманн Ш96 С++ для детей: Пер. с нем. — М.: АО «Интерэксперт», 2002. — 416 с. Доступное и живое изложение основ популярнейшего языка програм¬ мирования С++, сопровождаемое многочисленными примерами и упражнениями с использованием среды разработки C++Builder. Ба¬ зовые принципы объектно-ориентированного программирования: инкапсуляция, полиморфизм, наследование. Создание первого про¬ екта, представление различных типов данных и операторов, описа¬ ние управляющих структур и библиотек компонентов, методы рабо¬ ты с графическими объектами, MDI-приложения и виртуальные методы. Для всех, желающих освоить язык программирования С++. ISBN 5-85523-064-3 УДК 087.5:004.434 ББК 32.973.26 = 081.1 Ш 96
Научно-популярное издание Ханс-Георг Шуманн С++ для детей Редактор Корректор Верстка Авторобложки Дизайн русского варианта В. И. Фомичёв H. E. Аникина А. Ю. Татаринов Дж. Пулидо И. И. Цыганова Подписано в печать с оригинал-макета 10.08.2001. Формат 70x100/16. Бумага офсетная. Гарнитура TextBook. Печать офсетная. Усл. печ. л. 22,23 Заказ 4109. Тираж 5000 экз. АО «Интерэксперт» (ЛР № 090032 от 13.08.97) 109548, Москва, ул. Шоссейная, д. 1, корп. 1.Тел. (095) 179-49-71 Отпечатано в полном соответствии с качеством предоставленных диапозитивов в ОАО «Можайский полиграфический комбинат». 143200, г. Можайск, ул. Мира, 93.
Ханс-Георг Шуманн Объектно-ориентированное программирование для начинающих Жили-были два парня, звали ихДенис и Брайан. Как-то раз забавы ради они решили разработать абсолютно новый язык программирования, дав емусамое короткое название. - А что, если мы назовём этот язык А? - предложил Брайан. - Сказав А, нам придётся сказать и Б, - возразил Денис. И друзья назвали новый языкС. Время шло, многие люди научились программировать на языке С. С его помощью было разработано немало крупных проектов, в том числе и операционная система Windows, на основе которой работает большинство компьютеров. Десять лет спустя парень по имени Бьёрн взялся за усовершенствование языка С. Бьёрн не стал называть его D. Поскольку созданный им язык появился на основе прежнего, он добавил к С два плюса, в результате чего появилось название С++. Так все начиналось... Тебя заинтересовал язык программирования с таким необычным названием? Ты хочешь узнать, что такое объектно-ориентированное программирование? Тогда эта книга для тебя! Она предназначена не только для детей, но также и для новичков и начинающих программистов. Не верь тому, кто говорит, что С++ очень сложный язык. С помощью этой книги ты сможешь постепенно научиться программировать. В ней на многочисленных примерах даны объяснения всех основных понятий и структур языка. SBN 5-85523-064-3 785855 2306'