/
Author: Симонович С.В. Евсеев Г.А.
Tags: компьютерные технологии программирование информационные технологии искусство программирования язык программирования c++
ISBN: 5-7805-0728-7
Year: 2001
Text
АСТпрессС.Симонович
Г.Евсеев
занимательное
занимательное
С. Симонович, Г. Евсеев
С++
ПРОГРАММИРОВАНИЕ
ПРОГРАММИРОВАНИЕ
С++АСТПРЕСС
занимательное
программирование
C++С.Симонович, ГЕвсеев
Москва
«АСТ-ПРЕСС КНИГА»
2001
АСТпресс
УДК 681.3
ББК 32.973-018
C37
Симонович Ci B., Евсеев Г. А.
C37 Занимательное программирование: С++. Книга для детей, ро¬
дителей и учителей. — М.: АСТ-ПРЕСС КНИГА: Инфорком-
Пресс, 2001. — 368 с.
ISBN 5-7805-0728-7
Книга адресована школьникам 12-15 лет, желающим обучиться составле¬
нию программ для персонального компьютера. Прочитав ее, школьник узнает
основные понятия программирования, поймет принципы хранения и преобра¬
зования данных, освоит стандартные приемы программирования. В качестве
языка и среды программирования избрана система Borland С++ Builder, как
наиболее мощная и перспективная.
Книга рассчитана на самостоятельную работу школьника, не имеющего
преподавателя или опытного наставника. Авторы заранее предусмотрели отве¬
ты на вопросы, наиболее часто возникающие на ранних этапах обучения. Глав¬
ная задача книги — увлечь читателя интересными примерами и подвести его
к уровню, после которого он сможет расширять знания с помощью учебных
и справочных пособий.
УДК 681.3
ББК 32.973-018
ISBN 5-7805-0728-7
© «АСТ-ПРЕСС КНИГА», 2001
© «Инфорком-Пресс», 2001
Становление программирования s России
Создание компьютерных программ в России началось
50 лет назад вместе с производством первых электрон¬
ных вычислительных машин. Давайте мысленно пере¬
несемся в начало 50-х годов XX века. Для техники это
было необычное время. Сейчас нам трудно представить
тот энтузиазм, с которым молодое поколение бралось
за любые, самые смелые проекты в разных областях: в
ракетной, космической и атомной технике, в радиоло¬
кации, авиации и судостроении, в вычислительной тех¬
нике и программировании. Судите сами: что могло
остановить людей, прошедших войну, переживших
невообразимые трудности и потери? С любыми задачами
они справлялись играючи, и не было таких проблем,
которые могли бы их надолго остановить.
У новых научных и технических направлений были и
замечательные организаторы. Десятки тысяч молодых
офицеров, научившихся за годы войны руководить
большими массами людей, вернулись в науку и технику,
откуда их вырвала война. Им удалось создать сплочен¬
ные творческие коллективы.
К середине 60-х годов Советский Союз стал одной из
ведущих держав мира в области вычислительной тех¬
ники. В стране работали десятки тысяч программистов
высочайшего класса. Они располагали далеко не лучшей
микроэлектроникой, но проявляли незаурядную изобре¬
тательность и смекалку. Их трудами были быстро со¬
зданы несколько поколений первых ЭВМ, как правило
превосходивших западные образцы.
3
От авторов
Портрет ЭВМ БЭСМ-6, компьютера, который
на 10-15 лет опередил свое время
Так в нашей стране сложилась своя школа программи¬
рования. Ее отличало новаторство, умение находить
нестандартные решения и, главное, командный дух в
работе. Российская школа всегда высоко ценилась в
мире. Ценится она и сегодня. Перед нашими програм¬
мистами открыты двери многих зарубежных компаний
по всему миру, особенно в Германии и США. Безработица
им не грозит ни при каких кризисах в экономике.
Трудные 80-е годы
Во времена СССР в стране не было и не могло быть «ком¬
мерческого» программирования. Всепрограммысозда-
вались либо по государственному заказу, либо в ходе
работ над государственными проектами. Созданные
программы становились как бы частью ЭВМ или сис¬
тем, созданных на базе ЭВМ. Такого понятия, как со¬
здание программ на продажу, не существовало.
4
Долгое время программы не считались товаром и в за¬
падных странах, но там после 1976 г. началось массо¬
вое распространение персональных компьютеров. Им
были нужны тысячи разнообразных программ, от слу¬
жебных до игровых. Первое время их создавали энту¬
зиасты, но вскоре для многих это стало профессией.
К коммерческим программам предъявляются особые
требования. Они должны быть привлекательны, выра¬
зительны и дружественны, иначе их не будут покупать.
Программист либо должен стать художником, мульти¬
пликатором, писателем, сценаристом и композитором,
либо ему надо собрать команду людей, обладающих
этими качествами. Такие коллективы не могут рабо¬
тать только на энтузиазме, и в мире появились компа¬
нии, профессионально занимающиеся разработкой
коммерческих программ.
Когда во второй половине 80-х годов в России возникли
рыночные отношения, эмиссары ряда западных фирм
приехали сюда набирать программистов и столкнулись
с неожиданной проблемой. Из-за того что в России ни¬
когда не было коммерческого программирования, у
наших программистов не было ни традиций, ни опыта
в оформлении программ. Они могли оригинально и та¬
лантливо написать ядро программы, составляющее ее
суть, но старательно избегали заниматься оставшимися
90 процентами скучной и нетворческой оформитель¬
ской работы. В те годы это было серьезной проблемой.
Из-за нее образование в стране творческих коллекти¬
вов, профессионально создающих коммерческие про¬
граммы, отодвинулось на целое десятилетие.
Выход из тупика
Проблемы с оформлением программ нашли наконец
свое решение во второй половине 90-х годов. Тогда в
дополнение к обычным языкам программирования
5
появились так называемые системы программирова¬
ния. Они взяли на себя нелегкую заботу по оформлению
программ, и программисты смогли сосредоточиться
только на логике работы программ, то есть заниматься
тем, что они любят и лучше всего умеют делать.
В этой книге вы научитесь работать с одной из самых
мощных систем программирования: С++ Builder. В то же
время, благодаря ей даже школьник может осваивать
программирование не «мелом на доске» и не «каранда¬
шом в тетради», а так, чтобы любая, даже самая про¬
стая программа имела законченный вид и соблюдала
все требования операционной системы Windows.
Что вам потребуется
Для работы с этой книгой необходим компьютер с опе¬
рационной системой Windows и установленной систе¬
мой програмирования С++ Builder, версия которой
может быть любой. В этой книге используется версия
С++ Builder 4, но создаваемыые программы останутся
точно такими же и в более поздних версиях.
Очень хорошо, если вам уже зна¬
комы основные приемы работы
с компьютером: установка и за¬
пуск программ, создание папок,
загрузка и сохранение файлов,
работа с мышью и клавиатурой.
Если у вас это вызывает трудно¬
сти, прочитайте, пожалуйста,
нашу книгу «Занимательный
компьютер». Написана она про¬
стым языком, стоит недорого, а
приобрести ее можно там же, где
вы приобрели эту книгу.
6
Программирование без компьютера
Глава 1
Из этой главы мы узнаем, что програм¬
мировать мы уже умеем и что компью¬
тер отличается от простого будильни¬
ка только тем, что понимает не одну
команду; амного. Мыпридумаемсобст-
венный язык программирования, попро¬
буем составить несколько простьис про¬
грамм и научимся находить в них различия
и ошибки
Программировать можвт каждый
Если ты думаешь, что не умеешь программировать, то,
наверное, недооцениваешь свои способности. Вспомни,
не приходилось ли тебе когда-нибудь заводить будиль¬
ник, чтобы не опоздать утром в школу. Приходилось?
Давай-ка вспомним, как это делается.
1. Устанавливаем стрелку будильника на
нужный час.
3. Ждем, когда часовая стрелка подойдет
к стрелке будильника. В момент, когда
они совпадут, раздастся звонок.
То, что мы сделали, называется программированием.
Правда, будильник — слишком простой прибор, и про¬
граммировать его неинтересно. Единственная опера¬
ция, которую он способен выполнить по программе,—
издать утром противный звонок, который мешает слад¬
ко спать. Но, к счастью, будильник не единственный
прибор в нашей жизни. У нас есть еще автоматические
стиральные машины, которые можно программиро¬
2. Заводим пружину будильника.
7
вать на выполнение
нескольких операций
(замачивание, стир¬
ка, полоскание, от¬
жим белья). В некоторых семьях есть плиты СВЧ, ко¬
торые можно программировать на приготовление пищи
из сырых продуктов. Интересно, что для разных про¬
дуктов (мяса, рыбы, овощей) программы должны быть
разными. Та программа, которая хорошо сварит кашу,
непременно пережарит омлет.
Чем сложнее прибор, чем больше операций он может
выполнять, тем интереснее его программировать. Но
ничто не может сравниться в этом деле с компьютером.
Внутри него есть микросхема, которая называется про¬
цессором. Процессор может выполнять более тысячи
простейших операций. Это очень много. В русском ал¬
фавите всего-навсего 33 буквы, а сколько разных книг
можно написать с их помощью?! Теперь представьте
себе, сколько разных программ, составленных из тыся¬
чи простейших операций, может исполнить компью¬
тер. Это количество бесконечно точно так же, как бес¬
конечно количество книг, которые можно написать
русскими буквами.
Изобретаем команды
Всякая программа должна выполнять какую-то задачу.
Будильник мы программируем, потому что у нас есть
задача: получить в нужное время сигнал тревоги. Сти¬
ральную машину программируют, чтобы она в нужное
время прекратила стирку и начала полоскание белья,
а потом его отжим.
Взгляни на рисунок. Перед экипажем танка стоит зада¬
ча: проехать по полю и уничтожить вражескую пушку.
Пушка защищена валом, и только с одной стороны к
ней можно приблизиться на прямой выстрел. А теперь
8
взгляни на программу, которая описывает действия
экипажа.
Эта программа состоит из инструкций. Как и вся про¬
грамма, каждая инструкция тоже выполняет какую-
то задачу. Например, инструкции 2, 3, 4 и 5 мы ввели
потому, что на маршруте находится озеро, а танки пла¬
вать не умеют.
Наверное, ты все уже понял. Мы только что изобрели
набор команд. В него входят четыре команды: ВПЕРЕД,
НАПРАВО, НАПЕВО и ОГОНЬ! С их помощью ты, навер¬
ное, сможешь и сам записать программу для движения
танка по минному полю. На рисунке показан сектор
обстрела противотанковой пушки. Как ты понимаешь,
9
1) ВПЕРЕД 4
2) НАПРАВО
3) ВПЕРЕД 2
4 ) НАЛЕВО
5) ВПЕРЕД 2
6) НАПРАВО
7 ) НАПРАВО
8) ОГОНЬ!
заезжать в него нельзя. Определи, чем закончится дви¬
жение танка по каждой из приведенных программ.
Вариантов всего четыре: попадание в цель, промах,
подрыв на мине и гибель под огнем орудия.
10
Вперед 5
Направо
Огонь!
Направо
Вперед 2
Налево
Вперед 5
Направо
Огонь!
Направо
Вперед 6
Налево
Вперед 3
Налево
Огонь!
Направо
Вперед 2
Налево
Вперед 4
Направо
Вперед 1
Налево
Огонь!
Изобретаем язык программирования
Мы только что придумали несколько команд, с помо¬
щью которых можно записывать программы. Мы почти
придумали новый язык программирования и можем
даже дать ему название, например Атака 1.0. Цифры 1.0
означают, что это первая версия нашего языка про¬
граммирования. Если мы когда-нибудь введем в него
новые команды, то получится новая версия, Атака 2.0, воз¬
можности которой будут больше. Если мы внесем в него
мелкие изменения, будем менять вторую цифру: Атака 2.1,
Атака 2.2 и так далее.
Давайте, например, придумаем команду ДЫМ. По этой
команде наш танк создаст дымовую завесу. Вы уже
догадались, зачем это нужно? После исполнения такой
команды танку разрешается проезжать через сектор
обстрела вражеской пушки. Чувствуете, как новая вер¬
сия языка Атака 2.0 дает танку новые возможности? Так
и в языках программирования. Каждая новая версия
дает программистам новые возможности в управлении
компьютером.
— А зачем программистам управлять танком? Танками
управляют танкисты.
— Программистам все равно, чем управлять. Вместо
танка может быть автоматический станок или косми¬
ческий аппарат, это может быть даже автоматизиро¬
ванная птицеферма. У птицефабрики другие задачи,
поэтому и набор команд будет другим, и язык програм¬
мирования для нее лучше подойдет другой.
Инструкции, операторы и параметры
Для компьютера все команды являются инструкциями.
Получив очередную инструкцию, он должен ее выпол¬
нить. Все инструкции процессор компьютера выполняет
в том порядке, в котором они в него поступают. Обычно
11
они выполняются по очереди, но среди множества ин¬
струкций процессора есть и такие, которые позволяют
менять этот порядок очереди и переставлять инструк¬
ции с места на место.
А теперь давайте повнимательнее посмотрим на наш
язык программирования Атака 2.0. В него входят команды
ВПЕРЕД, ВПРАВО, ВЛЕВО, ОГОНЫ и ДЫМ. Все эти слова
являются операторами нашего языка. Это значит, что
их (и только их) можно использовать в своих програм¬
мах. Если в программе появится инструкция НАЗАД,
то это будет ошибкой, потому что никакого оператора
НАЗАД в нашем языке Атака 2.0 пока нет.
Список операторов — важная часть языка программи¬
рования, но это еще не весь язык программирования.
Кроме списка операторов в нем должны быть опреде¬
лены правила их использования (они называются пра¬
вилами синтаксиса). Например, в нашем языке рядом
с оператором ВПЕРЕД обязательно должно стоять какое-то
число, которое показывает, как далеко должен продви¬
нуться танк. Это число называется параметром.
Из одних и тех же операторов можно получить разные
инструкции, например:
Инструкция
ВПЕРЕД 2
Параметр
12
Оператор
ВПЕРЕД 5
ВПЕРЕД 3
Мы можем договориться, что параметр не обязательно
должен быть положительным числом. Допустим, что
нам разрешается записать отрицательный параметр,
например:
ВПЕРЕД -5
Это уже другое правило синтаксиса, причем очень по¬
лезное, потому что ВПЕРЕД -5 — это то же самое, что и
НАЗАД 5. Вот мы уже и способны выполнить команду
НАЗАД 5, даже не имея оператора НАЗАД. У нас на глазах
рождается новая версия языка — назовем ее Атака 2.1.
Что такое компилятор?
У нашего языка программирования есть один важный
недостаток — он записан символами русского языка.
Это значит, что пользоваться им в Австралии или в
Гренландии будет невозможно. В программировании
есть традиция использовать буквы, символы и слова
английского языка. Это не значит, конечно, что не су¬
ществует российских языков программирования, но
они плохо приживаются, и вовсе не потому, что рус¬
ский язык меньше подходит для программирования,
чем английский. Здесь дело в другом. Сейчас объясним.
Чтобы программа заработала, нашему языку еще кое-
чего не хватает. Дело в том, что процессор компьютера
не понимает ни русских, ни английских букв и ника¬
ких других. Он понимает только сигналы, которые за¬
писываются цифрами. Нам надо перевести слова своего
языка программирования в цифровой код, который
понимает процессор. Вручную сделать это очень труд¬
но, и такой перевод поручают самому компьютеру. Для
этого служат специальные программы, которые назы¬
ваются компиляторами. Вот компилятора-то нам и не
хватает.
13
Составить набор операторов для своего языка програм¬
мирования — дело нетрудное. Придумать правила для
их записи вместе с параметрами — тоже несложно. А вот
сделать компилятор — задача неимоверной сложности.
Самый первый язык программирования назывался
FORTRAN (ФОРТРАН). Его первая версия появилась еще
в 1958 г. Набор операторов и правила синтаксиса для
него разработала группа программистов всего за не¬
сколько недель. А вот разработка компилятора заняла
несколько лет и стоила десятки, если не сотни милли¬
онов долларов!
Разработку набора операторов языка и правил синтак¬
сиса называют идеей языка программирования. А раз¬
работку компилятора называют реализацией языка
программирования. Пока у нас для языка Атака 2.1 есть
только идея, но реализации, увы, нет.
Реализация языка обходится гораздо дороже разработ¬
ки его идеи. И это относится не только к первой версии,
но и ко всем последующим. Нам нетрудно ввести в идею
языка новый оператор, такой, как ДЫМ, но заново пере¬
делать компилятор при переходе от версии 1.0 к вер¬
сии 2.0 — дело очень сложное. Такие сложные дела
оправдываются только в том случае, когда языком про¬
граммирования пользуются не несколько человек, а
десятки и сотни тысяч людей. Вот именно для того,
чтобы как можно сильнее расширить круг пользовате¬
лей языка программирования, его операторы и стара¬
ются записывать английскими словами и буквами.
В разных странах не раз были попытки создать свои
национальные языки программирования на русском,
немецком, французском языках. Но опыт показывает,
что из-за ограниченности круга потребителей они мед¬
ленно развиваются, у них редко выходят новые версии
14
и они быстро начинают отставать по возможностям от
своих англоязычных конкурентов.
Нередко национальные языки программирования ис¬
пользуют в качестве учебных, и в некоторых школах
изучают русскоязычные языки программирования.
Однако для того, чтобы стать настоящим программи¬
стом, лучше все-таки осваивать универсальные языки,
признанные во всем мире. Здесь очень поможет знание
английского языка.
Делаем язык международным
Самое первое, что должен сделать тот, кто хочет стать
программистом, — это начать изучать английский
язык. Однако дело это хоть и не очень трудное, но все-
таки длительное — на это уйдет несколько лет. К тому
же английский язык изучают не во всех школах. Так
как же нам быть?
Будем постепенно тренироваться на примерах, а где не¬
обходимо, мы постараемся вам помочь. Вот, например,
взгляните на следующую программу и догадайтесь, что
делают команды LEFT, RIGHT, FORWARD, FIRE и SMOKE.
RIGHT
FORWARD 2
LEFT
FORWARD 4
SMOKE
FORWARD 1
RIGHT
FIRE
15
А теперь мы сократим наши команды так, чтобы вооб¬
ще забыть, на каком языке они написаны. Ведь в реа¬
лизации языка смысл слов, из которых состоят опера¬
торы, никак не используется. Мы ведь
собираемся изучать программирова¬
ние, а не английский язык, не правда
ли? Новый язык программирования,
который у нас получился, можно на¬
звать по-английски: Attack 3.0.
Определите, на каком рисунке изобра¬
жен результат действия следующей
программы.
16
FWD 3
LFT
RHT
FWD 1
SMK
FWD 2
RHT
FRE
Глово 2
Процедуры и функции
В этой главе мы узнаем, какие бывают
языки программирования, познакомимся
с процедурами, узнаем, что такое фор¬
мальные и фактические параметры и
поймем, что за очень непонятными сло¬
вами скрываются совсем простые вещи
Как выбрать язык программирования
Из прошлой главы мы узнали, что язык программиро¬
вания состоит из трех вещей: набора операторов, пра¬
вил синтаксиса и компилятора.
Если бы не потребность в компиляторе, то языки про¬
граммирования можно было бы не изучать, а изобре¬
тать. Каждый сам может придумать себе столько опера¬
торов, сколько захочет, и назначить им любые правила
использования. Это не сложнее, чем выдумать правила
для настольной игры с солдатиками. Вы сами можете
решить, на сколько клеток ходит конник и как далеко
стреляет лучник. Как только в этих правилах что-то
изменится или на поле появится новый «оператор» (ria-
17
ЯЗЫК ПРОГРАММИРОВАНИЯ
Идея языка
Реализация языка
Набор
операторов
Правила
синтаксиса
Компилятор
пример, пулеметчик, который может
стрелять несколько раз за один ход), об¬
разуется новая реализация игры.
В настольной игре компилятор не ну¬
жен. Точнее говоря, там «компилято¬
рами» выступают сами игроки. Вам,
наверное, не раз приходилось спорить
с друзьями во время игры по поводу
правил и приходилось изменять какие-
то правила, чтобы игра была интерес¬
ной. Каждый раз, когда вы с друзьями
устраиваете такие споры, вы на самом
деле занимаетесь компиляцией новой
версии игры.
В жизни это очень просто. Поспорили,
пошумели — и продолжили играть по
новым правилам. С компьютерами дело
обстоит гораздо сложнее. У компьюте¬
ра компилятор — это чрезвычайно
сложная программа, которую обычный
человек не может ни сделать, ни изме¬
нить. Вот поэтому нам и приходится не
создавать такие языки программирова¬
ния, которые нам нравятся, а пользо¬
ваться теми, для которых уже есть го¬
товая реализация.
Сегодня в мире существует несколько
тысяч языков программирования. Как
выбрать себе тот, который наиболее
подходит? Прежде всего, люди изуча¬
ют язык, для которого у них имеется
компилятор или когда они знают, где
его достать. Без компилятора изучать
язык программирования совершенно
18
бесполезно. Можно хоть год просидеть с
карандашом и бумажкой, а потом через
неделю все равно все забудется. Это то же
самое, что учиться плавать без воды или
ездить на велосипеде без велосипеда.
Если с выбором компилятора нет про¬
блем, то выбирают тот язык, который
наилучшим образом подходит для решения той или
иной задачи. Языки программирования, подготовлен¬
ные для каких-то специальных задач, называют спе¬
циальными.
У инженеров, банкиров, военных разные задачи. Они
выбирают для себя разные языки программирования.
Инженеры предпочитают язык ФОРТРАН (FORTRAN),
банкиры часто используют язык КОБОЛ (COBOL), воен¬
ные планируют боевые операции и управляют войсками
с помощью программ, написанных на языке АДА (ADA).
Архитекторы считают важным знать язык программиро¬
вания ЛИСП (LISP). Многим ученым полезен язык ПРОЛОГ
(PROLOG). Программисты, которые пишут программы
для Интернета, обычно знают язык JAVA. Все эти языки
программирования — специальные. Они имеют такие опе¬
раторы, которые позволяют решать специальные задачи
наиболее просто. Обычно эти языки изучают после устрой¬
ства на работу. Изучать их «заранее» не имеет смысла.
Кроме специальных есть еще универсальные языки
программирования. С их помощью можно решать почти
любые задачи: и управлять птицефермой, и создавать
компьютерные игры. Такие языки можно изучать «зара¬
нее» — их знание пригодится всегда. Сегодня наиболее
универсальными стали три языка программирования:
♦ Бейсик (Basic);
♦ Паскаль (Pascal);
4- Си++ (С++).
19
От языка программирования
к системе программирования
3a долгое время развития эти три языка стали не про¬
сто языками программирования, а целыми системами
программирования. Система отличается от языка тем,
что она не только имеет компилятор, но содержит еще
и несколько дополнительных программ для упрощения
программирования. С ее помощью можно не писать
программы, а собирать их из готовых компонентов точ¬
но так же, как из компонентов детского конструктора
собирают самые разнообразные игрушки.
Так, язык Бейсик превратился в систему программиро¬
вания Visual Basic. Язык Паскаль реализовался в систему
Delphi, а язык Си++ реализован в нескольких системах,
например Borland С++ Builder и Microsoft Visual С.
ЯЗЫКИ ПРОГРАММИРОВАНИЯ
Бейсик
Паскаль
СИСТЕМЫ ПРОГРАММИРОВАНИЯ
20
СИ++
Visual Basic
Delphi
С++ Builder
Visual С++
Наша книга посвящена системе программирования
Borland С++ Builder. Если она вам понравится, то в дру¬
гих похожих книгах вы познакомитесь с системами
Visual Basic и Delphi и при этом научитесь создавать про¬
граммы на языках Бейсик и Паскаль. Тогда вы будете
знать все три основных универсальных языка програм¬
мирования и все три системы, в которых они реализо¬
ваны. После этого вам будет нетрудно освоить любой
другой язык программирования, когда это потребуется.
Что такое подпрограмма
Программисты не любят писать большие программы
за один прием от начала и до конца. Когда в программе
появляется несколько десятков операторов, за ними
становится трудно следить, а когда их число возрастает
до нескольких сотен, досадные ошибки становятся не¬
минуемыми. Современные игровые программы содер¬
жат миллионы строк кода — их вообще невозможно
составлять, если не поделить программу на отдельные
подпрограммы.
Одна подпрограмма отвечает за то, как герой ходит,
другая — за то, как он прыгает, третья — за то, как он
стреляет. Современные программы нередко состоят из
десятков и сотен тысяч сравнительно небольших под¬
программ. Все они находятся в памяти компьютера и
ждут, когда их вызовут. В тот момент, когда игрок
нажимает кнопку «Огонь!», запускаются подпрограммы,
обрабатывающие режим стрельбы. Одна подпрограмма
проверяет, куда попали заряды, другая контролирует
количество оставшихся боеприпасов, третья ведет учет
набранных очков и так далее.
Давайте вернемся к нашему гипотетическому языку
программирования Атака. В нем, как вы, наверное, по¬
мните, нет оператора НАЗАД (по-английски BACKWARD
21
или BWD). Мы уже обсуждали, что если разрешить ис¬
пользовать оператор FWD с отрицательным парамет¬
ром, то это и будет движение назад, например:
FWD -3
Однако это не совсем то, что нужно. При таком движе¬
нии танк попятится назад, но не развернется. А как
сделать, чтобы он и развернулся, и сместился назад?
Наверное, вы уже догадались, как это сделать. Надо три
раза повернуться, а потом двинуться вперед:
Вы видите, как мы научились заменять инструкцию, для
которой нет оператора в нашем языке программирования,
тремя другими инструкциями, для которых необходимые
операторы есть. Этот прием называется эмуляцией. Наша
подпрограмма из четырех инструкций эмулирует дей¬
ствие недостающей инструкции. Эту маленькую подпро¬
грамму можно назвать процедурой разворота.
Процедуры и функции
Программисты различают два вида подпрограмм: про¬
цедуры и функции. Это деление, конечно, очень услов¬
ное, и не во всех языках программирования оно отра¬
жено, но разницу между ними надо чувствовать.
Функция — это подпрограмма, которую вызывают,
чтобы выполнить какие-то расчеты или проверки. Когда
она завершает работу, то возвращает управление вызы¬
вающей программе и передает ей результат расчета.
Представьте себе, что наш танк движется по полю ко¬
роткими шажками и после каждого шага вызывается
22
RHT
RHT
RHT
FWD 3
или так
LFT
LFT
LFT
FWD 3
GAME OVER
подпрограмма-функция для проверки, на что он на¬
ехал. Если это дерево или куст, функция может возвра¬
щать значение 0 — тогда танк движется дальше. А если
проверка покажет, что он наехал на мину, функция воз¬
вратит в программу другое число, например 1, и тогда
основная программа поймет, что произошло важное со¬
бытие. Она может принять меры и, например, вызвать
подпрограмму, которая называется
Game_Over! Знакомая ситуация, не
правда ли?
Процедура — это тоже подпрограмма. Ее тоже вызыва¬
ют, чтобы выполнить какие-то действия, но от нее не
требуется возвращать основной программе какие-либо
значения. В нашем примере с разворотом танка вполне
достаточно выполнить разворот и ничего не сообщать
вызывающей программе, поэтому мы и назвали нашу
подпрограмму процедурой разворота, а не функцией.
Объявление процедур и функций
В тех случаях, когда в языке программирования не
хватает средств, чтобы выполнить какую-то операцию,
его возможности можно расширять за счет создания
подпрограмм (процедур или функций).
Теперь мы могли бы написать программу, по которой
танк подъедет к орудию, развернется и отбуксирует его
в другое место.
23
FWD 3
RHT
FWD 3
LFT
FWD 2
BWD 5
LFT
FWD 3
Однако, к сожалению, у нас ничего из этого не выйдет.
Когда компилятор будет переводить нашу программу
с гипотетического языка Атака на язык, понятный про¬
цессору, он «споткнется» наинструкции BWD 5, потому
что у него нет оператора BWD и, что это такое, он не зна¬
ет. Мы-то знаем, что BWD — это никакой не оператор
языка программирования, а наша собственная проце¬
дура, но компилятору об этом никто ничего не сказал.
Как же дать компилятору понять, что это не оператор?
Самый простой способ: заранее предупредить компи¬
лятор о том, что BWD — это наше собственное изобре¬
тение. Обычно это делают так: в самом начале програм¬
мы вставляют описание всех процедур и функций,
которые в ней будут использоваться. Впрочем, не обя¬
зательно это делать именно в начале. Старые языки
программирования действительно требовали, чтобы
это было сделано только в начале, а современным язы¬
кам это все равно. Тем не менее программисты нередко
придерживаются строгих старых правил.
Выглядеть это может, например, так.
24
PROCEDURE BWD
RHT
RHT
RHT /
FWD 5
END
FWD 3
RHT
FWD 3
LFT
FWD 2 /
<^BWD(T^S
LFT
FWD 3
Описание процедуры
Основная программа
_Вызов процедуры
Здесь у нас появились два новых оператора: PROCEDURE
и END. Без них не обойтись, ведь кто-то же должен ука¬
зать компилятору, где начинается и где заканчивается
описание нашей процедуры BWD. Теперь, когда компи¬
лятор встретит незнакомое слово BWD и не найдет для
него знакомого оператора, он посмотрит: а не подпро¬
грамма ли это? Если так оно и есть, он просто вместо
этого слова подставит те инструкции, которыми описано
действие этой подпрограммы.
Формальные и фактические параметры
У нашей процедуры BWD есть один важный недоста¬
ток. Она позволяет развернуть танк и отодвинуть его
на пять клеток. А если надо на три, четыре или шесть
клеток? Что, так и будем для каждого случая писать
новую процедуру?
Это было бы неразумно. А нельзя ли сразу подготовить
однууниверсальную процедуру, которая будет развора¬
чивать и отодвигать танк на столько клеток, на сколько
потребуется, например на n клеток? Конечно, можно:
PROCEDURE BWD (n)
RHT
RHT
RHT
FWD n
END
— А откуда процедура во время работы узнает, чему
равно число n?
— Из основной программы. Если там будет записано
BWD (3), то она возьмет параметр 3 и подставит его в под¬
программу вместо n. А если там будет записано BWD (5),
то подставится число 5 и так далее. Как видите, этот
параметр у нас стал передаваемым.
25
При вызове подпрограммы мы указываем то значение
параметра, которое нам нужно, — оно называется фак¬
тическим значением параметра. А при описании под¬
программы мы еще не знаем, на сколько клеток при¬
дется двигать танк, и поэтому вместо числового значения
мы используем буквенный параметр n. Такие значения
параметров называются формальными.
Формальные значения используются при описании
процедур и функций, а при их вызове они должны быть
заменены фактическими параметрами.
Библиотеки подпрограмм
Современные компьютерные игры содержат миллионы
инструкций. Если программист будет за день создавать
по 100 строк (обычно одна строка — одна инструкция),
то среднюю игровую программу он будет делать лет 30
или 50, если при этом не запутается. А если для уско¬
рения работы поручить ее 30 программистам, то они
запутаются еще быстрее и не завершат программу ни¬
когда.
Поэтому современный программист предпочитает как
можно меньше программировать и как можно больше
использовать заранее заготовленные и давно проверенные
подпрограммы. Осталось только решить, где их взять.
Множество функций и процедур поступает к нам на
компьютер вместе с системой программирования.
Например, если мы задумали изучать язык програм¬
мирования С++, то вместе с системой программи¬
рования на компьютер кроме компилятора уста¬
навливаются стандартные библиотеки этого языка.
Библиотеки — это хранилища готовых функций и про¬
цедур. Их надо только взять (вызвать в свою про¬
грамму), немного поднастроить, и они будут отлично
работать.
26
Разумеется, программист не обязан пользоваться только
стандартными библиотеками. Если хочет, он может
написать собственные процедуры и функции, а когда
их станет очень много — собрать их в свою персональ¬
ную библиотеку, чтобы больше никогда не программи¬
ровать то, что один раз уже было сделано.
Обычно фирмы, которые создают компьютерные игры,
имеют свои «фирменные» библиотеки функций. Эти
библиотеки постепенно наращиваются, и каждая сле¬
дующая программа той же фирмы создается быстрее и
при этом выглядит интереснее и красивее.
Кок пишут программы
Сегодня программы пишут примерно так же, как строят
дома. Впрочем, ныне строители предпочитают гово¬
рить, что они не строят дома, а возводят. Программи¬
сты же говорят, что они не пишут программы, а коди¬
руют их. Это связано с тем, что программы, как и дома,
собирают из готовых блоков. Программисты стремятся
использовать как можно больше процедур и функций
из готовых библиотек и в основном занимаются не
столько программированием, сколько настройкой этих
готовых блоков. Лишь кое-где им приходится вписать
27
несколько инструкций, чтобы связать отдельные блоки
между собой. Точно так же строитель укладывает ра¬
створ для скрепления железобетонных конструкций.
Программа, написанная на языке программирования,
в основном состоит из многочисленных вызовов стан¬
дартных библиотечных подпрограмм. Такая программа
называется исходным текстом или исходным кодом.
Это еще не сама программа — это только ее текст. Текст
можно читать или править, но работать как программа
он пока не может.
На втором этапе исходный текст обрабатывают компи¬
лятором того языка программирования, на котором он
написан. После обработки компилятором исходный код
превращается в объектный. Он уже не написан ни на
каком языке программирования, и читать его беспо¬
лезно. Это просто набор цифр, понятных процессору.
Но и это еще не готовая программа. Хотя процессор и
понимает, что там написа¬
но, выполнить работу он
не может — ему не хвата¬
ет того кода, который со¬
держится в библиотеках
стандартных функций и
процедур.
На третьем этапе запуска¬
ется программа, которая
называется редактором
связей. Она проверяет,
какие стандартные под¬
программы были исполь¬
зованы программистом,
извлекает их код из библи¬
отек и вставляет в объект¬
ный код, сделанный ком¬
Библи¬
отеки
подпро¬
грамм
КОМПИЛЯТОР
Объектный код
28
Исходный
код
РЕДАКТОР СВЯЗЕЙ
Библи¬
отеки
подпро¬
грамм
Исполнимый код
(готовая
программа)
пилятором. Только после этого получается настоящая
рабочая программа. Ее еще называют исполнимым
кодом.
В виде исполнимого кода программу можно запустить,
и она заработает. Ее можно передать друзьям, чтобы
они запустили ее на своих компьютерах. Правда, зара¬
ботает она не на всех компьютерах, а только на компью¬
терах той системы, в которой была обработана компи¬
лятором. Если создать программу на компьютере типа
IBM PC, работающем в системе Windows 98, то и рабо¬
тать она будет успешно только на компьютерах той же
системы.
Заключительное упражнение
Программист написал игровую программу, имитиру¬
ющую действие танковой бригады в сражении под Про-
хоровкой (июль 1943 г.). Чтобы вступить в бой, танки
должны преодолеть по мосту небольшую реку. В бри¬
гаде 65 танков, и чтобы 65 раз не кодировать процедуру
форсирования реки, программист написал универсаль¬
ную процедуру под названием Bridge (Мост). Она исполь¬
зует три формальных параметра: a, b и с.
29
PROCEDURE Bridge(a,b,c)
FWD a
RHT
FWD Ь
LFT
FWD с
END
На маршруте движения танками управляет основная
программа, но когда танки подходят к мосту, вызыва¬
ется процедура Bridge. При этом для каждого танка в
нее подставляются фактические параметры. Какой из
следующих вызовов процедуры действительно прове¬
дет танк через мост?
A. Bridge (2, 3, 3) B.Bridge (3, 3, 2)
С. Bridge (3, 2, 3) D.Bridge (2, 3, 2)
Как люди учились программировать
компьютер
Глава 3
В этой главе мы за несколько минут
пройдем тот путь, который программи¬
рование прошло за полвека, и узнаем, что
приемы программирования менялись мно¬
гократно. Нам будет особенно приятно
узнать, что современный стиль програм¬
мирования, который шюывается визуаль-
ным, почти не требует изучения языков
программирования, потому что про¬
граммы создаются из готовых блоков
Программирование в машинных кодах
Программировать компьютер можно разными способа¬
ми. Самый очевидный — это брать разные команды,
которые понимает процессор, и записывать их подряд
друг за другом. Написав десяток команд, уже можно
утомиться, а когда их станет два десятка, даже очень
опытные программисты начинают путаться и делать
ошибки.
Для первых компьютеров программ не существовало,
поэтому их создавали те же самые люди, которые созда¬
вали эти компьютеры. Кроме них, мало кто знал, какие
команды понимает процессор, и только они умели им
управлять. Эти программы записывали как последова¬
тельности чисел, например так:
31, 45, 00, 33, 46, 00, 24, 32, 45, 00...
Такое программирование называют программировани¬
ем в машинных кодах. Понять, где здесь команды, где
31
числа, а где адреса ячеек памяти, из которых они берутся,
мог только тот, кто своими руками разрабатывал ком¬
пьютер и его процессор. К тому же у разных процессо¬
ров разные системы команд, и если кто-то мог научиться
программировать в машинном коде один компьютер,
то для другого компьютера ему приходилось начинать
все заново.
Для примера посмотрим, как такую программу можно
было расшифровать.
31, 45, 00 Скопировать в процессор число, которое хранится
в ячейке памяти №69
33, 46, 00 Скопировать в процессор число из ячейки №70
24 Сложить эти два числа
32, 45, 00 Скопировать результат в ячейку памяти №69
Чтобы каждый день заново не писать программу, ее за¬
поминали с помощью отверстий в длинной бумажной
ленте (эту ленту называли перфолентой). Такой подход
к программированию существовал в конце 40-х и начале
Программа длиной всего лишь в несколько сот инструкций
образовывала целый клубок бумажной ленты
32
50-х годов, а ввод программ с перфоленты сохранился
на некоторых компьютерах даже до начала 70-х годов.
\ЧЛ Если сравнить машинное программирование со строительством
*® дома, то оно больше всего похоже на вырубание пещеры в скале
с помощью молотка и зубила. S результате получается очень проч¬
ное жилище, способное перенести сильное землетрясение, но ком¬
фортом оно не отличается, 9а и постройку его приятной не назо¬
вешь.
Несмотря на то что машинным кодом программы никто
давным-давно не пишет, все равно все программы запи¬
саны именно этим кодом — ничего другого процессор не
понимает. Просто в наши дни машинный код пишут
не программисты, а программы-компиляторы. Компи¬
лятор переводит тексты программ с языков програм¬
мирования, которыми они записаны, в машинный код.
Если хотите, можете посмотреть на своем компьютере,
как выглядит машинный код. Для этого надо запустить
небольшую программу, которая называется debug.exe,
а делается это так.
1. Щелкните левой кнопкой мыши на кнопке Пуск.
Откроется меню, которое называется Главным.
2. В этом меню выберите пункт Выполнить — откроется
диалоговое окно Запуск программы.
3. Введите в текстовое поле имя программы: debug и
щелкните на кнопке OK — программа запустится.
2—1619
33
Программа debug.exe написана не для операционной
системы Windows, а для устаревшей системы MS-DOS,
поэтому ее окно на экране может выглядеть непривыч¬
но — это просто черный прямоугольник. Не пугайтесь,
а наберите команду:
-d 0000:0000
и нажмите клавишу ENTER. Знак «минус» перед бук¬
вой d можно не набирать — он подставляется автома¬
тически самой программой.
Вы увидите на экране содержимое оперативной памяти
CBoef4) компьютера, записанное в машинном коде. Мо¬
жете изменить адрес в памяти и посмотреть, что запи¬
сано там:
-d 0123:4567 [ENTER]
Сразу скажем, что машинный код не слишком поня¬
тен, но нам он и не нужен. Просто хотелось бы знать,
как выглядят программы, когда они загружены в ком¬
пьютер. Закройте программу debug.exe, ничего не сохра¬
няя, и продолжим знакомство с программированием.
Программирование на Ассемблере
Людям трудно представлять команды процессора в
виде чисел. Команды начинают путаться с параметрами,
которые тоже представлены числами, и, хотя компью¬
тер с легкостью разбирается в машинном коде, человеку
с ним разбираться очень трудно.
Прежде всего придумали записывать команды процес¬
сора не цифрами, а буквами. Современные процессоры
понимают более тысячи различных команд, и запом¬
нить, какая команда записывается каким числом,
весьма непросто. Буквенная запись все-таки понятнее,
например такая.
34
Операция
Назначение
Код операции
Move
Переместить
MOV
Add
Прибавить
ADD
Subtract
Вычесть
SUB
Multiply
Умножить
MUI
Return
Вернуться
RET
No operation
Ничего не делать
NOP
Затем придумали давать имена ячейкам памяти и ячей¬
кам процессора (эти ячейки называются регистрами).
После этого стало возможно вызывать содержимое из
ячеек памяти не по номеру, а по их имени. Это гораздо
удобнее: согласитесь, что если в компьютере миллионы
ячеек памяти, то по их номерам невозможно запом¬
нить, где что хранится.
В итоге запись машинных команд стала выглядеть чуть
более осмысленно.
mov a, account
Загрузить в регистр A процессора число из ячейки
памяти с именем account (текущий счет)
mov b, profit
Загрузить в регистр В процессора число из ячейки
памяти с именем profit (прибыль)
add a, b
К содержимому регистра А прибавить содержимое
регистра В (увеличили текущий счет)
mov account, a
Отправить содержимое регистра А на хранение в
ячейку памяти с именем account. Теперь денег на
счету стало больше, и они продолжают храниться
там же, где хранились
Такую запись называют записью на языке Ассемблера.
Для перевода этой записи в машинный код служат про¬
граммы, которые также называются Ассемблерами.
35
Программирование на Ассемблере очень близко к про¬
граммированию в машинном коде. Это и хорошо, и
плохо одновременно. Хорошо — потому, что програм¬
мы, написанные на Ассемблере, выполняются очень
быстро. Плохо — потому, что надо хорошо представ¬
лять устройство процессора и памяти, к тому же очень
многое приходится держать в голове. В итоге получа¬
ется так, что на Ассемблере выгодно писать только
очень маленькие программы, от которых требуется
высокая скорость работы.
К таким программам часто относятся системные про¬
граммы компьютера, например драйверы устройств.
Поэтому сегодня Ассемблером пользуются системные
программисты, отвечающие за взаимную работу раз¬
ных устройств компьютеров.
Если хотите, можете посмотреть содержимое оператив¬
ной памяти своего компьютера, записанное на языке
Ассемблера. Вэтом вампоможетпрограммасЬЬид.ехе, ко¬
торой мы уже пользовались, только в ней надо давать
не команду -d, а команду -и, например так:
-u 0000:0000 [ENTER]
или так:
-u 0123:4567 [ENTER]
Алгоритмическое программирование
Чтобы создавать программы наАссемблере, надо очень
хорошо знать устройство процессора, а у разных ком¬
пьютеров процессоры разные, так что и языки Ассемб¬
лера у них тоже разные. Требовать от обычного чело¬
века таких знаний невозможно. Инженерам и ученым
необходимы другие языки программирования, кото¬
рые не зависят от процессора.
Поэтому в середине 50-х годов началась разработка
алгоритмических языков программирования. Их смысл
36
состоял в том, чтобы люди могли писать программы
простыми словами (операторами языка) по простым
правилам синтаксиса, а перевод в машинный код вы¬
полняла бы специальная программа — компилятор.
Наша задача по суммированию данных из двух ячеек
памяти на алгоритмическом языке может быть запи¬
сана одним простым оператором, например так:
account = account + profit
Автор этой программы не должен думать, в каких ячей¬
ках памяти хранятся его расчетный счет и доход. Его
не волнует, какие регистры процессор использует во
время операции сложения, — он может сосредоточиться
только на своей задаче, а до устройства компьютера ему
нет дела.
Как видите, операторы алгоритмических языков про¬
граммирования очень похожи на знакомые всем со
школы математические формулы. Однако здесь есть и
важное различие. Например, если бы ученик написал
в своей тетради, что x = x + 2, то учитель сразу бы поста¬
вил ему заслуженную двойку.
Здесь дело в том, что переменная x никогда не может
равняться выражению x+2, и эта запись не имеет смыс¬
ла. А когда то же самое написано в программе, смысл
есть, но другой. Дело в том, что оператор «=» в програм¬
мах не означает равенство, а означает присваивание.
Запись x = x + 2 в программе надо понимать так, что в
ячейку памяти с именем x следует поместить другое
значение, равное x + 2, то есть увеличить значение пере¬
менной на 2 единицы.
В некоторых языках программирования принимают
специальные меры, чтобы не путаться. Например, в
37
языке С++, который мы и будем изучать, оператор при¬
сваивания может быть записан так:
account = account + profit;
Здесь «=» — это не знак равенства, а знак присваива¬
ния. В других языках программирования для этой цели
могут вводить особый знак (например, в Паскале —
«:=»). В языке С++ особый знак не вводят, но помнят,
что символ «=» — это особый оператор.
Первыми заказчиками задач для компьютера стали
военные. Им всегда были нужны артиллерийские таб¬
лицы для расчета движения снаряда для разных моде¬
лей пушек и разных типов боеприпасов.
Еще больше задач добавило начавшееся в те годы осво¬
ение ракетной техники. Движение ракеты посложнее,
чем снаряда, потому что на нее действуют силы от дви¬
гателей. Эти силы все время изменяются по мере рас¬
ходования запаса топлива на борту.
Для расчета траектории движения артиллерийского
снаряда обычно достаточно было нескольких десятков
строк инструкций, составленных по математическим
формулам.
Программы для ракет были посложнее, но все равно в
нескольких сотнях строк обычно удавалось рассчитать
траекторию движения аппарата. Примерно такой же
размер имели программы для расчета прочности мос¬
тов, больших кранов, плотин, дамб и других конструк¬
ций.
x\V/ Если сравнивать алгоритмическое программирование со строи-
в» тельством дома, то оно похоже на создание жилища из нестан¬
дартных камней, скрепленных между собой глиной или раствором.
Примерно так строили крепости в средние века.
38
Процедурное программирование
Артиллерийский снаряд — это один твердый объект,
движение которого математики описывают несколь¬
кими уравнениями. Его траекторию можно рассчитать
программой из нескольких десятков строк. Но пред¬
ставьте, что делать, если надо рассмотреть не один твер¬
дый объект, а десятки тысяч взаимосвязанных объек¬
тов?
К таким задачам ученые пришли в начале 70-х годов,
когда начался расцвет математического моделирова¬
ния. К этому времени компьютеры достигли прилич¬
ной мощности. С их помощью стало возможно рассчи¬
тать, например, что произойдет с автомобилем, если он
на полной скорости столкнется с препятствием, или, на¬
пример, что будет с его багажником, если на него неча¬
янно наступит слон. Конечно, можно разбить несколько
автомобилей на испытаниях, но все возможные вари¬
анты столкновений так проверить невозможно. Вы
представляете, сколько существует разных марок авто¬
мобилей, сколько может быть разных препятствий и
сколько стоит все это испытать? К тому же кроме авто-
Исторический танк CV35 (Италия, 1935 г.)
и его математическая модель
39
мобилей надо испытывать самолеты, вертолеты, кораб¬
ли, подводные лодки, дома, мосты и многое-многое
другое.
Тогда инженеры и ученые предложили ставить испыта¬
ния методом математического моделирования. Возьмем,
к примеру, танк. Мысленно разделим его на тысячи
маленьких элементов. Для каждого элемента можно
записать уравнения, которые покажут, что с ним будет
при воздействии внешних сил. Хотя уравнения для
каждого элемента записываются несложной програм¬
мой, общее количество элементов такое, что полную
программу человеку написать трудно.
Программистам помогло так называемое процедурное
программирование. Все части программы, одинаковые
для разных элементов танка, выделялись в отдельные
подпрограммы (процедуры, функции) и делались стан¬
дартными. Используя одни и те же стандартные блоки,
можно было испытывать на компьютере второй танк,
третий, пятый, в общем — любой. Одним из первых
языков процедурного программирования стал язык
Паскаль (Pascal), а вскоре появился и другой язык, тоже
ориентированный на процедурное программирование, —
Си (С).
Процедурное программирование похоже на строительство дома
из кирпичей. Хотя все кирпичи и одинаковые, из нихможно строить
разные дома — все зависит от того, в каком порядке они уклады¬
ваются.
Объектно-ориентированное программирование
Компьютеры 50-70-х годов стоили безумно дорого. С их
стоимостью заработная плата программиста была совер¬
шенно несопоставима, и это отражалось в программи¬
ровании. В те годы было не очень важно, сколько вре¬
мени программист пишет программу и как долго он ее
отлаживает; главное было — не перегружать компью¬
40
тер. Поэтому в ходе работы над программой компью¬
тер почти не помогал программисту. Каждая минута
работы компьютера стоила больших денег и скрупулез¬
но учитывалась, а время, затраченное программистом,
никто не считал. Он мог неделями писать и отлаживать
программу, на которую сегодня даже начинающий затра¬
тит всего несколько минут.
Все изменилось в середине 80-х годов, когда началась
эпоха персональных компьютеров. Их выпускали мил¬
лионами, для них требовались десятки тысяч программ,
и вдруг выяснилось, что один месяц работы квалифи¬
цированного программиста стоит не меньше, чем его
компьютер. Тогда люди задумались, как сберечь время
программиста, и языки программирования начали
вновь изменяться.
Прежде всего был введен принцип повторного исполь¬
зования кода. По этому принципу то, что было создано
кем-то один раз, должно не пропадать, а накапливаться
и как готовый блок переходить в труды других про¬
граммистов. Такие блоки назвали объектами. При не¬
обходимости разработать новую программу берутся
объекты от предыдущей программы и только перена¬
страиваются (подгоняются) под новые требования.
Наверное, и вам случалось видеть в разных программах
одинаковые по форме окна, похожие меню, одинаковые
кнопки. Большинство программ работают с одними и
теми же шрифтами. Все это настраиваемые объекты.
£сли сравнивать объектно-ориентированное программирование
со строительством дома, то дом строится из готовых комнат, у кото¬
рых на месте меняются свойства. Взяли комнату, поставили, под¬
ключили ее к водопроводу, электричеству и канализации — полу¬
чилась кухня или ванная, fl если вообще не подключать ни к
водопроводу, ни к канализации, а изменить свойства стен и пола
за счет дорогого покрытия, то получится спальная или гостиная.
41
Первым языком объектно-ориентированного програм¬
мирования стал язык Си++ (С++). Затем и у языка Паскаль
появилась объектно-ориентированная версия — Object
Pascal. Сегодня в мире много и других объектно-ориен¬
тированных языков программирования.
Визуальное программирование
В 90-х годах в мире персональных компьютеров опять
произошли важные изменения. Компьютеры приоб¬
рели графическое управление. Раньше ими в основном
управляли с помощью клавиатуры: на ней набирали
команды, с клавиатуры в компьютер вводили тексты
программ. Разумеется, компьютеры и тогда могли воспро¬
изводить на экране графические изображения, но это
было именно умение, а не способ управления. Телеви¬
зор, например, прекрасно воспроизводит изображения,
но способ управления им был и остается кнопочным.
С середины 90-х годов системы управления большин¬
ства компьютеров стали графическими. То, что мы ви¬
дим на экране, — это не просто картинки. Многие из
них служат графическими элементами управления —
на них можно навести указатель мыши, щелкнуть на
ее кнопке — и компьютер ответит на этот управляю¬
щий сигнал.
Разыщите на экране значок под названием Мой компью¬
тер и щелкните на нем дважды левой кнопкой мыши.
Вы увидите, как на экране откроется окно Мой компью¬
тер. То, что мы сделали, это и есть графическое управ¬
ление. Нам ведь не пришлось ничего вводить с клавиа¬
туры, не правда ли?
В следующей главе мы познакомимся поближе с гра¬
фическими элементами управления. Они нам потребу¬
ются, потому что когда мы будем создавать свои про¬
граммы, нам часто придется их использовать.
42
Значок объекта — это элемент управления
При двойном
щелчке на значке
открывается окно
Окно — это контейнер,
в котором собраны
разные элементы
управления: значки
объектов, кнопки,
меню и другие
Мой компьютер
С появлением графических систем управления ком¬
пьютерами изменился и метод разработки программ.
Он тоже стал графическим или, говоря по-научному, —
визуальным. С помощью мыши мы будем выбирать
готовые строительные блоки для будущих программ
(эти блоки называют компонентами), а потом точно так
же с помощью мыши будем настраивать их по своему
вкусу. И лишь в очень редких случаях, когда нам чего-
то будет не хватать, придется ввести несколько слов с
клавиатуры.
— А кто нам даст те компоненты, из которых мы смо¬
жем что-то выбирать?
— Их предоставляют те самые системы визуального
программирования, о которых мы уже говорили. В них
43
входят обширные библиотеки компонентов — только
выбирай. Ну, а если для каких-то целей мы и не найдем
нужного компонента, есть еще возможность устано¬
вить дополнительные библиотеки. Нередко програм¬
мисты, разрабатывающие новые компоненты, выстав¬
ляют их в Интернете, где каждый желающий может
их получить и использовать.
Компьютер без программирования
Глава 4
Из этой главы мы узнаем, какие бъшают
элементы управления в программах, и
познакомимся с тем, как они выглядят
наэкране. Программы, которыемыпотом
будем создавать, тоже будут использо¬
вать похожие элементы управления
О пользе стандартизации
В средние века каждый ремесленник создавал вещи,
не похожие на изделия других мастеров. Каждое от¬
дельное ружье или пистолет были неповторимы. Но
дальнейшее развитие техники потребовало стандарти¬
зации, чтобы детали были взаимозаменяемы. Где бы
ни были произведены гайка и болт, они должны подхо¬
дить друг к другу, если имеют одинаковые размеры.
Компьютерные программы прошли тот же самый путь
всего за несколько десятков лет. Первые программы
были штучными изделиями. Вопросы удобства или по¬
хожести программ во внимание не принимались. Каж¬
дая программа имела своеобразные средства для обще¬
ния с внешним миром, и только ее автор знал, как надо
с ней правильно работать.
Такая ситуация была приемлема, пока программисты
писали программы для себя. Но пришло время, когда
программы стали такими же коммерческими продук¬
тами, как и сами компьютеры. Современный програм¬
мист, как правило, пишет программы не для себя, а для
множества других людей, в том числе и тех, кто не име¬
ет специального компьютерного образования. Если
программист хочет, чтобы его программу купили мил¬
45
лионы простых пользователей, он должен сделать так,
чтобы она была простой в работе и понятной с первого
взгляда. Такие программы называют дружествен¬
ными.
Дружественные программы отличаются тем, что, рабо¬
тая с ними, пользователь может предугадать, что будет,
если он выполнит то или иное действие, например
щелкнет на какой-то кнопке. Простейшая дорога к дру¬
жественному программированию лежит через стандар¬
тизацию. Если бы все программы имели стандартные
элементы управления, то пользователи, освоившие
одну программу, могли бы очень легко осваивать и дру¬
гие. Тогда одного взгляда на экран было бы достаточно,
чтобы понять, что нужно делать, и не тратить многие
часы на изучение толстых инструкций и книг.
Взгляните на соседний рисунок. На нем показаны три
окна из различных прикладных программ: Paint, Microsoft
Word и Microsoft Outlook Express. Paint — это графический
редактор, Word — это текстовый процессор, а Outlook
Express — это клиент электронной почты.
Графическими редакторами называют программы для создания и
обработки рисунков. Текстовые процессоры — это программы для
подготовки печатных документов, а клиенты электронной почты
служат для приема и отправки сообщений по электронной почте.
Назначение у этих программ совершенно разное, но
обратите внимание на то, как много похожих элемен¬
тов управления и оформления использовано в их окнах.
Некоторые пункты меню, такие как Файл, Правка, Вид,
Справка и другие, используются в большинстве про¬
грамм. Нетрудно догадаться, что служат они приблизи¬
тельно для одних и тех же целей.
От стандартизации выигрывают не только пользовате¬
ли, но и программисты. Теперь им не надо заниматься
программированием стандартных элементов оформле-
46
Посмотрите, как много похожих элементов могут
иметь окна самых разных программ
ни я и управления — они могут применять готовые ком¬
поненты, как бы собирая программу из готовых бло¬
ков.
Давайте включим компьютер и посмотрим на то, как
выглядят программы. Чаще всего работающая про¬
грамма отображается на экране компьютера в виде
окна, содержащего данные, с которыми работает поль¬
зователь, а также элементы управления, с помощью
которых эти данные можно создавать, изменять или
настраивать.
47
Окно графического
. редактора Paint
Этот кадр сделан
в текстовом
_процессоре Word
Командные кнопки
Посмотрите на экран компьютера. В левом нижнем
углу вы увидите небольшой прямоугольник с надпи¬
сью Пуск. Этот прямоугольник изображен так,
что кажется немного выпуклым, как кнопка.
А если это кнопка, то на нее хочется нажать.
На обычные кнопки мы нажимаем пальцем. Если мы
попытаемся так же поступить с кнопкой Пуск, то ниче¬
го, кроме жирных пятен на экране монитора, не полу¬
чится. Кнопка — это экранный элемент управления, и
для работы с такими элементами используют мышь.
Посмотрите внимательно: на экране должна рас¬
полагаться маленькая стрелочка — указатель
мыши. Если вы ее не видите, попробуйте подвигать
мышь вправо-влево — заметить движение указателя
обычно достаточно легко.
Аккуратно наведите указатель мыши на кнопку Пуск.
Подождите немного, и вы увидите, как появится над¬
пись Начните работу с нажатия этой кнопки.
Так компьютер подсказывает нам, что экранная кноп¬
ка — это не просто картинка, а элемент управления.
При ее нажатии будет исполнена некая команда. По¬
этому такие элементы управления называются команд¬
ными кнопками.
Как пользоваться мышью
Нам осталось только понять, как нажать на экранную
кнопку. Для этого необходима мышь. У стандартной
мыши две кнопки: левая и правая. Левая кнопка — ос¬
новная, а правая — дополнительная. Щелчок основной
48
кнопкой мыши применяют для работы с элементами
управления, так что если вам предлагается нажать на
экранную кнопку, значит, надо навести на нее указа¬
тель мыши и быстро щелкнуть основной кнопкой.
Кроме обычного используют также двойной щелчок
основной кнопкой. С его помощью запускают програм¬
мы, открывают папки и окна. В общем, если простой
щелчок используют для работы с элементами управле¬
ния, то двойной щелчок используют для работы с объек¬
тами (значками и папками).
Интересен щелчок дополнительной (правой) кнопкой
мыши. Его применяют для доступа к свойствам объек¬
тов. Если на объекте щелкнуть правой кнопкой, то при
этом открывается меню, которое называется контекст¬
ным. У разных объектов может быть разное контекст¬
ное меню — это связано с тем, что разные объекты име¬
ют разные свойства.
8 своей работе мы буделл очень часто пользоваться контекстным
меню. Запомните, что оно открывается щелчком правой кнопки
мыши на объекте.
Меню
Меню — это набор возможных
команд, из которых надо вы¬
брать одну. Попробуйте нажать
на кнопку Пуск — и вы увидите
Главное меню операционной
системы Windows. Попробуй¬
те наводить указатель на раз¬
ные строки этого меню —
строки при этом выделяются
подсвечиванием.
Некоторые пункты помечены
справа черным треугольни-
49
ком. Это значит, что они открывают меню следующего
уровня — так называемые вложенные меню.
Давайте пока что закроем это меню, щелкнув за его
пределами, и проведем еще один эксперимент. Найдите
в правом нижнем углу экрана часы, показывающие
текущее время. Наведите на них указатель мыши и
выполните двойной щелчок, очень быстро дважды под¬
ряд щелкните левой кнопкой мыши. Если все пройдет
удачно, вы увидите на экране новое окно. Если у вас
получатся два отдельных щелчка, ничего не
произойдет и придется попробовать еще раз.
Диалоговые окна
Если у вас получился двойной щелчок на часовом инди¬
каторе, то на экране откроется диалоговое окно настрой¬
ки времени и календаря. Диалоговые окна использу¬
ются для проведения настроек и задания различных
50
параметров программ. Содержимое диалогового окна
составляют разнообразные элементы управления.
Строка заголовка. Давайте познакомимся с элементами
управления поближе. Открывшееся диалоговое окно
имеет заголовок Свойства: Дата и время, который распола¬
гается в строке заголовка окна. Все окна обязательно
имеют строку заголовка.
Раскрывающийся список. Текущий ме¬
сяц можно выбрать в раскрывающемся
списке. У раскрывающегося списка есть
раскрывающая кнопка. Если щелкнуть
на ней, список раскроется, и тогда в нем
можно выбрать нужный месяц при по¬
мощи щелчка. Списки делают раскры¬
вающимися исключительно для ком¬
пактности.
Поле ввода. Рядом с раскрывающимся
списком располагается элемент управле¬
ния для ввода текущего года. Такой эле¬
мент управления называется полем вво¬
да, или текстовым полем.
Кнопки счетчика. Данные в текстовые
поля обычно вводят с помощью клавиа¬
туры, но если эти данные числовые, то
удобно пользоваться кнопками счетчи¬
ка. Щелчок на верхней кнопке увеличи¬
вает значение, а на нижней — уменьша¬
ет его.
Аналогичным образом производится и
настройка времени, но здесь имеется об¬
щий счетчик на несколько полей — ре¬
гулировка производится после того, как
выбраны часы, минуты или секунды.
51
Область предварительного про¬
смотра. Некоторые диалоговые
окна имеют специальную панель,
которая называется областью
предварительного просмотра.
Она помогает наглядно оценить
сделанные настройки. В нашем
примере в области предвари¬
тельного просмотра отобража¬
ются часы со стрелками.
Стандартные командные кнопки. Автор программы
может создать любые командные кнопки, какие поже¬
лает, но есть несколько кнопок, считающихся обще¬
принятыми. Например, кнопка OK служит
для того, чтобы ввести сделанные настрой¬
ки в действие и закрыть диалоговое окно.
Чтобы сохранить настройки, но не закры¬
вать окно, используют кнопку Применить.
А для того чтобы отменить все сделанные
настройки, окно закрывают кнопкой Отмена. Если хо¬
тите, можете немного поэкспериментировать с окном
Свойства: Дата и время, a потом закройте его с помощью
этой кнопки.
Вкладки. В диалоговом окне
может быть так много элемен¬
тов управления, что они в нем
не поместятся. В таких случаях
окно составляют из нескольких
страниц-вкладок. У вкладок
есть корешки. Чтобы открыть
другую страницу диалогового
окна, надо просто щелкнуть на
ее корешке левой кнопкой
мыши.
52
Флажки. Флажки — это эле¬
менты управления, имеющие
два состояния. Они позволяют
пользователю делать нужный
выбор параметров. Если.фла-
жок установлен, то выбор сде¬
лан, а если он сброшен, зна¬
чит, пользователь от данного
параметра отказался. Устанав¬
ливают и сбрасывают флажки
щелчком левой кнопки мыши.
Флажки интересны тем, что
их можно использовать груп¬
пами. Тогда они позволяют
выбрать несколько вариантов
из одного списка. Раскрываю¬
щийся список такой возмож¬
ности не дает.
Переключатели. Переключатели очень похожи на
флажки — они тоже имеют только два состояния:
включено — выключено. Однако, если флажки и могут
существовать поодиночке, переключатели — нет. Один
из переключателей включен всегда. Когда включается
другой переключатель, предыдущий выключается.
Если пользователь не сделает никакого выбора, то бу¬
дут действовать те настройки, которые задал програм¬
мист. Они называются настройками, заданными по
умолчанию.
Приятного аппетита!
Переключатель
включен по
умолчанию
53
Контекстное меню
Забудем на минутку об элементах управления и вер¬
немся к объектам. Если вы работаете в операционной
системе Windows, то перед вами экран, который назы¬
вается Рабочим столом.
На Рабочем столе располагаются значки программ,
документов, папок — все это объекты Windows, вклю¬
чая и сам Рабочий стол. А у каждого объекта есть инди¬
видуальные свойства (объекты различаются по их свой¬
ствам). Названия объектов (программ, папок, файлов
Значки
объектов
Рабочий стол
с фоновым рисунком
Элементы управления
Рабочего стола
Мой компьютер
Рабочий стол Windows. На нем расположены значки объектов
Windows: программ, файлов, папок и других
54
и других) — это одно из их свойств. Значки, которыми
объекты представлены на экране, тоже относятся к
свойствам объектов. Разные программы имеют разные
значки.
Для настройки иЛи измене¬
ния свойств объектов слу¬
жит правая кнопка мыши.
Наведите указатель мыши
на свободное место Рабоче¬
го стола и щелкните правой
кнопкой — перед вами от¬
кроется меню, которое на¬
зывается контекстным.
Оно содержит команды, от¬
носящиеся именно к тому
объекту, на котором произо¬
шел щелчок.
Если мы щелкнули на Рабочем столе, то и команды
полученного контекстного меню относятся именно к
Рабочему столу. Если бы мы щелкнули правой кноп¬
кой мыши на каком-либо значке, контекстное меню
было бы иным.
Выберите в этом меню пункт Свойства — и откроется
диалоговое окно свойств экрана. С его помощью можно
настроить фоновый рисунок, лежащий на Рабочем сто¬
ле, изменить цвет надписей под значками, настроить
экранную заставку, которая включается, когда компью¬
тер простаивает.
Другие элементы управления
Движки. Когда мы перемещаем мышь, на экране пере¬
мещается ее указатель. А знаете ли вы, что скоростью
перемещения указателя можно управлять? Если дать
команду Пуск >• Настройка >■ Панель управления >• Мышь, то
55
откроется диалоговое окно, предназначенное для на¬
стройки параметров мыши.
В этом окне откройте вкладку Перемещение, и на ней
найдете движок, отвечающий за скорость перемеще¬
ния указателя. Если переместить движок влево, ука¬
затель мыши будет двигаться медленнее, а если впра¬
во — быстрее.
Движок перемещают методом перетаскивания при на¬
жатой левой кнопке. Наведите указатель мыши на дви¬
жок, нажмите левую кнопку и, не отпуская ее, пере¬
местите мышь вправо или влево. Вы увидите, как
движок потянется за указателем. Отпустите кнопку
мыши — и движок останется в новом положении.
Полоса прокрутки. Если в окне содержится так много
данных, что они в него не помещаются, в окне образу¬
ются полосы прокрутки. С их помощью можно «про¬
кручивать» содержимое окна.
Приемы использования полосы прокрутки (она может
быть вертикальной, как в данном случае, или горизон¬
тальной) стандартны. Чтобы незначительно перемес¬
тить содержимое окна, надо щелкнуть на кнопке полосы
прокрутки. Прокрутка пойдет быстрее, если щелкнуть
на полосе прокрутки рядом с ползунком. Наконец, са¬
мый быстрый (но наименее точный) способ прокрутки
состоит в перетаскивании ползунка.
56
Движок перемещают
методом
перетаскивания
Кнопка
прокрутки вверх
Ползунок
Полоса
прокрутки
Кнопка
прокрутки вниз
Надпись. Это самый-самый простой элемент управле¬
ния, встречающийся повсеместно. Это обычное тексто¬
вое сообщение, которое пользователь может прочитать,
но не может изменить. Его даже трудно назвать эле¬
ментом управления, потому что надпись ничем не управ¬
ляет — щелкать на ней кнопками мыши бесполезно.
Тем не менее, она тоже относится к элементам управ¬
ления, потому что в зависимости от того, что в ней ска¬
зано, действия пользователя могут быть различными.
В общем, сама надпись ничем не управляет, но помо¬
гает пользователю управлять программой.
Надпись
Надпись
57
Надпись
R теперь то же самое, но по-английски...
Когда мы начнем создавать свои программы, нам при¬
дется наполнять их окна элементами управления. Во
всех системах визуального программирования назва¬
ния элементов управления записываются английскими
словами. Английскими буквами записываются и назва¬
ния свойств элементов управления. Нам предстоит их
настраивать, а для этого желательно знать, как все это
выглядит по-английски. Вам поможет следующая не¬
большая таблица.
Элемент управления
Control
Кнопка
Button, Command Button
Меню
Menu
Раскрывающийся список
Combo Box
Диалоговое окно
Dialog box или просто Dialog
Полоса прокрутки
Scrollbar
Флажок
Checkbox
Переключатель
RadioButton, Option Button
Корешок вкладки
Tab
Текстовое поле
Edit box (или просто Edit), Text box
Надпись
Label
f?lQBQ 5
Объекты и их свойства
Изэтойглавымыузнаем, чтосовремен-
ные программы создают из заранее за-
готовленньис объектов, а потом их на¬
страивают, Это облегчает работу
программиста и помогает избегать
ошибок. Мы узнаем, что все программ¬
ные объекты обладают свойствами и
способны реагировать па события
Что такое объекты
В газетах время от времени пишут, что в каком-то го¬
роде видели НЛО — неопознанный летающий объект.
«Мигая разноцветными огнями и противно жужжа,
НЛО удалился за горизонт». В название входит слово
объект, так давайте подумаем, о чем же тут идет речь.
Ну, неопознанный — это значит, что никто так и не
знает, что это на самом деле. Летающий — это значит,
что НЛО видели в воздухе. А вот что же такое объект?
Так как про НЛО мы почти ничего не знаем, можно по¬
пытаться выяснить, что
такое объект вообще.
Во-первых, объект — это
нечто конкретное. Мы мо¬
жем сказать, где он начи¬
нается и где кончается,
что относится к объекту,
а что нет. Во-вторых, он
обладает некоторым вну¬
тренним устройством,
которое мы можем и не
59
знать. В-третьих, объект характеризуется некоторым
поведением, за которым можно наблюдать, а иногда и
влиять на него.
Согласитесь, что такое описание не выглядит ни очень
понятным, ни очень полезным, однако в жизни нас окру¬
жает множество самых разных вещей, которые ему со¬
ответствуют. Все эти вещи нам привычны, и мы пользу¬
емся ими, не задумываясь. Например, утром нас будит
будильник. Чтобы остановить противный звонок, надо
хлопнуть по кнопке, а чтобы на следующее утро снова
его услышать — повернуть ручку завода звонка.
Скажем так: чтобы завести объект «будильник», надо
повернуть объект «ручка». Чтобы выключить объект
«будильник», надо нажать объект «кнопка». Объект¬
ный подход полезен тем, что объекты можно рассмат¬
ривать как целые, законченные вещи, не вдаваясь в то,
как они устроены. Чтобы пользоваться будильником,
не надо знать, из каких колесиков и пружинок он со¬
стоит и как они связаны друг с другом. Если мы знаем,
как заводить будильник, как узнавать по нему время,
как управлять звонком, то мы знаем о будильнике все,
что о нем стоит знать пользователю.
Оглянитесь вокруг. Нас окружают тысячи объектов, про
которые мы не знаем, как они устроены, но это нам вовсе
не мешает жить и учиться. Вот телевизор. Мы знаем, как
его включить, или выключить, или переключить на дру¬
гую программу. Вот холодильник. Мы знаем, что внутри
у него всегда холодно, так что продукты там не портятся.
Вот продукты, которые лежат в холодильнике. Мы зна¬
ем, что их можно есть, хотя некоторые надо сначала при¬
готовить. А вот наши друзья. Мы знаем, что один из них
любит футбол, другой любит рисовать, а третий готов дни
и ночи просиживать в кино. Почему они такие разные,
мы не знаем, но это не мешает нам с ними общаться.
60
Свойства объектов
Слово «объект» — очень удобно, чтобы кратко выра¬
зить то, с чем мы часто встречаемся в жизни, не вни¬
кая во внутреннее устройство и принцип действия, но
это совсем не значит, что нас эти объекты вообще не
интересуют. Наоборот, очень даже интересуют. Если,
например,надозабитьобъект «гвоздь» вобъект «стена»,
мы непременно должны оценить свойства этих объек¬
тов. Если стена деревянная, то гвоздь должен быть
железным, и никак не наоборот.
Все объекты обладают свойствами, причем не какими
попало, а различимыми. Если мы не различаем свой¬
ства насекОмых, летающих над нами, то это не объекты,
а просто насекомые. Если же мы различаем их свой¬
ства, то понимаем, что перед нами муха, комар, бабочка
или пчела, и в каждом случае ведем себя по-разному.
Свойства объектов неразрывно связаны с самими объек¬
тами. Не может быть свойства без объекта, как не бы¬
вает улыбки без лица.
Давайте попробуем дать насекомым несколько оценок,
исходя из их свойств. Оценивать их мы будем не так,
как в школе (от единицы до пятерки), а так, чтобы
оценка выражалась одним байтом (от 0 до 255).
Свойство
Наша оценка Ваша оценка
Бабочка->Укус
“0
Муха->Укус
~20
Комар->Укус
~60
Пчела->Укус
255
Бабочка->Раскраска
255
Муха->Раскраска
~20
Комар->Раскраска
^0
Пчела->Раскраска
60
61
А теперь попробуйте догадаться, какой объект описан
следующими свойствами:
Насекомое->Объект->Полет =
false;
Насекомое->Объект->Звук: =
true;
Насекомое->Объект->Цвет =
Green;
Насекомое->Объект->Прыжок
= 255;
Значения false и true — это так называемые логические
значения. У них может быть только два состояния: ДА
(true — Истина) и НЕТ (false — Ложь). В жизни мы очень час¬
то встречаемся с объектами или явлениями, свойства
которых можно выразить двумя значениями, напри¬
мер: Ночь — День, Горячо — Холодно, Мальчик —
Девочка, Черное — Белое.
Такие значения нет смысла выражать числами, чтобы
не тратить зря на их запоминание компьютерную па¬
мять и не нагружать процессор излишними расчетами.
Впрочем, компиляторы некоторых языков программи¬
рования это допускают.
Отметим также, что по-английски Green — Зеленый.
Объектно-ориентированный nogxog
Какое отношение все это имеет к программированию?
Дело в том, что мы будем использовать объекты в наших
программах. Программные объекты похожи на объек¬
ты реальной жизни — они также различаются внутрен¬
ним устройством и поведением, то есть свойствами.
Можем ли мы увидеть программный объект еще до
того, как начали сами программировать? Да, и мы это
уже сделали. Сегодня практически все, что можно уви¬
деть на экране компьютера, реализовано в программах
как объекты. Любое окно на экране — это объект.
62
Любой значок — это объект. Любой элемент управле¬
ния — тоже объект. Любое меню — и это объект.
Давайте вернемся впрошлое, лет эдак на 25 назад. До¬
пустим, пришел молодой человек в компанию, зани¬
мающуюся выпуском компьютерных игр, и ему пору¬
чили создать в новой игре всего-навсего одну кнопку.
Сначала три дня он писал код, который будет рисовать
кнопку на экране. Потом два дня он писал код, кото¬
рый должен выполняться, когда кнопка будет нажата.
Потратив неделю, он сдал свою работу главному про¬
граммисту, и тот сказал:
— Все это, конечно, очень хорошо, но в нашей программе
будет еще десять тысяч разных кнопок, появляющихся
в самых неожиданных местах. Давайте подумаем, как
быть с ними?
Подумал наш программист и оформил все, что он напи¬
сал, в виде одной подпрограммы (процедуры), чтобы
все другие программисты, создающие игру, могли ее
вызвать, когда им понадобится какая-нибудь кнопка.
И тут выяснилось, что другим программистам нужны
кнопки не одинаковые, а разные: разных размеров и
цветов, с разными надписями и разными действиями.
Еще раз подумал наш программист и сообразил, что
если все это вводить в качестве параметров при вызове
его процедуры, то каждый коллега сможет использо¬
вать его кнопку, как пожелает. Обрадовался он, но, как
оказалось, преждевременно. Пришлось ему долго и
терпеливо общаться с каждым коллегой. Одному тре¬
бовалось, чтобы кнопка при нажатии издавала звук;
другому нужно было, чтобы на ней был рисунок, да не
любой, а специальный. Как только с одним догово¬
ришься, у другого начинаются проблемы. Год ушел на
преодоление трудностей и стыковку работ, сделанных
разными программистами, а тут как раз фирма присту¬
63
пила к новой компьютерной игре, в которой, по замыслу
дизайнера, кнопки должны быть не прямоугольными,
а круглыми, и все пришлось начинать сначала. Понял
наш программист, что не программированием он зани¬
мается, а бесконечными спорами да согласованиями,
и решил уйти в другую фирму.
Но и там оказалось не лучше, а еще хуже. Там свои про¬
граммисты за годы работы уже понаделали множество
самых разнообразных процедур, которые тоже ни с чем
не стыкуются. Попробовал он с ними разобраться да
рукой махнул — начал все заново.
Так обстояло дело во времена процедурного програм¬
мирования. Успешные проекты тех лет имели размер
до нескольких тысяч строк кода, а коллективы разра¬
ботчиков, создававшие их, обычно составляли от двух
до пяти человек. При попытке увеличить размер про¬
граммы до десятков тысяч строк разрастался размер
рабочего коллектива и возникал такой клубок проти¬
воречий, который проще бросить, чем распутывать.
Создание языков объектно-ориентированного програм¬
мирования позволило решить множество проблем.
Программный объект, например такой, как кнопка,
хранится в основной программе в виде набора стандарт¬
ных процедур. Если надо изменить надпись на кнопке
или размер кнопки, то никакие процедуры не надо ме¬
нять — надо только изменить свойства объекта, и все
необходимые изменения в процедурах произойдут авто¬
матически — за это отвечает система объектно-ориен¬
тированного программирования.
Свойство и методы.объектов
В обычной жизни мы используем множество разнооб¬
разных объектов, не зная, как они устроены. Более
того, куда не следует, лучше и вообще не лазить: поко¬
64
вырявшись отверткой в телевизоре, можно полностью
вывести его из строя.
Но мы можем воздействовать на свойства объектов.
Возьмем, к примеру, телевизор. Главное свойство теле¬
визора — способность воспроизводить на экране теле¬
передачи. Сведения о том, какую программу показы¬
вать, хранятся внутри телевизора. Мы не знаем и не
хотим знать, как именно. Нам требуется лишь удобная
возможность переключить телепрограмму, когда нас
не устраивает ее содержание.
Средства для этого могут быть разными. У обычного
домашнего телевизора переключение программ может
производиться кнопками на лицевой панели или с по¬
мощью пульта дистанционного управления. В конце
концов, если нас никакая телепрограмма не устраивает,
можно подключить видеомагнитофон и еще раз про¬
смотреть программу, записанную на прошлой неделе,
но для подключения видеомагнитофона телеприемник
должен обладать специальными свойствами, например
специальным разъемом и кабелем.
Программные объекты также имеют свойства — это те
их параметры, которые мы можем выбирать при созда¬
нии программы и менять при ее работе. Прочее устрой¬
ство объекта для нас закрыто, и у нас просто нет возмож¬
ности сделать что-то не то. Для проверки и изменения
свойств объекта используются специальные процедуры —
методы, также входящие в состав объекта. Можно ска¬
зать, что, для того чтобы что-то сделать со свойствами
объекта, надо попросить у самого объекта, чтобы он их
нам сообщил или изменил.
Число свойств у телевизора относительно невелико.
Объекты в программах могут иметь намного больше
свойств. Например, командная кнопка в окне имеет
размер, положение, цвет, текст подписи и другие свой-
3—1619
65
ства. Мы можем работать только с теми свойствами,
которые нам нужны. Для прочих свойств можно один
раз задать необходимые значения при создании про¬
граммы, после чего забыть о них.
Объект, который использовался в одной программе,
можно использовать в другой программе. При этом:
♦ добавление объекта не нарушает работу всех
прочих объектов, имеющихся в программе;
♦ поведение вставляемого объекта не изменяется
из-за наличия других объектов.
Допустим, программист написал программу, описыва¬
ющую танковое сражение. В одном сражении может
принимать участие несколько десятков танков. Не¬
смотря на то что поведение всех танков описывает одна
и та же процедура, каждый танк является самостоя¬
тельным объектом, и программа управляет им инди¬
видуально. Каждый из этих объектов имеет следующие
свойства.
Свойства
Различия
Изображение
Одинаково для всех
Бортовой номер
Различен для всех
Фамилия командира
Различна для всех
Техническое состояние
В начале игры одинаково для
всех, далее различается
Боекомплект
В начале игры одинаков для
всех, далее различается
Положение на экране
Различно для всех
Звук выстрела
Одинаков для всех
Звук при движении
Одинаков для всех
Теперь допустим, что программист решил ввести в игру
кроме танков еще и артиллерийские орудия и броне¬
транспортеры. Значит ли это, что для новых классов
66
объектов он должен заново переписывать все процеду¬
ры? Вовсе нет: достаточно только поменять некоторые
свойства, например так.
Свойство
Танк
Орудие
Бронетранспортер
Стоимость
240
35
45 *
Скорость
~40
^^0
60
Броня
120
^~0
50
Скорострел ьность
~1
^^2
24
Бронебойность
160
180
25
Изображение
tank.bmp
art.bmp
btr.bmp * *
Звук выстрела
tank.wav
art.wav
btr.wav * *
* Во многих игровых программах учитывается стоимость боевых
единиц в неких условных единицах, чтобы игрок не увлекался составле¬
нием своих частей из самых лучших образцов техники.
* * Здесь в качестве свойства выступает имя файла, в котором хранится
заранее заготовленный рисунок или записанный звук.
Программирование без труда
Если мы хотим работать с объектами, это не означает,
что все объекты мы должны создавать самостоятельно.
В разных областях человеческой деятельности разде¬
ление труда используется уже давно.
Например, вряд ли строители должны знать, кто и как
изготовляет для них кирпичи. Их дело — получить го¬
товые строительные материалы и сложить из них дом.
А иногда дом вообще собирают из готовых панелей —
то-то он растет, как на дрожжах.
Аналогичный подход применяют и те, кто хочет выра¬
стить, например, яблоневый сад. Все знают, что яблоня
вырастает из семечка. Но кто и когда сажал в саду ябло¬
невые семечки? Чтобы выросла яблоня, сажают трех¬
летний саженец, взятый в специальном питомнике.
Для садовода саженец — это объект, поскольку он обла¬
67
дает различимым свойством, которое называется сор¬
том яблони. Семечко для него не объект, поскольку
никакими различимыми свойствами с точки зрения садо¬
вода не обладает. Из семечка можно вырастить только
дикую яблоню. Чтобы яблоня стала сортовой, необхо¬
димо сделать специальную операцию, которая называ¬
ется прививкой, чем и занимаются в питомниках.
Строительные кирпичи все одинаковы, а построить с
их помощью можно все что угодно. Если бы мы начи¬
нали строительство с готовых квартирных блоков, то
смогли бы строить только жилые дома, но не театры
или стадионы. Поэтому и в программировании мы дол¬
жны начинать «строительство» своей программы с таких
объектов, которые не ограничат наши возможности, но
и не заставят нас каждый раз начинать все с самого
начала.
На такую роль отлично подходят экранные элементы
управления. Сами они и способы работы с ними стан¬
дартны, а действия, которые они выполняют, — раз¬
ные. Мы будем знакомиться с программированием на
основе системы С++ Builder, предоставляющей пользова¬
телю большой набор заготовок для будущих объектов.
Их называют компонентами. Что бы мы ни захотели
сделать, нам всегда удастся подобрать подходящие
компоненты.
Кстати, вы, наверное, замечали, что все «полезные»
программы выглядят примерно одинаково, даже если
их назначение разное. Так происходит как раз потому,
что их «строительство» ведется из одинаковых компо¬
нентов. Такие программы работают надежно на любых
компьютерах и редко вызывают проблемы.
Напротив, компьютерные игры нередко очень сильно
отличаются друг от друга по внешнему виду и спосо¬
бам управления. Это происходит потому, что авторы
68
таких программ всегда стремятся нас чем-нибудь уди¬
вить. Можно сказать, что компьютерные игры «выра¬
щиваются из семечка». Поэтому не каждая игра отра¬
батывает от начала и до конца без сучка и задоринки,
да и при установке многих игр тоже приходится как
следует попотеть.
События и их обработка
Мы пока что упустили из виду еще один важный факт.
Говоря о будильнике, мы не обратили внимания на то,
что он срабатывает без нашего вмешательства — мы в
это время спим. Это происходит из-за того, что насту¬
пает то время, на которое будильник был настроен. Для
будильника этот факт можно рассматривать как собы¬
тие. Способность реагировать на определенные собы¬
тия — это разновидность свойства. При возникновении
события производится его обработка, для будильни¬
ка это — включение сигнала.
Объекты, используемые в компьютерных программах,
также могут реагировать на события. При возникно¬
вении события происходит автоматический запуск спе¬
циального метода — обработчика данного события.
Как правило, разные события вызывают разную реак¬
цию, хотя вполне допустимо, чтобы некоторым собы¬
тиям соответствовал один и тот же обработчик. Таким
образом, в состав программных объектов еще входят
подпрограммы-методы, обрабатывающие события.
С помощью событий обеспечивается взаимодействие
программы с пользователем. Когда мы перемещаем
мышь или нажимаем клавишу, это регистрируется эле¬
ментом управления как событие, а необходимые дей¬
ствия выполняются в методе-обработчике. События,
связанные с действиями пользователя, называются
пользовательскими.
69
Кроме пользовательских событий в программах проис¬
ходят так называемые программные события. Напри¬
мер, когда танк наезжает на мину, срабатывает метод —
обработчик объекта «мина». По этой подпрограмме ми¬
на изменяет свое состояние (перестает существовать), а
объект танк получает событие «подрыв». В результате
срабатывает метод — обработчик объекта «танк». Эта
подпрограмма рассчитывает величину повреждения и
может привести к уничтожению объекта «танк» в про¬
грамме, а может только обездвижить его — тогда свой¬
ство «скорость» объекта «танк» получит значение 0.
8ы чувствуете, что s то время, кок но экране разворачиваются
грандиозные сражения, в программах происходят простейшие
операции: запускаются процедуры-обработчики событий, изме¬
няются свойства объектов, возникают новые события, запускают¬
ся новые обработчики и так далее?
Для таких событий, как выстрел из пушки, подрыв
танка, встреча снаряда с преградой, методы обработки
записывает сам программист — это требует ручной
работы. То же относится и к таким типовым объектам,
как командная кнопка. Для нее основное событие —
это щелчок на ней. Как мы знаем, последствия такого
щелчка могут быть любыми — все зависит от самой
кнопки. Для того чтобы задать реакцию на щелчок,
программисту тоже приходится создавать обработчик
щелчка на кнопке.
Событийный механизм управления программой
Мы только что познакомились с совершенно новым
механизмом выполнения программы — событийным.
Давайте вспомним, как работал наш автоматический
танк, действия которого мы программировали на гипо¬
тетическом языке программирования Атака.
Он по очереди брал команды, имеющиеся в программе,
и «бездумно» их выполнял. Знал ли танк, что перед ним
70
препятствие, которое надо объехать? Нет, не знал —
он, на самом деле, был и слеп, и глух. Зато это знал про¬
граммист, который заранее проследил за тем, чтобы
танк не наехал на мину и не попал под выстрел пушки.
А как сделать, чтобы танк стал «умным», чтобы он мог
видеть и слышать то, что происходит вокруг, чтобы при
звуке выстрела из вражеского орудия он разворачи¬
вался к немуибрал его на прицел? Увы, для этого наш
язык программирования Атака не годится. В нем нет
ничего для настройки свойств танка. Раз нет свойств,
значит, нет и событий, потому что для каждого объекта
возможные события — это разновидность свойств. Раз
нет событий, значит, нет и реакции на них. Программа,
написанная на языке Атака, проведет танк по полю
именно так, как задал программист, и выключится по
окончании работы.
Современные программы так не работают. Возьмите
для примера операционную систему Windows — это
ведь тоже программа. Она запускается сразу после
включения компьютера и уже не прекращает свою
работу, пока он работает. Где-то в глубине Windows
постоянно работает процесс, проверяющий, что проис¬
ходит с экранными объектами и элементами управле¬
ния. В итоге система всегда готова отреагировать на
наше перемещение мыши, нажатие ее кнопок или кла¬
виш клавиатуры.
Какие бывают события
Событийный механизм управления, присущий системе
Windows, передается и всем программам, работающим
в этой системе. Чтобы программа могла выполнять то,
что хочет ее пользователь, а не только то, что задумал
ее создатель, она должна постоянно проверять состоя¬
ние своих элементов управления.
71
Конкретные события соответствуют типичным при¬
емам управления. Например, кнопки, флажки и пере¬
ключатели могут реагировать на щелчок мыши. Про¬
граммы способны также отличить одинарный щелчок
от двойного и используют в этих случаях разные обра¬
ботчики.
Помните, как при наведении указателя мыши на кнопку
Пуск появилась всплывающая подсказка? Наведение
указателя тоже может рассматриваться как событие,
на которое объект способен реагировать. Для сложных
элементов управления иногда требуется отдельно обра¬
батывать нажатие и отпускание кнопки мыши. При
этом также проверяется, какая именно кнопка нажата
или отпущена и не были ли при этом нажаты клавиши
SHIFT, CTRL или ALT, способные изменить способ выпол¬
нения операции.
Повторим гловное
Итак, при объектно-ориентированном программирова¬
нии данные и процедуры для их обработки, которые
называют методами, объединяют вместе и называют
объектами. Изменяют эти данные путем настройки
или изменения свойств объекта.
Записывать программу в виде набора последовательно
выполняемых команд в этом случае неудобно. Вместо
этого используют событийный механизм управления,
при котором процедуры-обработчики вызываются при
возникновении событий. Все действия пользователя
обычно рассматриваются как события, связанные с
конкретными элементами управления. Это и позволяет
нам управлять компьютерными программами.
72
C++ Builder: первое знакомство
Глава 6
В этой главе мы познакомимся со средой
визуальной разработки С++ Builder, ко¬
торая станет нашим Основным рабочим
инструментом для создания программ
Запуск системы С++ Builder
Наверняка вам уже хочется перейти от теории к прак¬
тике и приступить к написанию программ. В принци¬
пе, мы уже почти к этому готовы. Но для создания про¬
грамм нам понадобятся инструменты. Еще не так давно
для этой цели программисту служили карандаш и бу¬
мага. А сегодня инструментом создания программ для
компьютера служит тот же самый компьютер, на ко¬
тором эти программы потом будут исполняться.
Для работы нам потребуется не только компьютер, но
и специальная программа. Программы, которые позво¬
ляют создавать другие программы путем сборки из от¬
дельных компонентов, называют средами визуальной
разработки. Среда визуальной разработки берет на
себя следующие хлопоты и заботы:
♦ поставляет нам кирпичики-компонентыв, из
которых «собираются» готовые программы;
♦ дает возможность записывать необходимые
операторы языка программирования;
♦ помогает находить ошибки и исправлять их;
♦ позволяет оформлять готовые программы так,
чтобы их можно было запускать на любом ком¬
пьютере, а не только на том, где их создали.
73
Благодаря заботливости, которую проявляет система
программирования, труд автора программы становится
более творческим, а производительность и эффектив¬
ность работы повышаются.
Вы уже знаете, что в мире существует несколько раз¬
личных сред визуальной разработки: Visual Basic, Delphi,
Borland С++ Builder и другие. Каждая рассчитана на свой
язык программирования, от которого зависят правила
записи программ. Готовая программа состоит из опе¬
раторов этого языка, причем часть этих операторов
автор записывает вручную, а часть операторов система
программирования подставляет автоматически.
Мы будем использовать одну из самых мощных систем
визуальной разработки программ — среду С++ Builder.
Язык программирования, который использован в ней,
называется С++ (Си++).
Мы надеемся, что вы уже установили эту систему про¬
граммирования на своем компьютере. Возможно, для
этого вам потребовалась помощь старших. Если на Ра¬
бочем столе Windows был создан значок для
запуска системы, щелкните на нем двойным
щелчком. Если такого значка нет, разыщите
команду запуска в Главном меню: Пуск ► Про¬
граммы >- Borland С++ Builder >• С++ Builder.
Что мы видим но экране
Основное окно системы. После запуска системы откры¬
вается ее основное окно, с которым мы и будем рабо¬
тать дальше. Честно говоря, оно выглядит немножко
устрашающе. От изобилия элементов управления ря¬
бит в глазах, да на рисунке мы видим к тому же не все,
что* есть.
О таких программах говорят, что они имеют сложный
интерфейс. Пожалуйста, не пугайтесь, разбираться с
74
Инспектор
объектов
системой мы будем постепенно: не все нам понадобится
сразу. А начнем мы свое путешествие в мир програм¬
мирования с того, что узнаем, как что называется.
И действительно, если приглядеться повнимательнее,
можно увидеть, что у нас на экране не одно, а целых
четыре окна, в общем-то независимых друг от друга.
Мы можем перемещать каждое из этих окон по экрану
независимо от всех остальных.
Главное окно. Главное управляющее окно системы С++
Builder обычно располагается в верхней части экрана.
Оно содержит средства управления созданием програм¬
75
Строка
.заголовка
Строка
_ меню
Панели
_ инструментов
Палитра
_ компонентов
\
■ Окно формы
Окно
кода
Главное окно системы С++ Builder
мы и выглядит наиболее загруженным из всех окон.
Но обращаться к нему нам придется довольно часто.
Давайте посмотрим, что в нем есть.
Строка заголовка. У каждого окна всегда есть строка
заголовка. В строке заголовка окна системы записано
имя открытого в нем проекта — Project1.
Строка меню. Под строкой заголовка окна системы рас¬
полагается строка меню, содержащая команды системы
С++ Builder. Строка меню — это очень важный элемент
управления любой программы или системы. Кроме
строки меню программы обычно содержат и другие эле¬
менты управления: командные кнопки, кнопки инст¬
рументов и прочие. Кнопками удобно пользоваться для
исполнения операций, но не для всякой операции есть
кнопка, а только для тех, которые встречаются особен¬
но часто. Зато с помощью строки меню можно найти
любую команду, на которую система способна.
Панели инструментов. Под строкой меню располага¬
ются панели инструментов с кнопками. Во многих про¬
граммах имеется только одна такая панель, но в сис¬
теме С++ Builder их несколько. Кнопки панелей
инструментов обеспечивают доступ к наиболее часто
встречающимся командам. Чтобы узнать, как называ¬
ется та или иная кнопка, надо навести на нее указатель
мыши и подождать, пока рядом с ним появится всплы¬
вающая подсказка. На нашем рисунке для примера по¬
казана кнопка со значком дискеты. Она
позволяет сохранить открытый проект в
виде файла на жестком диске и потому
дает всплывающую подсказку Save (Со¬
хранить).
Палитра компонентов. Одна из панелей инструментов,
имеющихся в главном окне системы С++ Builder, замет¬
но отличается от остальных. Это палитра компонентов.
76
На ней собраны кнопки, соотвётствующие компонен¬
там окна программы — тем самым «кирпичикам», из
которых мы и будем свои программы собирать. Ком¬
поненты — ото заготовки будущих элементов управле¬
ния.
Если навести указатель мыши на кнопку компонента,
то рядом появляется всплывающая подсказка с его на¬
званием. В нашем примере выбран компонент Label (Над¬
пись). Он нам пригодится всякий раз, когда надо будет
что-то написать в окнах наших будущих программ.
ponerti Patette
Палитра компонентов
Обратите внимание на то, что палитра компонентов
содержит множество вкладок. Каждая из них предо¬
ставляет свой набор компонентов. Общее число компо-
нетов достигает нескольких сотен. В этой книге мы
будем использовать в своих программах лишь некото¬
рые из них.
Инспектор объектов. Перейдем к рассмотрению других
окон. Слева первоначально располагается очень полез¬
ное окно Object Inspector (Инспектор объектов). Мы будем
пользоваться им очень часто. Именно с его помощью
настраивают объекты, используемые в программах.
Когда у нас начнут появляться первые объекты, мы
сможем выбирать их щелчком мыши, и тогда в этом
77
. Вкладки с наборами
компонентов
. Текущий набор
компонентов
Компонент
Label (Надпись)
Кнопки прокрутки
вкладок
Текущий
.объект
Настройка
событий
Окно Инспектора объектов служит для настройки
объектов программы
окне мы увидим их свойства и сможем их изменить по
своему усмотрению.
Это же окно используется для выбора и настройки со¬
бытий, на которые будут реагировать объекты нашей
программы. С его помощью мы будем создавать или вы¬
бирать нужные процедуры обработки.
Окно формы. Форма — это заготовка окна будущей про¬
граммы. Каждая программа содержит хотя бы одно
окно и, следовательно, одну форму. Поэтому при работе
над программой окно формы мы видим на экране все¬
гда.
Заготовка первого окна называется Form 1. Если в про-
граме будет два окна, то заготовка второго будет назы¬
ваться Form2 и так далее. Конечно, у нас будет возмож¬
ность заменить стандартное название более подходящим
для нашей программы.
Сейчас окно формы пустое, но, создавая программу, мы
добавим в него нужные элементы управления. Сетка
из точек в окне формы поможет разместить объекты
78
Настройка
свойств
Настраиваемое
свойство /
ровно и аккуратно. При работе программы эта сетка
видна не будет.
Окно формы служит для размещения элементов управления
будущей программы
Окно кода. Последнее из открытых окон содержит код
нашей программы.
— Простите, но ведь никакой программы у нас еще нет,
а в окне кода что-то уже есть?
— Да, это действительно так. Большую часть нашей
программы система С++ Builder формирует автоматичес¬
ки. И это не преувеличение. Даже «пустая» программа
для Windows собирается из нескольких тысяч опера¬
торов языка С++.
Чтобы в программе
было легче разобраться,
ключевые слова языка
С++ выделяются в окне
кода полужирным
шрифтом.
Окно кода содержит текст программы
79
Тем не менее, когда нам придется добавлять операто¬
ры в свою программу, мы будем делать это именно в
этом окне. Некоторые операторы система С++ Builder
добавит (по нашей просьбе) сама, другие мы введем
вручную. Все программы в этой книге довольно про¬
стые, так что ручного труда потребуется немного.
Добавлять нужные операторы система С++ Builder начи¬
нает еще до того, как мы приступим к созданию своей
программы. Именно поэтому окно кода никогда не
бывает пустым. К сожалению, это помешает нам в даль¬
нейшем увидеть полные тексты своих программ. Мы
будем приводить только фрагменты, связанные с опе¬
раторами, написанными собственноручно.
Простейшая программа
В этой главе мы создадим свою первую
программу. Система C4-4- Builder помо¬
жет собрать ее из готовых «кубиков>>,
которые называются компонентами
Начнем с конца
Самая простая программа — это программа, которая
ничего не делает. Ее можно только открыть и закрыть.
Чтобы было, что открывать, программа должна иметь
окно, а чтобы было, чем его за¬
крыть, надо сделать закрываю¬
щую кнопку. Еще мы сделаем в
окне программы какую-нибудь
надпись. Должно получиться
примерно так, как показано на
рисунке.
Как хранится разрабатываемая программа
Для системы С++ Builder каждая незавершенная про¬
грамма — это проект. Система знает обо всех файлах
проекта и записывает их в заданную папку. Проект
всегда включает множество файлов. Их большая часть
создается системой автоматически. Для нас значение
имеют три файла: файл формы, файл кода и файл про¬
екта. Об остальных файлах можно не задумываться —
система сама следит за порядком.
Познакомьтесь с формой
Форма — это заготовка окна будущей программы. Мы
имеем дело с формой, пока занимаемся программиро-
Вот что должно у нас
получиться
81
ванием. Когда программа будет готова и мы ее запус¬
тим, форма превратится в полноценное окно.
У любой программы должно быть хотя бы одно окно и,
значит, форма. Сейчас мы создадим форму для нашей
первой программы.
Запустите систему С++ Builder. Она автоматически со¬
здаст форму — заготовку окна для новой программы, а
также подготовит начальный фрагмент кода, общий
для всех создаваемых программ. На форме нет ни од¬
ного элемента управления, а фрагмент кода не содер¬
жит ни единого действия, которое могла бы выполнить
наша программа.
Познакомьтесь с компонентами
Итак, пока что форма у нас пуста. Таким же пустым
будет и окно программы, созданной из нее. Согласи¬
тесь, что это нехорошо. Обычно окна программ пустыми
не бывают — в них содержатся элементы управления:
разные кнопки, надписи, панели и другие полезные
вещи. Система С++ Builder позволяет создавать их из за¬
готовок, которые называются компонентами.
Пустая форма и первоначальный вид окна кода
82
Окно
кода
Окно
формы
Компоненты лучше всего представлять как детали дет¬
ского конструктора. Есть такой компонент гайка, и
есть еще компонент винт. Если для сборки игрушки
потребуется двадцать винтов и гаек, значит, надо взять
20 экземпляров компонента гайка и столько же экзем¬
пляров компонента винт.
В детском конструкторе детали разложены по отделе¬
ниям в ящичке. В системе С++ Builder для доступа к ком¬
понентам есть специальная панель инструментов
Component Palette (Палитра компонентов). Она состоит из
множества вкладок, содержащих разные категории
компонентов, от самых простых до очень сложных.
Самые простые компоненты, с помощью которых со¬
здают стандартные элементы управления системы
Windows, представлены на вкладке Standard (Стандарт¬
ные). Каждый компонент представлен значком. Выби¬
рают компоненты с помощью мыши — надо один раз
щелкнуть на значке компонента. Если дважды щелк¬
нуть на значке, компонент не только автоматически
выбирается, но и добавляется в середину формы.
Палитра компонентов в системе С++ Builder
Для программы, которую мы задумали, необходимо
всего два компонента: Label (Надпись) и Button (Кнопка).
С прочими компонентами мы познакомимся позже,
когда они потребуются.
83
Компонент
Надпись (Label)
Компонент
Кнопка (Button)
Стандартные
компоненты
Создаем объект Надпись
Надпись содержит текст, который можно прочесть во
время работы программы. Ее можно создать с помощью
компонента Label (Надпись). Выберите этот компо¬
нент щелчком на значке в палитре компонентов.
Наведите указатель мыши на форму и нажмите левую
кнопку. Не отпуская ее, переместите мышь вправо-
вниз — за указателем потянется серая рамка. Внутри
этой рамки и будет создан объект Надпись. Запомните
этот прием работы с мышью. Он называется протяги¬
ванием и используется при создании на форме любых
объектов.
Создание объекта протягиванием мыши
Объект, помещенный на форму, отмечен квадратными
маркерами по углам и сторонам. С помощью этих мар¬
керов можно изменять размеры объектов. Наведите
указатель мыши на маркер, зацепите его нажатием ле¬
вой кнопки и перетащите в новое место, не отпуская
84
Указатель мыши при
создании объекта
Эта всплывающая
подсказка помогает
контролировать размер
создаваемого объекта
кнопку. После отпускания кнопки мыши размеры
объекта изменяются.
Объект можно переместить в другое место формы. Для
этого поместите указатель мыши внутри объекта и на¬
жмите кнопку. Переместите указатель, и вместе с ним
переместится объект. Этот прием называется перетас¬
киванием. Перетаскивание очень похоже на протяги¬
вание.
Выбранный объект
помечен маркерами
Указатель мыши
при протягивании
маркера
Указатель
мыши при
перетаскивании
объекта
Новые размерь
объекта
Новое положение
объекта
Настроим свойства объекта
Мы создали свой первый объект — Label1 (НадписИ), одна¬
ко нужного нам текста в этом объекте пока нет — его
тоже нужно создать. Для этого давайте познакомимся
со свойствами объектов.
Все объекты имеют свойства. Разные объекты имеют
разные свойства. Для объекта Label1 содержание над¬
писи — это одно из его важнейших свойств. Пока мы
85
Размер объекта изменяют
протягиванием
Положение объекта изменяют
перетаскиванием
его не задали, считается, что содержание надписи сов¬
падает с именем объекта. Именно поэтому мы и видим
на экране надпись Label1.
Для настройки свойств объектов служит специальная
панель Object Inspector (Инспектор объектов). Это очень важ¬
ный инструмент, которым приходится пользоваться
все время. Работают с этой панелью так.
1. Сначала выбирают настраиваемый объект.
2. Затем выбирают нужное свойство.
3. И наконец, задают этому свойству нужное значение.
Текущий текст надписи показан прямо на форме. Сей¬
час это стандартный текст: Label1. Изменить его можно
при помощи Инспектора объектов. Имя выбранногообъек-
та задается в раскрывающемся списке в верхней части
панели — сейчас здесь как раз выбран объект Label 1.
Кнопка
выбора
объектов
Значение
свойства
Настройка свойств объекта при помощи Инспектора
объектов
За текст надписи отвечает свойство Caption (Заголовок).
Чтобы изменить его, щелкните на нем в окне Инспекто¬
ра объектов. Введите текст надписи, например: Моя пер¬
вая программа. Если значением свойства является текст,
то в нем можно использовать русские буквы. Однако
русские буквы недопустимы в именах объектов.
Имя выбранного
объекта
Работа со
свойствами
Выбранное
свойство
86
Компонент О Объект Ф Элемент
Мы взяли компонент, с его помощью создали на форме
объект и ждем, что в готовой программе он станет эле¬
ментом управления. Вы еще не запутались? Если нет, то
хорошо.
Компонент — это как бы образец. С помощью компонен¬
та Label можно создать много похожих объектов: Label 1,
Label2, Label3... В первый моментэти объекты почти оди¬
наковы и различаются только именами. Но потом мы нач¬
нем их настраивать, и они станут различаться другими
свойствами. Когда программа будет готова, наши объек¬
ты станут в ее окне элементами управления.
Когда на заводе делают автомобили, используют одни и
те же детали — компоненты. Из них получается много по¬
хожих объектов — машин. В первый момент они почти
одинаковы и различаются только номерами на двигате¬
лях. Когда машины покрасят в разные цвета, эти объекты
будут различаться еще
больше. А когда машины
продадут покупателям и
они поедут по улицам и до¬
рогам, то станут элемента¬
ми дорожного движения.
87
Вы могли заметить, что размер объекта автоматически
изменился так, чтобы в точности соответствовать длине
надписи. Такое его поведение
определяется особым свой¬
ством. Это свойство AutoSize
(Автоподбор). Найдите его в
окне Инспектора объектов и убедитесь, что для него зада¬
но значение true (Да). Это и означает, что режим подбора
размера поля по его содержимому включен.
Мы можем выбрать еще и цвет шрифта. За него отвеча¬
ет свойство Font (Шрифт). Это составное свойство — обра¬
тите внимание на знак «+» рядом с его именем. Если
щелкнуть на этом знаке, свойство развернется. В Инс¬
пекторе объектов появится список подсвойств этого свой¬
ства.
Кнопка
настройки
шрифта
Вложенные
свойства
изображаются
со сдвигом
Свойство Font содержит вложенные свойства
Если щелкнуть на подсвойстве Color (Цвет), справа по¬
явится раскрывающийся список. В нем выбирается
нужный цвет. Например, можно выбрать фиолетовый
цвет — пункт clPurple.
Выбор цвета шрифта
88
Возможно, такое обозначение вас несколько удивило, по-англий¬
ски фиолетовый цвет обозначается просто словом purple. Но имен¬
но такое значение определено в языке С++, и мы можем (и бу¬
дем!) использовать его в своих программах.Буквы cl как бы
напоминают о назначении этой величины (от слова color).
Шрифт можно также сделать наклонным. Для этого
надо было бы воспользоваться свойством Style (Стиль),
которое также помечено знаком «+». Но это очень не¬
удобная операция — лучше действовать по-другому.
Если щелкнуть непосредственно на свойстве Font (Шрифт),
в правом столбце появится специальная кнопка
построителя. Если щелкнуть на ней, открывается
диалоговое окно Выбор шрифта.
Настройка характеристик шрифта
Свойство Font (Шрифт) содержит много разных парамет¬
ров. С помощью диалогового окна Выбор шрифта можно
сразу настроить все, что нужно, в том числе цвет, кото¬
рый мы уже настраивали непосредственно в Инспекторе
объектов. Система С++ Builder сама знает, какие свойства
отражают настройки, выполняемые в этом окне.
89
Создаем командную кнопку
Командная кнопка с подписью создается с помощью
компонента Button (Кнопка). Он тоже доступен на
вкладке Standard (Стандартные) палитры компонен¬
тов. Поместить кнопку на форму можно точно так же,
как надпись. Обычно размер кнопки делают таким,
чтобы подпись на ней располагалась аккуратно. Иногда,
чтобы привлечь внимание, создают очень большие
кнопки.
У длинной
кнопки —
длинная
подпись
Большая
кнопка
привлечет
внимание
Маленьких
кнопок
может быть
много
Окно с кнопками разного размера
Мы для своей программы сделаем кнопку небольшой.
Текст на ней, как и в случае надписи, задается свойством
Caption (Заголовок).Допустим, нана-
шей кнопке будет написано: За¬
крыть!
Окно формы после настройки параметров кнопки
90
Реакция на кнопку
Созданная кнопка сейчас служит лишь украшением
окна. При щелчке на ней должно происходить какое-
то событие, но программа о нем пока ничего не знает.
Наша задача — указать программе, что она должна де¬
лать при щелчке на кнопке. То есть мы должны напи¬
сать программный код, по которому будет обрабаты¬
ваться щелчок.
Чтобы задать реакцию на кнопку, выделите ее на фор¬
ме (кнопка помечается маркерами). Перейдите в окно
Инспектора объектов и щелкните на вкладке Events (Собы¬
тия). Теперь левый столбец Инспектора объектов содержит
все события, на которые может реагировать данный
объект. Против каждого события может быть указана
процедура — обработчик данного события. Пока что
никаких процедур у нас нет, и правый столбец пуст.
Дважды
щелкните
здесь
Создаем процедуру обработки щелчка
Ha какое событие нам надо реагировать? На щелчок
мышью на кнопке. Соответствующее событие называ¬
ется OnClick (При щелчке мыши). Чтобы создать для него
процедуру-обработчик, надо дважды щелкнуть в пра¬
вом столбце Инспектора объектов напротив этого события.
91
Это событие
описывает
щелчок _
Система С++ Builder автоматически активизирует окно
кода и добавляет в него обработчик этого события,
оформленный в виде процедуры. Процедура начинается
с ключевого слова void, после которого идут имя проце¬
дуры и ее параметры. Текст процедуры заключается в
фигурные скобки { и }, которые в языке С++ играют
роль операторов. Эти операторы система С++ Builder
вставляет автоматически, чтобы мы сразу видели, где
процедура начинается и где заканчивается.
Заготовка процедуры обработки в окне кода
Осталось только записать своими руками операторы,
описывающие, что происходит по щелчку на кнопке.
Чтобы нам проще было сделать это, текстовый курсор
уже поставлен в нужное место.
В нашем случае щелчок на кнопке должен завершать
работу приложения и закрывать окно. В языке С++ это
достигается при помощи одного-единственного опера¬
тора:
Close();
Его нужно ввести в процедуру вручную. На самом деле
это вызов стандартной процедуры, которая и выполня¬
92
Сюда вставляются операторы процедуры
Имя процедуры
ет все необходимые операции. «Пустые скобки», ука¬
занные после имени прОцедуры, говорят о том, что
никаких параметров в эту процедуру не передается.
В языке С++ опускать их нельзя.
Запуск программы
Наша программа готова! Хотя текст программы в окне
кода довольно длинный, мы «отвечаем» только за че¬
тыре оператора, из которых только один написали
сами. Все остальные действия берет на себя система
визуального проектирования — она гарантирует пра¬
вильность «обслуживания» использованных нами ком¬
понентов.
Давайте убедимся, что наша программа работоспособ¬
на и правильна. Для этого дайте команду Run >■ Run
(Выполнение >• Запуск). То же самое можно сделать, нажав
клавишу F9.
Система С++ Builder компилирует программу, создает ис¬
полняемый файл и запускает его. Посмотрите, как вы¬
глядит окно программы после запуска. Обратите вни¬
мание на то, что форма при этом временно убирается с
экрана, чтобы мы не перепутали ее с окном програм¬
мы. После того как вы убедитесь, что все в порядке,
щелкните на кнопке Закрыть! Окно программы закро¬
93
Эти строки программа
ввела автоматически
Эту строку ввели
мы сами
void fastcall TForml::ButtonlClick
(TObject *Sender)
Close(); чч
ется, как мы и хотели. При этом вы вернетесь в среду
С++ Builder.
О^Л 8 операционной системе UUindoujs часто встречаются диалоговые
5 окна, содержащие краткое сообщение и несколько командных
кнопок, например Да, Нет и Отмена. Программа, которую мы
только что написали, очень напоминает диалоговое окно. Можно
считать, что мы создали заготовку такого окна для будущих про¬
грамм. Впрочем, система С++ Builder содержит и более удобные
средства для создания диалоговых окон.
06 именах объектов и процедур
Система С++ Builder сама дает имена всем объектам, ис¬
пользуемым в программе. Но нам чаще всего надо знать
эти имена, потому что они понадобятся при написании
кода программы. Если мы создаем объект на основе
компонента, к имени компонента добавляется поряд¬
ковый номер.
Элемент управления
Компонент
Объект
Кнопка
Button
Button 1 *
Надпись
Label
Label 1 **
* Следующие кнопки получат имена Button2, Button3 и такдалее
** Следующие надписи получат имена Label2, Label3 и так далее
Такая система не всегда удобна. Если форма содержит
тридцать разных кнопок, искать, которая из них имеет
номер 17, неудобно. Часто имеет смысл переименовать
объект, изменив его свойство Name (Имя), но сделать это
Наша программа в работе
94
желательно сразу после создания объекта, не отклады-
вая«напотом».
Старое имя
Новое имя
Переименование кнопки
Свойство Name (Имя) выглядит точно так же, как и все
прочие свойства, но используется не в ходе работы про¬
граммы, а только во время ее написания. Фактически,
меняя это свойство, мы меняем текст нашей программы.
Если объект содержит текст, который виден e элементе управле¬
ния, то этот текст первоначально совпадает с именем объекта. Но
этот текст хранится в ином свойстве. Если мы хотим изменить его,
не переименовывая объект, свойство Name (Имя) нам не потре¬
буется.
Имя объекта — это имя переменной, описывающей его
в программе на языке С++. Если изменить свойство
Name (Имя), то надо поменять и имя переменной по все¬
му тексту программы. Поэтому это свойство лучше на¬
страивать сразу же после создания объекта. В этом слу¬
чае переменная еще не используется в программе и ме¬
нять ничего не придется.
Кроме того, система С++ Builder использует имена объек¬
тов для создания составных имен, например имен
процедур — обработчиков событий. Так, при обработке
щелчка (Click) на кнопке Button1 имя соответствующей
процедуры формируется как
Button1 + Click = Button 1Click
95
Если бы мы переименовали кнопку с запозданием, имя
процедуры тоже потребовало бы исправления.
Уже в следующей нашей программе нам придется ис¬
пользовать имена объектов в тексте кода. Но теперь это
не должно вызвать у нас трудностей.
. friQSQ 8
Сохранение — ллать учения!
В этой главе мы узнаем, как надо хра¬
нить свои труды на жестком диске, а
для этого нам придется познакомиться
с файлами и папками
Как хранится информация в компьютере
Многие любят компьютерные игры, a игры бывают раз¬
ные. В некоторые можно поиграть минут десять-пят-
надцать, потом выключить компьютер и завтра начать
все сначала. Но бывают такие игры, в которые играют
годами. За один день такую игру не пройти, и прихо¬
дится сохранять игру на жестком диске перед выклю¬
чением компьютера. Обычно это делается командой
Save Game (Сохранить игру).
Если игра сохранена, то на следующий день ее можно
продолжить с того же самого места. Обычно в играх для
этого служит команда Load Game (Загрузить игру) или Load
Saved Game (Загрузить сохраненную игру).
За сохранение и загрузку данных отвечают не програм¬
мы, работающие на компьютере, а операционная сис¬
тема Windows. То есть сама.программа не должна ни¬
чего записывать на жесткий диск или читать, что там
хранится. Вместо этого она обращается к операцион¬
ной системе с просьбой это сделать.
Существует множество самых розных видов и типов жестких дис¬
ков и других устройств для записи и чтения данных. Автор програм¬
мы не может знать заранее, что за устройство установлено имен¬
но в вашем компьютере. Зато это хорошо знает операционная
система компьютера. Вот почему программы сами не занимаются
записью но диск, а обращаются за помощью к операционной сис¬
теме.
4—1619
97
Файлы
Теперь посмотрим, как работает с жестким диском опе¬
рационная система Windows. Она записывает на диск
информацию в виде блоков, которые называются фай¬
лами, и тут же запоминает, в каком месте диска какой
файл лежит. Для этого Windows ведет бортовой жур¬
нал, который называется таблицей размещения фай¬
лов (она записана в служебной области диска).
Современные жесткие диски имеют очень большие раз¬
меры. На одном диске могут храниться сотни тысяч
файлов. Чтобы они не перепутались, каждый файл обя¬
зательно должен иметь имя, причем оно должно быть
уникальным — на диске не должно быть двух файлов с
одинаковыми именами.
А дальше все происходит очень просто. Если програм¬
ма хочет что-то записать на диск, она обращается к
операционной системе Windows и сообщает, под каким
именем она желает записать файл. Если это имя раньше
не встречалось, Windows выполнит указание. Если же
такое имя ранее было занято другим файлом, Windows
откажется от работы и вернет программе сообщение об
ошибке, а программа передаст его нам.
При загрузке данных все происходит в обратном поряд¬
ке. Сначала программа сообщает операционной систе¬
ме имя файла, который она желает загрузить. Затем
операционная система по таблице размещения файлов
определяет, в каком месте диска он находится, а потом
читает данные с диска и передает их программе.
98
Папки
Если бы тысячи файлов хранились на жестком диске
записанными подряд, разобраться с ними было бы не¬
возможно. Попробуй догадаться, к какой программе
относится файл отложенной игры Savegame15!
К счастью, кроме файлов на жестком диске можно со¬
здавать папки. У каждой папки, как и у файла, тоже
должно быть уникальное имя. Все файлы, относящиеся
к одной программе или к одному проекту, удобно хра¬
нить в одной папке.
Внутри папок можно создавать другие пап¬
ки — так образуются вложенные папки.
Например, у всех, кто работает на компью¬
терах с операционной системой Windows, на жестком
диске обязательно есть папка с именем Program Files.
Большинство программ, установленных на компьюте¬
ре, хранятся именно в ней.
Папки позволяют не только навести порядок в хране¬
нии файлов, но и помогают решить важную проблему,
связанную с тем, что у каждого файла должно быть осо¬
бое имя. Если бы все файлы хранились в одном месте,
было бы трудно придумывать им новые, ранее не ис¬
пользованные имена. А когда файлы лежат в разных
папках, иметь одинаковые имена им разрешено.
Диалоговое общение с компьютером
Сохранение и загрузка файлов происходят в диалого¬
вом режиме. Если вы еще не знаете, что это такое, то
вот вам небольшой пример. Заходит молодой человек
в книжный магазин и говорит продавцу:
— Здравствуйте, я хотел бы купить книжку.
— Здравствуйте! — отвечает продавец. — А какую
книжку вы хотели бы купить?
99
— Я еще не решил. Вы покажите, что у вас есть, а я
выберу.
Не правда ли, очень удобно? В словах: «Вы мне пока¬
жите, а я выберу»,— заключена суть диалогового ре¬
жима. Когда мы что-то хотим загрузить или, наоборот,
сохранить, мы даем команду программе, а она обраща¬
ется к операционной системе с просьбой открыть диа¬
логовое окно и показать, что и как хранится на диске.
В этом окне мы и делаем свой выбор. При сохранении
файлов выбирается место сохранения (папка), а при
загрузке — файл, который нам нужен.
Создание папки
Не только начинающий программист, но и вообще лю¬
бой пользователь компьютера должен четко понимать,
что у каждого файла должна быть папка. Запись фай¬
лов куда попало и как попало — самая вредная при¬
вычка, какая только может быть в работе с компьюте¬
ром. Приступая к любой работе, надо сначала решить,
где будут храниться ее результаты. Если для этого уже
есть подходящая папка — хорошо, а если ее нет, зна¬
чит, ее надо создать.
В системе Windows папки создаются очень про¬
сто — с помощью контекстного меню, которое,
если вы помните, открывают щелчком правой
кнопки мыши. Допустим, мы хотим сохранять все свои
труды на жестком диске в папке Cpp.
1. Сначала надо открыть окно с содержимым
жесткого диска. Для этого сначала дваж¬
ды щелкните на значке Мой компьютер, а
потом в открывшемся окне дважды щелк¬
ните на значке диска C:.
2. Когда откроется окно с содержимым диска C:, вы¬
берите в нем место, свободное от значков, и щелк¬
100
ните на нем правой кнопкой мыши — откроется
контекстное меню.
3. Выберите в контекстном меню пункт Создать. Он
помечен треугольником, a это значит, что он от¬
крывает новую группу команд.
4. Выберите команду Создать ► Папку, и вы увидите,
что в окне диска С: появился значок новой папки,
которая так и называется: Новая папка.
Создание новой папки с помощью контекстного меню
Переименование папки
Созданная папка имеет невыразительное
имя Новая папка. Это имя автоматически
подставила операционная система — оно
принято по умолчанию. Нам папка с таким
именем не нужна, и мы должны его заменить. Это мож¬
но сделать сразу после создания папки, пока ее имя еще
выделено. Сотрите его и введите новое имя, например
Cpp. Не всегда это получается с первого раза. Если оши¬
бетесь, щелкните на значке папки правой кнопкой
мыши, выберите в контекстном меню команду Переиме¬
новать и попробуйте еще раз.
101
По-английски знак + записывается словом plus. Когда запись С++
по какой-либо причине неприемлема, принято заменять символы
+ на буквы р.
Сохраняем свой проект
В прошлой главе мы научились работать с системой С++
Builder и даже сконструировали свою первую программу.
Однако мы не сделали одно важное дело — не сохрани¬
ли свой проект. Давайте придумаем ему имя, например
Закрыватель, и научимся сохранять свои труды. Для сис¬
темы С++ Builder разрабатываемая программа — это про¬
ект, поэтому сохранить нам надо именно проект. В даль¬
нейшем мы будем хранить все свои проекты в одной
общей папке C:\Cpp (ее мы уже создали), а внутри нее
каждый проект будет лежать в своей отдельной папке.
Сохранение проекта состоит из трех шагов:
1) создание папки для проекта внутри папки C:\Cpp;
2) сохранение файлов программы в папке проекта;
3) сохранение файла проекта в папке проекта.
Начать эту операцию можно командой File >• Save Project
As (Файл >• Сохранить проект как). Откроется диалоговое
окно Save Unitl As (Сохранить текст программы как).
Диалоговое окно сохранения проекта. Наша первая задача —
открыть в нем папку, в которой будет храниться проект
102
Ранее мы отвели для наших программ папку C:\Cpp.
Сейчас мы ее найдем (1), раскроем (2) и создадим в ней
вложенную папку для своего проекта (3).
Раскрывающийся
список выбора
папки
Раскрывающая
кнопка
Значок
жесткого диска
Сначала выбираем жесткий диск, на котором
будет храниться проект
1. Поиск папки. Щелкните на раскрывающей кнопке
списка Папка и выберите в открывшемся списке значок
диска C:.
Щелкните на значке диска С: — откроется его содер¬
жимое. Здесь вы должны увидеть значок нужной нам
папки Cpp.
Выбрав жесткий диск, выбираем папку для
хранения своих проектов
2. Раскрытие папки. Теперь дважды щелкните на знач¬
ке папки Cpp, чтобы она открылась.
103
Значок папки
для хранения
проектов /
3. Создание папки проекта. Мы уже умеем создавать
новые папки с помощью контекстного меню, и сейчас
могли бы создать внутри папки Cpp вложенную папку
проекта Закрыватель. Однако здесь можно обойтись без
контекстного меню, потому что в диалоговом окне
сохранения есть специальная кнопка для созда¬
ния новых папок.
Щелкните на кнопке Создание новой папки. Введите имя
папки (Закрыватель) и нажмите клавишу ENTER — папка
будет создана. Теперь ее можно раскрыть, как обычно,
и двойным щелчком сохранить в нее файлы.
Только что созданная
папка проекта
Кнопка создания
новой папки
Создание папки для хранения проекта
Сохраняем файлы
Теперь мы готовы записать в эту папку файлы програм¬
мы. Сначала мы сохраняем файл текста программы.
Для него уже подобрано имя Unit1. Мы можем его ис¬
пользовать. Щелкните на кнопке Сохранить — система
сохранит текст программы в файле Unit1 .cpp. Одновре¬
менно сохраняется и файл формы (с расширением .dfm).
104
Теперь надо сохранить файл проекта. На экране сразу
же появится диалоговое окно Save Project1 As (Сохранить
проект как). Оно уже настроено на нашу папку. Снова
щелкните на кнопке Сохранить — запишется файл
Project1.bpr.
Последняя операция — сохранение файла проекта
Каждая наша программа — отдельный проект. Мы бу¬
дем хранить каждый проект в отдельной папке. Имена
файлов внутри каждой из папок проекта могут быть
одинаковыми, но так как каждый проект хранится в
папке с особым именем, мы ничего не перепутаем.
Полезные советы
Возможно, создание новых папок и присвоение им
имен показалось вам слишком хлопотным делом. Да,
действительно, оно требует внимательности и аккурат¬
ности, но трудно только в первый раз. Когда проект уже
был сохранен, последующие сохранения происходят
легко и быстро. Для этого надо дать команду File > Save
All (Файл >- Сохранить все). Поскольку система С++ Builder
уже знает, где хранится проект, она обновляет все фай¬
лы без лишних вопросов.
105
— А когда надо сохранять проект? Наверное, когда он
завершен?
— Нет, на самом деле с этого надо начинать работу.
Сразу после создания нового проекта его надо сохра¬
нить в первый раз. Запомните это правило и всегда его
придерживайтесь.
— А как часто надо сохранять проект во время рабо¬
ты?
— Чем чаще, тем лучше. Например, стоит давать ко¬
манду сохранения после каждого добавления на фор¬
му нового объекта и его настройки.
Глава 9
Учим программу читать
Программисты создают свои програм¬
мы не для себя, а для другихлюдей. Здесь
мы научимся делать так, чтобы буду¬
щие пользователи программы могли вве¬
сти в нее текст и использовать его в
работе
Шаг назад
Вспомним, что в предыдущей программе мы научились
создавать текстовые сообщения. Для этого использо¬
вался компонент Label (Надпись). То, что программист
записал в объекте Надпись, пользователь увидит в окне
программы.
Но этот текст не «живой», а «мертвый». Пользователь
не может его ни изменить, ни использовать. Мы как
бы научили программу писать то, что хочет програм¬
мист. Сейчас мы научим ее читать то, что напишет
пользователь.
Описание новой программы
Для того чтобы пользователь мог вводить в программу
свой собственный текст, служит элемент управления,
который называется Текстовое поле (Text box или Edit box).
Текст в него можно не только вводить, но и изменять,
то есть редактировать. При щелчке на этом элементе
управления в нем появляется курсор. Он показывает
позицию ввода символов. Ввод или стирание символов
пользователь выполняет как обычно, с помощью кла¬
виатуры. Ввод данных в текстовое поле завершается
нажатием клавиши ENTER.
107
Окно нашей новой программы будет содержать три эле¬
мента управления: надпись, текстовое поле и команд¬
ную кнопку. В надписи будет записано сообщение
пользователю о том, что он должен сделать. В текстовом
поле он сможет ввести то, что захочет, а при щелчке на
командной кнопке этот текст будет подставляться в
строку заголовка окна. В общем, наша программа по¬
зволит пользователю изменять заголовок. Мы назовем
наш новый проект: Читатель.
Форма и компоненты
Запустите систему С++ Builder и создайте новый проект.
Сохраните его в папке Читатель. Для программы Читатель
нам нужны два знакомых компонента: Label (Надпись) и
Button (Кнопка) — и один незнакомый: Edit (Текстовое поле).
Настраиваем форму
Форма, на которую мы помещаем объекты, сама тоже
объект. У нее тоже есть название и свойства. Название
объекта-формы задается по общему правилу — Form 1, а
свойства доступны для настройки в Инспекторе объектов.
Первоначально форма — единственный объект, кото¬
рый есть у нас в программе. Она уже выбрана на панели
свойств. Позднее выбрать форму можно так же, как
Описание
текстового поля
(надпись) —v
Окно
программы
Командная
кнопка
Вот что должно у нас получиться
108
Текст в строке
заголовка
Текстовое
поле
любой другой объект. Щелкните
на ней на свободном месте или
найдите ее в раскрывающемся
списке в верхней части окна Ин¬
спектора объектов.
Начнем настройку формы со
строки заголовка. Текст заголовка хранится в свойстве
Caption (Заголовок). У новой формы это свойство имеет
значение Form1, такое же, как имя объекта. Щелкните
в правом столбце палитры свойств напротив этого свой¬
ства и введите слово Читатель — новое значение этого
свойства.
Форма — тоже
_ объект
_Содержимое
строки заголовка
окна — это одно
из свойств
формы
Текст в строке заголовка определяется
свойством формы
Создаем надпись
Теперь поместим на форму компонент Label (Над¬
пись). Найдите нужный значок в палитре компо¬
нентов и щелкните на нем.
Нарисуйте объект Label1 на форме протягиванием мыши.
Переместите его в левый верхний угол формы. Текст
надписи, как мы уже знаем, хранится в свойстве Caption
(Заголовок). Измените его, введя в него текст Введите новый
заголовок:.
109
Выбор формы в списке
объектов
Форма с надписью
Добавляем текстовое поле
Текстовые поля служат для ввода символов. Обычно
такие поля рассчитаны на одну строку текста, поэтому
созданный нами объект Edit1 должен иметь небольшую
высоту.
Выберите компонент Edit (Текстовое поле) в палитре
компонентов. Текстовое поле тоже создается про¬
тягиванием мыши. Если объект получился высоким,
уменьшите его высоту. Для этого служат маркеры из¬
менения размеров. Ширину поля сделайте побольше —
пользователь может захотеть ввести довольно длинный
заголовок окна.
Форма с надписью и текстовым полем
Настраиваем текстовое поле
Объект Edit1 содержит одну строку текста, но в отличие
от знакомого нам объекта Label1 он не имеет свойства
Caption (Заголовок). Действительно, обычная строка тек¬
ста не очень похожа на заголовок. Свойство, в котором
110
хранится содержимое текстового поля, называется Text
(Текст). Сюда система С++ Builder заносит имя созданного
объекта — строку Edit1.
Когда программа начнет работать, пользователь введет
в текстовое поле то, что пожелает, но мы можем пойти
ему навстречу и ввести что-нибудь заранее. Это удоб¬
но, чтобы он знал, чего от него ждет программа. Здесь
у нас есть три варианта действий.
1. Мы можем оставить текстовое поле пустым.
Для этого надо просто очистить значение свой¬
ства Text (Текст). Тогда после запуска программы
пользователь увидит пустое поле и сможет в
него что-нибудь ввести.
2. Мы можем сделать так, чтобы при запуске про¬
граммы в текстовом поле была записана какая-
нибудь подсказка, например: Здесь вы можете вве¬
сти новый заголовок окна, или короче: Введите новый
заголовок.
3. Третий вариант такой: можно сделать, чтобы
здесь был записан тот текст, который в данный
момент виден в строке заголовка окна. В момент
запуска в строке заголовка программы написа¬
но: Читатель. Здесь может быть написано то же
самое исходное значение: Читатель.
Все варианты хороши. Главное —
не то, что мы напишем в текстовом
поле, а то, что дальше пользователь
сам сможет изменять его, как по¬
желает. Давайте остановимся на
третьем варианте.
Запишите в качестве свойства Text Задаем содержимое
значение Читатель, как показано на текстового поля
рисунке.
111
Добаеляем кнопку
Командная кнопка нам нужна, чтобы при ее на¬
жатии программа прочитала то, что пользователь
ввел в текстовое поле, и поместила этот текст в строку
заголовка окна. Ей соответствует компонент Button (Кноп¬
ка). Выберите его в палитре компонентов, а затем про¬
тягиванием нарисуйте кнопку на форме.
Сразу же сменим надпись на кнопке. Она задается уже
знакомым нам свойством Caption (Заголовок). Обычно
надпись на кнопке описывает действие, которое выпол¬
няется при ее нажатии. Введем в это свойство строку
Сменить заголовок окна.
Наш текст длиннее, чем заданная по умолчанию стро¬
ка с именем компонента Button 1. Такая подпись может
поместиться не на любой кнопке. Поэтому мы и не ста¬
ли подбирать размер кнопки заранее.
Теперь, когда длина надписи известна, сделать это про¬
ще. Система С++ Builder всегда располагает подпись в
одну строку. Увеличьте длину кнопки так, чтобы все
слова поместились на ней.
Заодно переместим кнопку так, чтобы наше окно вы¬
глядело законченным. Действие кнопки связано с тек¬
стовым полем. Поместите ее поближе к этому полю.
Создаем командную кнопку
112
Нажатие кнопки с клавиатуры
В принципе, графический образ нашей программы уже
готов. Осталось только запрограммировать те действия,
которые будут выполняться при нажатии командной
кнопки. Но давайте еще немножко подумаем, как упро¬
стить жизнь будущему пользователю нашей програм¬
мы. Программист всегда должен об этом думать, если
хочет достичь успеха и признания.
Мы предполагаем, что пользователь во время работы
программы введет в текстовое поле какой-то текст,
после чего щелкнет на кнопке. Однако обратите вни¬
мание на то, что текст он будет вводить с клавиатуры,
а для щелчка ему понадобится мышь. Это значит, что
после ввода текста надо тянуться от клавиатуры к
мыши. Согласитесь, что это не очень удобно.
Проявить заботу о будущем пользователе совсем не¬
трудно. Операционная система Windows позволяет на¬
жимать одну из кнопок клавишей ENTER. Так как у нас
всего-то одна кнопка, мы можем это сделать. Чтобы задать
такую настройку, надо для свой¬
ства Default (По умолчанию) выбрать
в раскрывающемся списке значе¬
ние true (Да).
Текстовое поле уже работает
Пора переходить к программированию. Программа во
время работы должна позволять пользователю редак¬
тировать текст в поле и нажимать кнопку. Если бы мы
не пользовались системой визуального программиро¬
вания, то нам пришлось бы написать довольно слож¬
ную подпрограмму-редактор, которая вызывалась бы
всякий раз, когда пользователь начинал работу с тек¬
стовым полем.
113
К счастью, мы создали объект Edit1 из готового компо¬
нента, а это значит, что ничего нам программировать
не надо. Все необходимые процедуры редактирования
были вставлены самой системой С++ Builder вместе с
объектом. Ввод текста, его выбор и изменение — это
операции, «родные» для текстового поля, и нам не надо
его ничему учить.
Запустите созданную программу, нажав клавишу F9.
Проверьте и убедитесь, что в ней сразу можно менять
содержимое текстового поля. Нам доступны ввод и уда¬
ление символов, перемещение курсора, выбор фрагмен¬
тов текста. Можно даже копировать текст в буфер об¬
мена Windows и вставлять его оттуда.
Правда, мы не сделали ничего, чтобы окно программы
можно было закрыть. Если вы помните, в нашем про¬
екте Закрыватель мы создавали специальную командную
кнопку для закрытия окна программы. Здесь ее нет.
Как же мы закроем окно?
На самом деле нет ничего проще. У каждой програм¬
мы, правильно написанной для Windows, на правом
краю строки заголовка окна всегда есть три кнопки для
управления окном. Одна из этих кнопок — закрываю¬
щая. Как видите, и здесь система визуального програм¬
мирования заранее позаботилась о нас и оформила нашу
программу именно так, как это принято в операцион¬
ной системе Windows.
Строка заголовка со стандартными кнопками
Щелкните на закрывающей кнопке в строке заголовка —
и программа прекратит работу. То же самое можно сде¬
114
Закрывающая
кнопка
лать и нажатием стандартной клавиатурной комбина¬
ции: ALT+F4.
Программируем работу кнопки
Осталось только сделать самое главное — запрограмми¬
ровать работу своей командной кнопки так, чтобы текст,
введенный пользователем, становился заголовком окна.
Дважды щелкните на кнопке, помещенной на форме.
Откроется окно кода программы. В нем появится заго¬
товка процедуры Button 1 Click. Эта процедура вызывается
при щелчке на кнопке, сделанном во время работы про¬
граммы.
Нам надо перенести содержимое текстового поля в заго¬
ловок окна, а это значит, что в свойство Caption формы
надо записать то, что находится в свойстве Text тексто¬
вого поля. Сделать это можно с помощью оператора
присваивания:
Forml->Caption = E&itl->Text;
Мы присваиваем свойству формы Form1->Caption значе¬
ние, которое хранится в свойстве поля Edit1 ->Text. Опе¬
ратор присваивания «=» копирует значение свойства
Text текстового поля в свойство Caption формы. Это все,
что нужно, чтобы программа заработала.
Обратите также внимание на способ доступа к свой¬
ствам объектов из программы. Язык С++ требует для
этого указать «стрелочку», состоящую из двух симво¬
лов: «минус» и «больше». Пробел между ними недопу¬
стим.
Теперь программа готова. Благодаря нам в ней появи¬
лись четыре оператора, в том числе один, написанный
вручную. Запуск программы можно выполнить как
обычно — клавишей F9.
115
Эти строки подставила система
Эту строку вписали мы
Напоследок получше познакомьтесь с возможностями
текстового поля. Измените положение курсора, введи¬
те и удалите текст, выделите его. Скопируйте выделен¬
ный текст в буфер обмена Windows, нажав комбинацию
клавиш CTRL + С, а затем вставьте его в поле комбина¬
цией клавиш CTRL + V. Все эти приемы типичны для ра¬
боты с системой Windows. После того как «испытания»
закончены, щелкните на кнопке Сменить заголовок окна.
Убедитесь, что текущее содержимое текстового поля
перенесено в заголовок окна. Проверьте, можно ли по¬
менять заголовок еще раз.
Программа позволяет ввести в строку
заголовка любой текст
void fastcall TForml::ButtonlClick
(TObject *Sender)
Forml->Caption = Editl->Text;
f?lQBO 1 0
Создаем электронный альбом
Современная программа должна быть не
только удобной и понятной, но и краси¬
вой, а для этого она должнауметь рабо¬
тать с компьютерной графикой
Начнем с конца
Наши программы уже умеют воспроизводить на экра¬
не текст, причем двух видов: тот, что заложил в про¬
грамму ее автор, и тот, который ввел пользователь во
время работы с готовой программой. В этой главе мы
научим программу показывать рисунки.
117
Программа будет работать так: после запуска в ее окне
изображается рисунок, который заготовил програм¬
мист, а дальше пользователь может на нем щелкнуть
и выбрать в открывшемся диалоговом окне любой иной
рисунок, который он хочет посмотреть.
Окно готовой программы будет содержать только один
элемент управления — рисунок. Но, как мы вскоре уви¬
дим, это не единственный объект нашей формы — нам
придется еще кое-что сделать, чтобы рисунок выгля¬
дел красиво и чтобы пользователь мог его сменить.
Начало работы
Запустите систему С++ Builder и создайте новый проект.
Сохраните его в папке Электронный альбом. Переименуй¬
те форму так, чтобы в строке заголовка вместо слова
Form1 была запись Электронный альбом. Это мы уже умеем
делать — надо просто изменить свойство Caption (Заго¬
ловок) у объекта Form 1.
Теперь пора подумать о рисунке. Он создается с помо¬
щью компонента lmage (Рисунок). Но если мы немедленно
поместим его на форму, то его граница не будет никак
обозначена. Рисунок будет выглядеть неряшливо.
Картины принято вешать на стены в рамах — и нам следует
задать рамку для нашего рисунка
В системе С++ Builder есть специальный компонент, по¬
зволяющий создать подложку для любых элементов
118
управления. Он называется Panel (Панель). Давайте сна¬
чала поместим на форму его.
Подберите подходящий размер формы, изменив его
протягиванием за правый нижний угол. Затем вы¬
берите компонент Panel (Панель) на палитре компо¬
нентов и протягиванием нарисуйте панель так, чтобы
она заняла почти все окно формы. В ее центре распола¬
гается подпись, которая нам не нужна. Чтобы ее удалить,
очистите значение свойства Caption (Заголовок).
Обратите внимание на то, что панель выглядит выпук¬
лой. Давайте сделаем ее вдавленной. Для этого восполь¬
зуемся свойствами BevelOuter (Внешняя фаска) и Bevellnner
(Внутренняя фаска). Поэкспериментируйте с разными
значениями этих свойств и посмотрите, как выглядит
панель. Мы сохраним для свойства
Bevellnner (Внутренняя фаска) значение
bvNone, а свойству BevelOuter (Внешняя
фаска) присвоим значение bvLowered.
Кроме того, изменим ширину рамки, присвоив свой¬
ству BewelWidth (Ширина фаски) значение 2. Настройка
панели закончена. Запишите на бумажке размеры
панели — значения свойств Width (Ширина) и Height (Вы¬
сота). Мы будем считать, что они имеют значения 241
и 185, у вас эти числа могут быть другими. Они пона¬
добятся нам, чтобы точно подобрать размеры рисунка.
Панель для размещения рисунков
119
Теперь можно добавить и сам рисунок. Для этого нам
надо выбрать вкладку Additional (Дополнительные) на па¬
литре компонентов. Выберите на ней компонент
lmage (Рисунок). Создайте объект lmage1 в центре па¬
нели. Это и будет рисунок. Сейчас мы настроим его так,
чтобы он занял всю нужную область.
Мы настроим положение и размеры области рисунка с
помощью Инспектора объектов. Рисунок должен зани¬
мать практически всю панель, за исключением рамки,
которая создает впечатление вдавленности. Так как мы
создали объект lmage1 на панели, его положение опре¬
деляется относительно ее верхнего левого угла. Нам
нужно отступить от него на две точки — задайте для
свойств Left (Слева) и Тор (Сверху) значение 2.
Область рисунка переместится в левый верхний угол
панели. Теперь нам надо изменить ее размеры. С уче¬
том ширины рамки размеры рисунка должны быть на
четыре точки меньше, чем размеры панели. Нужные
числа надо присвоить свойствам Width (Ширина) и Height
(Высота). В нашем примере это будут значения 237 и 181.
Область рисунка: сразу после создания и после настройки
120
Осталась еще одна тонкость. Дело в том, что изображе¬
ния могут иметь разный размер. Мы же хотим, чтобы
они всегда изображались в одной и той же области. Нам
надо, чтобы программа сама растягивала или сжимала
рисунок в случае необходимости.
К счастью, ничего программировать нам не надо. Выбе¬
рите объект lmage1 и найдите в Инспекторе объектов свой¬
ство Stretch (Растяжка). У него только два состояния: true
или false (Включено или Выключено). Если растяжка вклю¬
чена, то размер рисунка будет подгоняться под размер
области просмотра: большой рисунок сожмется, а ма¬
ленький, наоборот, растянется. Включите для свойства
Stretch (Растяжка) значение true (Да).
Размещение иосодного рисунка
Мы хотим, чтобы в момент запуска программы ее окно
было не пустым. Поэтому программист должен сразу
загрузить в эту область какой-то рисунок. Для этого
найдите в Инспекторе объектов свойство Picture (Изображе¬
ние) и щелкните на нем. Напротив
свойства появится кнопка с много¬
точием.
Такие кнопки в системах визуального проектирования
называют построителями. Знак многоточия говорит
о том, что сейчас откроется диалоговое окно, в котором
можно будет что-то выбрать или настроить, причем
обойтись одной строчкой в окне Инспектора объектов
нельзя.
Щелкните на кнопке построителя — и откроется диало¬
говое окно Picture Editor (Редактор изображений), позволяю¬
щее выбрать рисунок, который будет сам загружаться
при запуске программы. Назовем его исходным рисун¬
ком. Щелкните на кнопке Load (Загрузить) — откроется
диалоговое окно Load Picture (Загрузка рисунка).
121
Выбираем исходный рисунок
Исходный рисунок надо выбрать из тех, что есть на ком¬
пьютере любого пользователя нашей программы. На эту
роль хорошо подходят рисунки, хранящиеся в папке
C:\Windows, потому что они есть у всех, кто работает с
операционной системой Windows. Откройте папку
C:\Windows и выберите файл Облака.Ьтр, после чего щелк¬
ните на кнопке Открыть. Вернувшись в окно Редактора
изображений, щелкните на кнопке ОК.
Теперь внешний вид окна нашей программы уже готов
Компонент-нееидимка
До сих пор мы имели дело с компонентами, создающи¬
ми на экране видимые объекты. Однако это далеко не
122
Система С++ Bilder позволяет открывать
разные стандартные диалоговые окна
Щелкните на значке компонента OpenDialog.
Затем протягиванием выберите область окна
формы вне рисунка облаков. В этом мес¬
те появится значок объекта OpenDialog1.
Этот значок имеет фиксированный раз¬
мер. Сколько места мы отвели под этот
объект методом протягивания, значения
не имеет.
123
все, что необходимо программисту. Объекты могут
быть и невидимыми — они работают где-то в глубинах
оперативной памяти компьютера и ждут своего собы¬
тия, после которого проявляют себя на экране. Таким
событием может быть, например, щелчок кнопкой
мыши.
Вспомним о том, что пользователь нашей программы
должен иметь возможность просматривать любые ри¬
сунки, какие пожелает. Нам надо дать ему средство для
их выбора. Проще всего это сделать с помощью стан¬
дартного диалогового окна выбора файла. А получить
это окно можно щелчком на текущем рисунке.
Для открытия стандартных диалоговых окон Windows
нужен особый компонент. Он находится на вкладке
Dialogs (Диалоговые окна) палитры компонентов. Чтобы
разыскать эту вкладку, нам придется не один раз щелк¬
нуть на стрелке вправо на правом краю палитры ком¬
понентов.
Вкладка
Dialogs_
Кнопки прокрутки
вкладок
Значок
компонента
на форме
Пожалуйста, обратите внимание на то, что созданный нами объект
OpenDialogl на самом деле не должен быть видимым. Это диало¬
говое окно, которого нет на экране во время работы программы.
Оно появляется, лишь когда пользователь щелкнет на рисунке. Та¬
кие объекты называют невизуальными. Размещать их на форме,
как мы делали это с визуальными объектами, не надо. Заместите¬
лем невизуального объекта на форме является его значок. Он
нужен только для настройки свойств невидимого объекта.
Настраиваем стандартное диалоговое окно
Вид будущего диалогового окна настраивается как
обычно, при помощи Инспектора объектов. Нам придется
изменить большинство свойств объекта OpenDialog1.
Свойство Title (Заголовок окна) определяет текст заголов¬
ка открываемого диалогового окна. Введем в него текст
Выбор изображения. В поле FileName (Имя файла) указыва¬
ется имя выбранного файла. Так как у нас уже есть
файл Облака.Ьтр, введем в это поле полный путь досту¬
па к нему: С:\УУтсЬуу5\Облака.Ьтр.
Объект lmage (Изображение) может показывать только
растровые изображения формата .BMP. Да мы и хотим,
чтобы можно было выбирать только картинки. Типы
допустимых файлов задаются свойством Filter (Фильтр).
Это свойство довольно сложное. Элемент фильтра со¬
стоит из двух частей, разделенных символом « | » (вер¬
тикальная черта). Введите в поле Filter (Фильтр) такую
строку:
Описание типа файла пользователь увидит в раскры¬
вающемся списке Тип файлов, а маска задает способ от¬
бора файлов. В нашем случае отбираться будут все фай¬
лы, имеющие расширение имени .BMP. Сформировать
содержимое фильтра также можно с помощью постро¬
124
Ппигпнир
Маска
Растровое изображение (*.bmp) | *.bmp
ителя. Это удобно, если надо задать несколько типов
файлов.
Если расширение имени файла не указано, оно берется
из свойства DefaultExt (Стандартное расширение). Дадим
этому свойству значение .bmp.
Пока что достаточно. Посмотрите на окно Инспектора
объектов и еще раз вспомните, какие свойства будущего
диалогового окна мы настроили.
Свойства объекта после выполнения настройки
Начинаем программирование
Сам по себе объект OpenDialog1 никак себя не проявля¬
ет — он невизуальный. До тех пор пока не произойдет
нужное событие, диалоговое окно на экране не появит¬
ся. Таким событием, по нашему замыслу, должен стать
щелчок на изображении. Изображение, открытое в
окне программы, — это единственный элемент управ¬
ления, для которого мы можем задать реакцию на щел¬
чок.
Выберите на форме объект lmagel. В окне Инспектора
объектов перейдите на вкладку Events (События) и дважды
щелкните напротив события OnClick (При щелчке мыши).
В окне кода появится заготовка процедуры lmage1Click —
обработчика щелчка на изображении.
125
void fastcall TForml::ImagelClick
(TObject *Sender)
{
' >
Характеристики диалогового окна для выбора файла
заданы свойствами объекта OpenDialog1. Чтобы пока¬
зать это окно на экране, надо вызвать метод Execute.
Итак, добавим всего лишь один оператор:
OpenDialogl->Execute();
Все действия пользователя при работе с диалоговым
окном «спрессованы» в этом операторе. Он и откроет
диалоговое окно на экране, и позволит пользователю
разыскать нужную папку и выбрать файл с картинкой,
и закроет диалоговое окно после нажатия кнопки ОК.
Этот оператор будет работать до тех пор, пока работа с
диалоговым окном не будет завершена.
Диалоговые окна, которые зобирают но себя все управление про¬
граммой, называются модальными. 6 операционной системе
UUindouus немало модальных окон. Мы сталкиваемся с ними при от¬
крытии, закрытии и печати файлов.
В итоге у нас должно получиться так:
void fastcall TForml::ImagelClick
(TObject *Sender)
{
OpenDialogl->Execute();
}
Загрузка изображения
Когда пользователь завершит работу с диалоговым
окном загрузки файла, имя выбранного файла вместе
126
с полным путем доступа к нему запомнится в свойстве
OpenDialog1->FileName.
Такую запись называют точечной и читают справа на¬
лево: OpenDialog1->FileName — этосвойство FileName (Имя
файла) объекта OpenDialog1. Термин точечная запись
пришел из других языков программирования, где вме¬
сто «стрелочки» используется точка.
Однако нам нужно не имя файла, а само изображение,
которое в нем хранится. Но это уже не трудно. Когда
имя файла известно, изображение загрузить можно.
Объект Picture (Изображение) имеет метод LoadFromFile (За¬
грузить из файла). Полный путь поиска файла передается
в качестве параметра. А есть ли у нас объект Picture? Да,
именно таким объектом представлено одноименное
свойство объекта lmagel. В итоге нужный нам опера¬
тор выглядит так:
Imagel->Picture->LoadFromFile
(OpenDialogl->FileName);
Метод LoadFromFile изменяет свойство Picture объекта
lmage1.
Проверка программы
Мы добавили в программу пять операторов, из кото¬
рых два нам пришлось записать вручную:
void _fastcall TForml::ImagelClick
(TObject *Sender)
{
OpenDialogl->Execute();
Imagel->Picture~>LoadFromFile
(OpenDialogl->FileName);
>
127
Можно ли считать программу готовой? В принципе —
да, но стоит поискать в ней недостатки. Начнем с того,
что проверим ее, запустив нажатием клавиши F9. В от¬
крывшемся окне щелкните на рисунке. Откроется ди¬
алоговое окно Выбор изображения. Обратите внимание на
строку заголовка и на раскрывающийся список Тип фай¬
лов. Их содержимое мы сформировали сами.
Заголовок окна, допустимые типы файлов
и первоначально открываемую папку мы задали сами
Прочие элементы этого окна нам хорошо знакомы. От¬
кройте папку C:\Windows и выберите в ней файл J1ec.bmp.
Обратите внимание на то, что в диалоговом окне ука¬
заны только файлы с расширением .BMP, а все осталь¬
ные не показываются. Это обеспечивает заданный нами
фильтр. Щелкните на кнопке Открыть. Убедитесь, что
изображение в окне нашей программы изменилось.
Не упустили ли мы чего-нибудь? Например, диалого¬
вое окно содержит кнопку Отмена. Что произойдет при
щелчке на ней? Проверьте сами: ничего произойти не
должно — окно просто закроется, а свойства объекта
не изменятся. Соответственно, в электронном альбоме
останется тот же рисунок, что и был.
128
Чтобы сменить рисунок, надо щелкнуть на изображении
и выбрать другой файл
Жестко© тестирование
Если программа работает так, как было задумано, это
еще не значит, что она безошибочна. Когда пользова¬
тель действует точно так же, как задумал программист,
это хорошо, но ведь он может совершать ошибки. По¬
этому, чтобы проверить программу по-настоящему, ей
устраивают жесткое тестирование и стараются действо¬
вать не так, как задумал программист.
Допустим, что будет, если пользователь не будет выби¬
рать файл с помощью мыши в диалоговом окне Выбор изо¬
бражения, a просто введет в поле Имя файла какое-нибудь
слово? Можете проверить это сами. Если файл сущест¬
вует, то изображение откроется в нашем окне. Это слу¬
чится, даже если расширение имени .BMP опущено. Но
если такого файла не существует, то при попытке его за¬
грузить произойдет ошибка и наша программа «рухнет».
Чтобы исправить этот недочет, надо обратиться к свой¬
ству объекта OpenDialogl, которое мы еще не настраи¬
вали. Оно называется Options (Параметры). Это сложное
свойство: щелкните на знаке «+» слева от имени — и
вы увидите, что оно содержит множество подсвойств.
5—1619
129
Все эти подсвойства могут быть включены или выключе¬
ны. Нас интересует подсвойство ofFileMustExist (Файл должен
существовать), которому надо присвоить значение true (Да).
Теперь наша программа и в самом деле готова. Если в
диалоговом окне Выбор изображения вместо имени файла
ввести какую-нибудь абракадабру, вы увидите сообще¬
ние об отсутствии файла.
После дополнительной настройки свойств использовать
некорректное имя файла становится невозможно
Закрыть это диалоговое окно теперь можно, либо выбрав
подходящий файл, либо командой Отмена. В обоих слу¬
чаях программе не придется иметь дела с некоррект¬
ным именем файла.
Запрещаем ввод имен несуществующих файлов
f?lQSO 11
Создаем строку меню
Почти все программы Windows позволяют
управлять ими при помощи строки меню.
В наших программах пока что никакого
меню не было. В этой главе мы научимся
создавать строку меню, а заодно сделаем
так, чтобы при щелчке в окне программы
открывалось контекстное меню
Описание программы
Если программа управляется через меню, ей, в прин¬
ципе, не нужны никакие другие элементы управления.
Строка меню обычно дает доступ ко всем существую¬
щим командам.
Наша программа будет очень простой. Мы запрограм¬
мируем минимальное число команд: команды показа
рисунков и команду завершения работы с программой
и выхода из нее. Еще мы научимся создавать вложен¬
ное меню, в котором и будут находиться команды по¬
каза рисунков. В итоге окно программы должно выгля¬
деть примерно так, как показано на рисунке.
Строка меню
Открытое меню
Вложенное
меню
Вот что должно у нас получиться
131
fge взять лленю
Запустим систему С++ Builder и создадим новый проект.
Давайте сохраним его в папке Меню. Теперь можно при¬
ступать к созданию самого меню. Откроем в палитре
компонентов вкладку Standard (Стандартные). Выбе¬
рем компонент MainMenu (Строка меню) и попыта¬
емся создать меню на форме обычным образом.
Вы увидите, что система С++ Builder поместит на форму
значок меню, но никакого меню не создаст. В чем же
тут дело? А в том, что меню — это не простой объект.
Каждый пункт меню является отдельным объектом и
обладает отдельным набором свойств. То есть вся сис¬
тема меню — это взаимосвязанный набор объектов.
Значок меню на форме — это инструмент, который по¬
зволит получить доступ к специальному редактору
меню. С помощью данного редактора мы создадим всю
структуру меню за одну операцию. Но прежде надо про¬
думать эту структуру самостоятельно и расписать ее на
бумаге.
Структура меню нашей
программы видна на ри¬
сунке. В строке меню име¬
ется два пункта: Файл и Вы¬
ход. При щелчке на пункте
Файл открывается меню,
также содержащее два пун¬
кта: Рисунки и Выход. Если
щелкнуть на пункте Рисун¬
ки, откроется вложенное
меню, состоящее из двух
пунктов, соответствующих разным рисункам. Эти пун¬
кты позволят показать выбранные рисунки на форме.
Если выбрать любой из пунктов Выход, работа програм¬
мы завершается.
132
Создаем меню e редакторе
Чтобы запустить редактор меню, дважды щелкните на
значке меню на форме. Откроется диалоговое окно,
озаглавленное так же, как объект
меню, в нашем случае — Form1->
MainMenu1. Оно содержит пустое
меню. Одновременно обратите вни¬
мание на окно Инспектора объектов.
В нем сейчас не выбран никакой
объект!
rFoim1>MainMenu1 Wf*I E3
При создании пунктов меню используются специальный
редактор и окно Инспектора объектов
Что бы это значило? А то, что как только мы зададим
текст пункта меню, система С++ Builder тут же создаст
нужный объект. Этот объект описывает ие меню в це¬
лом (объект MainMenu 1 у нас уже есть), а только данный
пункт меню. Система С++ Builder нумерует пункты меню
и начинает название объекта с буквы N.
Щелкните в окне Инспектора объектов напротив свойства
Caption (Заголовок), введите текст пункта меню — Файл
и нажмите клавишу ENTER. Система С++ Builder тут же
присвоит этому объекту имя N1 и создаст заготовку для
нового пункта меню.
Между существующими и будущими пунктами меню
можно переключаться с помощью щелчка мыши или
курсорных клавиш. Например, выберите щелчком но¬
вую заготовку в строке меню, снова переключитесь в
окно Инспектора объектов и введите для этого пункта на¬
133
звание Выход. Когда вы нажмете клавишу ENTER, этот
объект получит имя N2.
Теперь снова щелкните на пункте Файл. Вы увидите,
что редактор меню создал еще одну заготовку под этим
пунктом. Это заготовка для меню, которое откроется
при выборе пункта Файл в работающей программе. Ис¬
пользуя заготовки, создайте в этом меню два пункта:
Рисунки (система присвоит ему имя N3) и Выход (N4).
Но постойте, ведь пункт Рисунки должен также откры¬
вать подменю, но никакой заготовки система С++ Builder
на этот раз не создала. Как же быть? Все очень просто.
Выберите в редакторе меню пункт Рисунки и нажмите
комбинацию клавиш CTRL + ВПРАВО.
Система С++ Builder сформирует нужную нам заготовку,
и мы сможем создать пункты меню Облака (N5) и Лес
(N6). Ha этом наше меню готово.
Готовое меню в окне редактора
Закройте окно редактора меню и убедитесь, что теперь
строка меню появилась в основной форме программы.
Нам она еще понадобится.
Создаем контекстное меню
Техника создания контекстного меню такая же, как и
при создании строки меню. В палитре компонентов
выберите на вкладке Standard (Стандартные) компо¬
нент PopupMenu (Контекстное меню). Щелкните на
форме, чтобы создать значок объекта.
%
134
Двойной щелчок на этом значке откроет редактор
меню, работа с которым ведется так же, как и раньше.
С помощью уже знакомых нам приемов создайте два
пункта меню: Облака и Лес. Система С++ Builder ведет
сквозную нумерацию пунктов меню, так что они полу¬
чат имена N7 и N8.
Контекстное меню в окне редактора
Добавляем изображение
Чтобы завершить создание формы, нам осталось только
добавить область, в которой будет отображаться изобра¬
жение. Откройте вкладку Additional (Дополнительные)
палитры компонентов, выберите компонент lmage
(Изображение) и нарисуйте на форме область изоб¬
ражения. Сразу же задайте для свойства Stretch (Ра¬
стяжка) значение true (Да).
На этом создание формы программы завершено, и мы
можем приступать к программированию
Этот значок
представляет
контекстное
меню
Готовая форма для нашей программы
Строка меню
отображается
на форме
Этот значок
представляет
строку меню
135
Область
рисунка
Приступаем к программированию
Как обычно, мы сначала подготовили графический об¬
раз будущей программы и только после этого присту¬
паем к настройке логики работы ее элементов управ¬
ления. Давайте начнем с того, что посмотрим, что же у
нас получилось. Для этого нажмите клавишу F9.
Легко заметить, что наше меню уже присоединено к
форме. Мы можем щелкать на пунктах меню и откры¬
вать вложенные меню. Все это уже работает. Но если
щелкнуть на любом из двух пунктов Выход или на ко¬
манде выбора рисунка, то меню закроется и ничего не
произойдет. Это и понятно: ведь мы пока ничего для
этого не сделали. Мы не написали процедуры, которые
должны обрабатывать щелчки мышью на пунктах
меню. Так что пока завершить работу с программой
можно только щелчком на закрывающей кнопке в пра¬
вом верхнем углу окна.
Итак, приступим к созданию процедуры, соответству¬
ющей пункту меню. Наше меню отображается не только
в работающей программе, но и в окне формы. Щелк¬
ните на пункте Выход в строке меню. В окне кода будет
создана процедура-обработчик с именем N2Click. Имя
этой процедуры сформировано по знакомому нам пра¬
вилу, а в качестве имени объекта, связанного с собы¬
тием, стоит имя пункта меню:
void_fastcall TForml::N2Click(TObject *Sender)
{
>
Впишите в эту процедуру один оператор:
136
Close();
Теперь создадим обработчик для вложенного меню. Это
делается точно так же. Если щелкнуть на пункте Файл,
откроется вложенное меню, в котором тоже можно вы¬
брать пункт Выход. Будет создана процедура-обработчик
N4Click. В нее тоже можно вписать оператор Close, и это
будет правильно, но, если можно так сказать, не совсем
« по-программистски ».
Программисты так не поступают, и вот почему. В на¬
шей программе пункт Выход повторяется два раза, и про¬
граммист должен специально позаботиться о том, чтобы
все они работали совершенно одинаково. Он, конечно,
может повторить оператор Close в двух местах — он та¬
кой короткий, что сделать это нетрудно. Но скажите
пожалуйста, что мы будем делать, когда придется по¬
вторять многочисленные строки кода, а такое встреча¬
ется часто.
Самый грамотный подход здесь такой. Раз мы уже один
раз создали процедуру, выполняющую какую-то рабо¬
ту, то во втором случае мы не будем ее создавать, а вызо¬
вем исполнение ранее созданной процедуры. Для этого
выберем объект N4 в раскрывающемся списке в верх¬
ней части окна Инспектора объектов,
перейдем на вкладку Events (События),
но вместо того чтобы создавать но¬
вую процедуру-обработчик, выбе¬
рем уже существующую из раскры¬
вающегося списка.
Как следствие, какой бы из пунктов Выход ни выбрал
пользователь, завершение работы программы обеспе¬
чит одна и та же процедура.
137
Если нам захочется изменить реакцию программы на
выбор пункта Выход, то придется внести изменение
только в одно место — в процедуру N2Click, а не в два
разных.
Программируем показ картинок
Выберите в строке меню на форме пункт Облака. Сис¬
тема С++ Builder создаст процедуру N5Click для обработки
этого пункта меню. Мы уже умеем показывать картин¬
ки в окне программы, так что добавим в эту процедуру
единственный оператор:
Imagel->Picture->LoadFromFile
("C:\\Windows\\06naKa.bmp");
Аналогичным образом создадим процедуру для обра¬
ботки пункта Лес. В нее надо добавить оператор:
Imagel->Picture->LoadFromFile
("С:\\Windows\\nec.bmp");
Проверка
Еще раз запустим нашу программу, нажав клавишу F9.
Теперь она уже почти готова. Строка меню и все коман¬
ды работают безупречно. Любой из пунктов Выход завер¬
шает работу программы.
Контекстное меню
Но мы еще хотели открывать контекстное меню при
щелчке правой кнопкой мыши на окне программы.
Контекстное меню мы уже создали, но оно пока что
неработоспособно. Кроме того, нам нужно запрограм¬
мировать операцию открытия контекстного меню.
138
Запрограммировать команды контекстного меню не¬
сложно. Здесь дело обстоит точно так же, как и с пунк¬
том Выход: команды контекстного меню дублируют ко¬
манды основного меню, и мы можем использовать для
одинаковых команд одну и ту же процедуру-обработчик.
Выберите с помощью Инспектора объектов объект N7 и
на вкладке Events (События) подключите к событию
OnClick (При щелчке) процедуру N5Click. Точно так же
подключите к объекту N8 процедуру N6Click.
А как открыть контекстное меню? Очень просто.
Объект PopupMenu содержит метод Popup, который от¬
крывает контекстное меню в точке с заданными экран¬
ными координатами.
«По правилам» контекстное меню открывается при
щелчке правой кнопкой мыши, но мы можем откры¬
вать его при нажатии любой кнопки. Для этого нам
достаточно описать обработчик события MouseDown (На¬
жатие кнопки мыши) для формы. Щелкните на формы вне
имеющихся объектов, выберите в окне Инспектора объек¬
тов вкладку Events (События) и создайте обработчик для
события OnMouseDown (При нажатии кнопки мыши). В про-
цедуру-обработчик передаются локальные координаты
указателя мыши.
Если вы внимательно читали эту книгу, то могли за¬
метить, что перед нами встала небольшая, но важная
проблема. Контекстное меню выдается в точке, кото¬
рая задана экранными координатами, отсчитанными
от верхнего левого угла экрана. А локальные коорди¬
наты отсчитываются от левого верхнего угла формы.
К счастью, система С++ Builder предоставляет стандарт¬
ный метод ClientToScreen, который выполняет нужное
преобразование. Правда, данные передаются в этот
метод в виде структуры TPoint, которая содержит два
поля: X и У.
139
Поэтому нам придется создать переменную типа TPoint,
записать в нее локальные координаты, получить экран¬
ные координаты, а потом уже открыть контекстное
меню. Процедура FormMouseDown будет выглядеть так:
140
void fastcall TForml::FormMouseDown
(TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y)
{
TPoint p;
p.x = X;
Р-У = Y;
p = ClientToScreen(p);
PopupMenul->Popup(p.x, p.y);
}
Запустите программу, нажав клавишу F9. Щелкните
на форме левой или правой кнопкой мыши. Убедитесь,
что в обоих случаях открывается контекстное меню, со¬
держащее два пункта — команды загрузки картинок.
Это меню теперь работает, хотя его команды использу¬
ют процедуры обработки, которые мы написали для
строки меню. Фактически, мы обращаемся к командам
строки меню через контекстное меню.
Контекстное меню при работе программы
Что у нас получилось
Вот теперь наша программа готова. Давайте приведем
ее еще раз, целиком:
141
void_fastcall TForml::N2Click(TOb;ject *Sender)
{
Close();
>
void fastcall TForml::N5Click(TObject *Sender)
{
Imagel->Picture->LoadFromFile
("С:\\Windows\\Q6naKa.bmp");
}
void fastcall TForml::N7Click(T0bject *Sender)
{
Imagel->Picture->LoadFromFile
("С:\\Windows\\nec.bmp");
}
void fastcall TForml::FormMouseDown
(TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y)
{
TPoint p;
p.x = X;
p.y = Y;
p = ClientToScreen(p);
PopupMenu1-> Popup(p.x, p.y);
}
Посмотрите еще раз код готовой программы. Он содер¬
жит всего четыре процедуры. Обратите внимание на то,
что все процедуры обработки пунктов меню активизи¬
руются из двух разных мест программы.
Проверьте, понятно ли вам, что делает каждая из этих
процедур? Если что-то осталось непонятным, вернитесь
к проработанному материалу еще раз. Не стоит остав¬
лять «на потом» то, с чем по каким-то причинам пока
не удалось разобраться.
f?IQSQ 1 2
npOrpQAAMQ уЧИТСЯ СОЧИНЯТЬ
В этой главе мы научимся создавать рас¬
крывающиеся списки, а наша программа
научится составлять предложения из
тех слов, которые мы заранее в эти
списки введем
Проект «Писатель»
Мы уже научили наши программы писать и читать и
теперь можем подготовить программу, которая умеет
сочинять предложения. Правда, кому-то ее творения
могут показаться нелогичными, зато все согласятся,
что они забавны.
Наша программа будет работать так. Мы введем в нее
три списка слов и научим ее брать одно слово из перво¬
го списка, одно — из второго и одно — из третьего. Из
этих трех слов она и будет составлять предложения.
Грамматически эти предложения всегда будут пра¬
вильны. Ачто до нелогичности, так это дело наживное —
просто пока наши программы еще не умеют думать.
Придет время, мы научим их и думать, и запоминать,
и считать.
Раскрывающиеся списки
>— Кнопка случайного выбора
Вот что должно у нас получиться
143
Форма и компоненты
Запустите систему С++ Builder и создайте новый проект.
Сохраните его под именем Писатель.
Наша форма будет содержать четыре элемента управ¬
ления: три раскрывающихся списка и одну командную
кнопку. Раскрывающийся список задается компонен¬
том ComboBox (Поле со списком). С помощью этого же ком¬
понента можно создать поле со списком.
Создаем и настраиваем списки
Ha вкладке Standard (Стандартные) палитры компо¬
нентов выберите компонент ComboBox (Поле со спис¬
ком) . Поместите список на форму методом протягивания.
Немного увеличьте ширину формы и разместите рядом
с первым списком еще два таких же. Постарайтесь, чтобы
созданные объекты имели одинаковую высоту.
Расположите списки ровно и аккуратно
Теперь настроим созданные списки. Сначала определим
тип списка. Он задается свойством Style (Стиль). Щелк¬
ните на панели свойств напротив этого свойства и вы¬
берите нужное значение из раскрывающегося списка.
Нам нужен вариант csDropDownList (Раскрывающийся спи¬
сок). Проделайте эту операцию для всех трех списков.
Выбираем тип списка
144
Для каждого из списков надо задать содержимое, то
есть набор пунктов, которые в него должны входить.
Содержимое списка — это одно из важнейших его
свойств, так что вводить его надо с
помощью Инспектора объектов. Разы¬
щите в этом окне свойство ltems (Пункты). Если выбрать
его, вы увидите кнопку построителя.
Щелкните на ней — откроется диалоговое окно String
List Editor (Редактор списка строк). Это специальный инст¬
румент для заполнения списка. Мы вводим пункты
списка по одному в каждую строчку, завершая ввод
нажатием клавиши ENTER. После того как список готов,
надо щелкнуть на кнопке ОК.
Пусть в первом списке у нас будут названия животных.
Во второй список занесем наречия, характеризующие
образ действия, например: «быстро», «медленно»,
«хорошо», «плохо» и так далее.
Наконец, в третий список занесем глаголы, описыва¬
ющие разные действия, например так, как показано на
рисунке.
Формируем содержимое раскрывающихся списков
145
Списки бывают разные
Для системы Windows обычны пять разных видов списков.
Раскрывающийся список занимает в окне
одну строчку. По щелчку на раскрывающей
кнопке список разворачивается. В нем мож¬
но выбрать любой пункт. Когда список свер¬
нут, мы видим только выбранный пункт.
Раскрывающееся поле со списком похоже
на раскрывающийся список. Но оно позво¬
ляет вводить с клавиатуры новые значения,
которых в списке еще нет. Текст в поле спис¬
ка можно выбирать и редактировать.
У простого поля со списком список лежит
ниже текстового поля, а раскрывающей
кнопки нет. Сегодня этот элемент управления
встречается редко.
Эти элементы управления создают компо¬
нентом ComboBox (Поле со списком).
У простого списка на экране сразу видно
несколько строк. Простые списки использу¬
ют, если надо подчеркнуть наличие выбора
или выбрать сразу несколько пунктов.
Простой список создают компонентом
ListBox (Список).
Еще один вид списка — это список флажков.
Возле каждого пункта списка имеется фла¬
жок, что позволяетодновременно выбрать не¬
сколько пунктов.
Список флажков создают компонентом
CheckListBox (Список флажков).
146
Добавление кнопки
Командную кнопку для «перемешивания» спис¬
ков создадим при помощи компонента Button (Ко¬
мандная кнопка). Поместим ее в нижнюю часть формы под
раскрывающимися списками. Изменим ее свойство
Caption (Заголовок), определяющее надпись на кнопке.
Пусть на кнопке будет написано: Случайный выбор.
Первая проверка
Как ни странно, наша программа уже почти готова. Ее
можно запустить, нажав клавишу F9. Компоненты
системы С++ Builder таковы, что все функции работы с
раскрывающимися списками берут на себя. Не написав
ни единой строчки кода, мы, тем не менее, можем вы¬
бирать все, что захотим.
Запуск программы, не содержащей ни одного оператора
Однако в нашей программе можно заметить два недо¬
статка.
1. Во-первых, в начальный момент наши списки пу¬
сты. В них ничего не отображается, а это непра¬
вильно. Хотелось бы, чтобы программа порадова¬
ла нас предложением типа «Заяц высоко летает».
2. Во-вторых, не работает командная кнопка. Можно
сколько угодно щелкать на ней — никакой види¬
мой реакции не будет, так как мы пока не запро¬
граммировали процедуру — обработчик нажатия
кнопки.
147
В общем, давайте остановим программу при помощи
команды Run >• End (Запуск >• Остановить) и приступим к
«ручному» программированию.
Присвоение начальных значений: ищем событие
С начальными значениями списков есть немалая про¬
блема, которую мы сейчас будем преодолевать. Дело в
том, что, сколько бы мы ни изучали окно Инспектора
объектов, мы не найдем свойства, отвечающего за то, что
должно быть записано в списке в момент запуска про¬
граммы! Как же нам быть?
Напрашивается такое предложение: если мы не можем
задать начальное значение списка, пусть программа это
сделает сама. В таких случаях говорят, что операция
должна выполняться программно.
Итак, первый выбор значения из списка должен выпол¬
няться сам, без нашего участия, то есть автоматически.
Как это сделать?
Это сделать можно, если мы научим программу реаги¬
ровать на какое-то событие, но давайте подумаем, что
это за событие? Оно не может быть связано со списка¬
ми, поскольку должно произойти еще до того, как про¬
грамма нарисует их на экране. Оно не может быть свя¬
зано и с мышью, поскольку тоже должно произойти до
того, как на экране появятся какие-либо элементы
управления.
К счастью, такое событие есть. Оно связано с формой.
Форма — это ведь тоже объект, имеющий свойства, и у
нее есть нужное нам событие — это создание. Оно на¬
ступает в тот момент, когда на экране появляется окно
будущей программы, но до того, как в нем появятся
элементы управления. Называется это событие — Create
(Создание). С этим событием связанапроцедура-обработ-
148
чик FormCreate. Именно ее мы и используем, чтобы за¬
дать начальные значения списков.
0 f /, Запомните этот прием. Он пригодится вам не роз и не два, когдо
^^ вы будете разрабатывать собственные программы. Программи¬
сты очень часто используют именно его, чтобы ввести начальные
параметры, с которыми программа начнет свою работу.
Щелкните на пустом месте формы, там, где нет никаких
объектов. В окне Инспектора объектов выберите вкладку
Events (События). Дважды щелкните напротив события
OnCreate (При создании). Система С++ pg^—жита V|
Builder создаст процедуру FormCreate.
То, какой элемент списка отображается на экране, опре¬
деляет свойство ltemlndex (Индекс элемента). Наши раскры¬
вающиеся списки — это объекты с именами ComboBox1,
ComboBox2 и ComboBox3. Нумерация пунктов в списках
начинается с нуля. Индекс первого элемента имеет зна¬
чение 0.
Пусть eac не пугает страшное слово «индекс». Это самый обычный
номер элемента списка, правда, если начинать отсчет с нуля.
Пусть сначала мы хотим показать первый элемент каж¬
дого списка. Для этого в процедуру FormCreate надо вве¬
сти такие операторы:
149
Окно кода с процедурой FormCreate
ComboBoxl->ItemIndex = 0;
ComboBox2->ItemIndex = 0;
ComboBox3->ItemIndex = 0;
Чтобы получить после запуска программы какую-то
конкретную фразу, можно выбрать и другие пункты
списка. Только не забудьте, что нумерация элементов
начинается с нуля.
Допустим, мы хотим, чтобы при запуске программы
появлялось предложение Кузнечик высоко прыгает. Слово
Кузнечик — четвертый элемент первого списка, значит,
его индекс равен 3. Получается оператор:
ComboBoxl->ItemIndex = 3;
Слово Высоко — второй элемент второго списка, значит,
его индекс равен 1. Получается оператор:
ComboBox2->XtemIndex = 1;
И наконец, слово Прыгает — пятый элемент третьего
списка, значит, его индекс равен 4. Получается опера¬
тор:
ComboBox3->ltemIndex = 4;
Как запрограммировать неожиданность
Ура! Наша программа при запуске уже может состав¬
лять предложение, правда, всегда одно и то же, а имен¬
но то, которое подготовил автор программы. Наша сле¬
дующая задача: сделать так, чтобы предложение было
неожиданным и неподготовленным, в общем, случай¬
ным. Для этого у нас даже есть заготовленная команд¬
ная кнопка Случайный выбор, которая, увы, пока не рабо¬
тает.
Для случайного выбора в системе С++ Builder есть спе¬
циальная функция random. В эту функцию надо пере¬
дать один параметр — целое число. Функция вычис¬
ляет случайное число от нуля до заданного параметра.
Это именно то, что нам нужно. Если в качестве пара¬
150
метра передать число элементов в списке, то мы и по¬
лучим номер случайно выбранного элемента.
— Все это хорошо, но откуда наша программа узнает,
сколько элементов находится в каждом из ее списков?
Сегодня в нашем списке зверей 7 элементов, а завтра
мы добавим в него какого-нибудь зверя или двух, и всю
программу придется заново переписывать?
— Нет, ничего переписывать не придется. Мы задавали
пункты списка с помощью свойства ltems (Пункты). Но это
свойство имеет вложенное подсвойство Count (Число), в
котором как раз и хранится количество элементов,
имеющихся в списке.
Правда, в окне Инспектора объектов этого свойства вы не
найдете — это потому, что свойство Count предназначе¬
но только для чтения, его нельзя вводить вручную.
Число элементов в первом списке определяется свойством
ComboBox1->ltems->Count, во втором — ComboBox2->ltems->
Count и так далее.
Таким образом, чтобы найти случайный элемент для
первого списка, нам надо просто вычислить выражение
random(ComboBox1->ltems->Count), а для второго спис¬
ка — random(ComboBox2->ltems->Count) и так далее.
Это все! Теперь мы знаем достаточно, чтобы запрограм¬
мировать действие кнопки Случайный выбор.
Программируем кнопку
При щелчке на кнопке в каждом из трех раскрываю¬
щихся списков должен появляться какой-то элемент
списка, взятый случайным образом.
Вспомните: когда нам надо было, чтобы в первом списке
появлялся его четвертый элемент (индекс равен 3), мы
вводили в процедуру FormCreate оператор
ComboBoxl->ItemIndex = 3;
151
Тогда мы точно знали индекс элемента — он равнялся
трем. Сейчас мы хотим, чтобы индекс рассчитывался
случайно, и мы не знаем заранее, каким он будет, но
зато знаем, как его вычислить:
ComboBoxl->ItemIndex =
random(ComboBoxl->Xtems->Count);
Теперь мы готовы написать все необходимые операто¬
ры. Выберите кнопку на форме, перейдите в окно Инс¬
пектора объектов, откройте вкладку Events (События) и
дважды щелкните напротив события OnClick (При щелч¬
ке). Система С++ Builder создаст процедуру обработки
щелчка на кнопке — Button 1Click. Вставьте в нее следу¬
ющие операторы:
ComboBoxl->ItemIndex =
random(ComboBoxl->Items->Count);
ComboBox2->ItemIndex =
random(ComboBox2->Items->Count);
ComboBox3->ItemIndex =
random(ComboBox3->Items->Count);
Они делают как раз то, о чем мы говорили: выбирают
случайный элемент в каждом из списков.
Правда, есть еще одна тонкость. Если мы попытаемся
немедленно запустить нашу программу, то система С++
Builder укажет, что функция random не определена. При
использовании стандартных процедур они также дол¬
жны быть описаны. К счастью соответствующие опи¬
сания подготовлены заранее, и их необходимо просто
добавить в программный код. Вставка выполняется с
помощью специальной директивы (это не оператор
языка, а указание компилятору).
В начало кода программы надо добавить следующую
строку:
152
#include <stdlib.h>
Она указывает, что в данное место надо вставить содер¬
жимое служебного файла stdlib.h, который содержит,
среди прочего, и описание функции random.
Вторая проеерка
Давайте снова запустим программу и убедимся, что за¬
меченные нами ранее ошибки и недочеты исправлены.
Сразу после запуска программы списки уже не выгля¬
дят пустыми, а командная кнопка работает именно так,
как мы хотели. Можно ли считать, что программа рабо¬
тает правильно? В целом да, но в ней все-таки остался
еще один недочет.
Чтобы его увидеть, надо запустить программу несколько
раз и каждый раз внимательно следить, какие предложе¬
ния сочиняет программа по нажатию кнопки Случайный
выбор. Вы должны заметить, что последовательность
фраз после каждого запуска в точности повторяется.
Так происходит потому, что функция random — это не
игральные кости, на которых выпадают случайные зна¬
чения. Это математическая функция, работа которой
запрограммирована неким алгоритмом. После каждого
запуска этот алгоритм работает совершенно одинаково.
Поэтому в одном запуске программы результаты выгля¬
дят довольно случайными, но они совпадают с резуль¬
татами, выданными во втором запуске, третьем и так
далее. В общем, функция random выдает нам не совсем
случайные числа, а так называемые псевдослучайные.
Это не всегда плохо. В компьютерных играх, например,
иногда приятно знать, что за каким-то поворотом до¬
роги вас всегда ждет один и тот же монстр. Если не уда¬
лось пройти его с первого раза, значит, загружаем игру
заново и делаем вторую попытку. Но вы сами, навер-
153
ное, не раз встречали игры, в которых после каждого
запуска ситуация становится новой, непредсказуемой.
Сейчас мы узнаем, что нужно сделать, чтобы и в нашей
программе последовательность чисел, которые создает
функция random, была более случайной. Для этого надо
немного перенастроить работу этой функции.
Неповторяющиеся случайные число
Функция random всегда работает по одному и тому же
алгоритму. Мы его не знаем, да он нам и не нужен. Но,
в двух словах, он работает примерно так.
1. Берется какое-то число от 0 до 1 (всегда одно и то
же).
2. Из этого числа по какому-то алгоритму рассчиты¬
вается другое число, тоже находящееся в диапа¬
зоне от 0 до 1.
3. Из второго числа по тому же самому алгоритму
рассчитывается третье.
4. И так далее, до тех пор пока программа работает.
5. Когда мы обращаемся к функции random, «теку¬
щее» случайное число приводится к запрошенно¬
му нами диапазону.
Все числа, которые выдает функция random, выглядят
случайными, но поскольку она начинает работу всегда
с одного и того же числа, то разные последовательно¬
сти оказываются одинаковыми. Чтобы они оказались
разными, надо «уговорить» функцию random начинать
работу не с одного и того же числа, а с какого-то слу¬
чайного, заранее неизвестного числа. Есть только один
вопрос: «Где его взять, если оно заранее неизвестно?»
Это действительно проблема, причем не математиче¬
ская, а техническая: «Где взять то, не знаю что? » К счас¬
тью, у нее есть оригинальное решение.
154
Как вы, наверное, знаете, в компьютере есть электрон¬
ные часы — они называются системным таймером.
Системный таймер отсчитывает миллисекунды, которые
слагаются в секунды, минуты, часы, дни и годы. Если
определить момент запуска любой программы в милли¬
секундах, то можно сказать, что это значение для любой
программы всегда разное, а значит, его можно исполь¬
зовать для того, чтобы функция random начинала работу
каждый раз с разного числа.
Делается это удивительно просто — надо в программу
вставить всего один оператор:
randomize();
Добавьте его в начало уже существующей процедуры
FormCreate. Теперь наша программадействительно гото¬
ва. Вот как выглядит та ее часть, которая написана
нами:
void __fastcall TForml::FormCreate
(TObject *Sender)
{
randomize();
ComboBoxl->ItemIndex = 0;
ComboBox2 ~ >11 emIndex = 0;
ComboBox3->ItemIndex = 0;
}
void fastcall TForml::ButtonlClick
(TObject *Sender)
{
ComboBoxl->ItemIndex =
random(ComboBoxl->Items->Count);
155
ComboBox2->ItemIndex =
random(ComboBox2->Items->Count);
ComboBox3->ItemIndex =
random(ComboBox3->Items->Count);
>
Запустите программу, нажав клавишу F5, и убедитесь,
что она работает так, как мы того хотели. Изменять
фразу, получающуюся на экране, можно путем выбора
слов в списках или щелчком на кнопке Случайный выбор.
В последнем случае текущие пункты списков выбира¬
ются случайным образом.
Готовая программа в работе
Если выйти из программы щелчком на закрывающей
кнопке, а затем запустить ее снова, последовательность
«случайных фраз» будет другой.
Учим программу запоминать
fnaea 13
Память — великое свойство человечес¬
кого мозга. Она позволяет нам учиться,
размышлять, сравнивать и сопостав¬
лять. Человек обычно принимает реше¬
ния на основе накопленных знаний и опы¬
та. Компьютерные программы тоже
должны принимать решения, но для это¬
го они должны сначала научиться запо¬
минать данные
Как хранятся объекты
Оперативная память компьютера собрана на микросхе¬
мах. 06 устройстве микросхем мы говорить не будем —
просто считайте, что в них есть ячейки, в которых хра¬
нятся электрические заряды.
Хранение — это не просто сваливание разных вещей в
одну большую кучу. Правильное хранение позволяет
не только сохранить что-то нужное, но и потом разыс¬
кать это что-то, когда оно потребуется. Возьмите, к
примеру, эту книгу. Ее можно положить в коробку из-
под обуви и засунуть под кровать. Книга от этого ни¬
чуть не пострадает — когда-нибудь кто-нибудь ее най¬
дет и прочитает.
А вот другой пример. Из книги можно вырвать листы,
засунуть их в ту же коробку и спрятать под ту же кро¬
вать. Такую книгу прочитать будет уже сложнее, но
сделать это все-таки можно, потому что странички в
ней пронумерованы. Мы можем сказать, что этот метод
хранения информации менее удобен, но все-таки при¬
емлем.
157
И наконец, самый варварский пример. Допустим, из
книги вырезали каждую букву и ссыпали все их в одну
коробку. Вроде бы все то же самое, но теперь эту книгу
невозможно прочитать. Может быть, это плохой способ
хранения информации? Он не просто плохой. Это вообще
не способ. Для информации это не хранение, и говорить
о том, хорошее оно или плохое, нелепо.
Хранение любого объекта только тогда считается хра¬
нением, когда к этому объекту обеспечен доступ. Если
этот доступ удобен, например, к книге, то хранение хоро¬
шее. Если он не очень удобен, например, когда книга
разобрана на отдельные листочки, значит, такой метод
хранения не очень хорош, а если доступ невозможен,
то это не хранение.
Что такое адресация
В качестве метода доступа к хранящимся объектам
люди давным-давно придумали метод, который назы¬
вается адресацией. У каждого хранящегося объекта
должен быть адрес — это одно из свойств объекта. Если
книга хранится в коробке под кроватью, ее адрес изве¬
стен «хранителю». Если же она разобрана на странич¬
ки, то ее можно восстановить благодаря тому, что у
каждой странички есть свой уникальный номер — его
тоже можно считать адресом.
Адреса бывают двух типов: числовые и символьные.
Пример числового адреса — порядковый номер, напри¬
мер: Дело № 015383. Кстати, если кроме цифр в адресе
будут буквы, это сути не меняет — такой адрес все равно
считается числовым, просто буквы в нем выполняют
ту же роль, что и цифры. Возьмите для примера номер¬
ной знак автомобиля с787мк77. Это не что иное, как
адрес регистрационной записи в ГИБДД. По нему можно
однозначно установить, кто владелец данного автомо¬
биля, и выяснить, где он живет.
158
А вот еще пример. Возьмите в руки любую денежную
купюру. У нее обязательно есть уникальный номер,
например OB 2144818. Он тоже выполняет функцию
адреса. По нему можно узнать, когда и где эта купюра
была напечатана и чему равен ее номинал.
Компьютеры и другие устройства вычислительной тех¬
ники предпочитают работать с числовыми адресами.
Так, например, чтобы перейти от адреса 0000:0000 к
адресу 0000:0001, надо просто прибавить единичку, а
компьютеры умеют это делать быстро и хорошо.
Другой метод адресации — символьный, например та¬
кой: Россия, Москва, ул. Гагарина, дом 20, кв. 32, Иванову П.Г.
Неважно, что в этом адресе использовано несколько
цифр. Важно, что этот адрес не имеет характера поряд¬
кового номера — он больше похож на имя объекта.
Людям удобнее работать с символьными адресами.
Двойная адресация
Возможно, у читателя возник вопрос: «А какая адре¬
сация лучше — числовая или символьная? » На самом
деле нужна и та и другая, просто они используются в
разных областях жизни. А чтобы не морочить себе голову
над тем, какой адресацией пользоваться, мы очень ча¬
сто не задумываясь используем двойную адресацию, но
при этом обязательно создаем таблицу, в которой запи¬
сано, какому символьному адресу какой числовой адрес
соответствует. Наглядный пример — телефонная книга.
В нашей книге тоже есть такая таблица. Она называ¬
ется содержанием и приведена в конце книги.
Символьный адрес (глава)
Числовой адрес (страница)
Введение
3
Программирование без компьютера
7
Процедуры и функции
17
159
Адресация в компьютере
В компьютере два устройства памяти: жесткий диск и
оперативная память. В оперативной памяти хранятся
работающие программы и их данные, когда компью¬
тер включен, а на жестком диске хранятся неработаю¬
щие программы и данные, когда компьютер выключен.
И в том и в другом устройстве используется двойная
адресация и, конечно, есть таблицы соответствия между
символьным адресом и числовым.
Хранение информации на жестком диске. О файлах и
папках мы уже говорили. Вы уже знаете, что имя файла
выполняет роль его символьного адреса. Оно служит
для удобства человека. На самом деле файлы записаны
в каких-то секторах жесткого диска, у которых имеют¬
ся свои порядковые номера — их можно считать число¬
выми адресами, но людям они не понятны и не инте¬
ресны. А для того чтобы адреса можно было быстро
пересчитать из символьной формы в числовую и наобо¬
рот, на жестком диске имеется таблица размещения
файлов. Ее обслуживанием и всеми пересчетами зани¬
мается операционная система Windows.
Хранение информации в оперативной памяти. Опера¬
тивная память современного компьютера измеряется
десятками мегабайт, то есть она состоит из десятков
миллионов ячеек, хранящих числа. У каждой ячейки
есть свой порядковый номер — считайте, что это ее
числовой адрес.
Когда-то в прошлом, когда оперативная память у ком¬
пьютеров была небольшой (до нескольких тысяч яче¬
ек), люди еще могли следить за тем, какие данные в
каких ячейках хранятся. Они отправляли данные в
память, указывая адреса ячеек, в которые те должны
быть помещены, и, наоборот, вызывали данные из
памяти, указывая нужные числовые адреса.
160
Сегодня так работать уже невозможно: не может чело¬
век держать в голове адреса миллионов ячеек. Поэтому,
как всегда, был предложен символьный подход.
Если нам надо вычислить путь, пройденный пешехо¬
дом за определенное время, мы и путь, и скорость, и
время можем записать символами, например так:
s = v * t;
Где, в каких адресах памяти хранятся величины s, v и t,
нас не волнует. Но это очень волнует компьютер. Ему-то
это знать необходимо. Значит, нужна таблица соответ¬
ствия между символьными адресами и числовыми. Ее
ведет и обслуживает сама система программирования
С++ Builder. Пока мы работаем над программой, мы своим
данным можем давать символьные имена, а когда
программа будет запущена на исполнение, вместо этих
имен будут подставлены конкретные номера ячеек па¬
мяти.
Переменные
Сегодня все языки программирования позволяют вме¬
сто числовых адресов ячеек памяти использовать про¬
извольные имена — они называются переменными.
Каждый школьник знает, что переменная — это имя
прилагательное. И то, что адресное имя называют при¬
лагательным, многих путает. Опытные программисты
к этому привыкли, а молодых людей с незатуманенными
мозгами это шокирует, причем совершенно справедливо.
Впрочем, в жизни еще и не такое бывает. Надо с этим
смириться и понять раз и навсегда, что переменная —
это не прилагательное, а существительное. Это сим¬
вольная форма записи адреса для ячейки или группы
ячеек памяти.
6—1619
161
Слово переменная пришло в программирование из
математики. Математики со времен Ньютона опериру¬
ют такими понятиями, как переменная величина, зави¬
симая переменная, которую еще называют функцией,
и независимая переменная, которую называют также
аргументом.
Формула S = V • t в математике и физике означает, что
переменная величина S (путь) зависит от значений неза¬
висимых переменных величин V (скорости) и t (вре¬
мени).
Поскольку первыми разработчиками языков програм¬
мирования были математики, то они принесли в про¬
граммирование свою терминологию.
Та же самая запись:
S = V* t
в программировании понимается не так, как в матема¬
тике. Это оператор присваивания, дающий команду
«заслать в ячейки памяти, имеющие имя S, результат
произведения того, что было взято из ячеек, имеющих
имена V и t».
Благодаря условным символьным обозначениям про¬
граммисты избавились от необходимости запоминать
числовые адреса ячеек в оперативной памяти. Теперь
за адресами следит система программирования, в кото¬
рой программа создается, а ее автор должен позабо¬
титься лишь о том, чтобы случайно не назвать две раз¬
ных величины одним и тем же именем переменной.
Переменные и идентификаторы
У каждой переменной есть имя и значение. Имя пере¬
менной дает сам программист — оно может быть по¬
чти любым. По имени переменной система программи¬
рования отводит ей одну или несколько ячеек памяти
(это зависит от того, какие данные хранятся в перемен¬
162
ной). Имя выполняет роль адреса, а значение — роль
содержимого. То есть, если мы хотим, чтобы програм¬
ма что-то запомнила, нам надо просто создать перемен¬
ную и присвоить ей то значение, которое мы хотим со¬
хранить до будущих времен.
Имена переменных еще называют идентификаторами.
Мы уже сказали, что они могут быть почти любыми.
Разные языки и системы программирования обычно
вводят небольшие ограничения на то, какие идентифи¬
каторы программист может использовать, а какие —
нет. Например, ни один язык программирования не
позволяет использовать свои так называемые ключе¬
вые слова, которыми записываются операторы этого
языка.
В системе С++ Builder используется язык С++. В нем иден¬
тификатор переменной — это последовательность ла¬
тинских букв, цифр и символов подчеркивания, при¬
чем первый символ не может быть цифрой.
Для примера приведем несколько допустимых иденти¬
фикаторов переменных:
♦ i — самый короткий идентификатор состоит из
одной буквы;
♦ AaAa — буквы могут быть большими или ма¬
ленькими;
♦ a1B2c3 — буквы и цифры могут идти впере¬
межку;
♦ April_2001 — можно использовать символ под¬
черкивания (обычно его применяют вместо про¬
бела для разделения нескольких слов);
♦ 1 —идентификаторможетначинатьсяссим-
вола подчеркивания.
163
Другие символы и пробелы применять нельзя. Длина
идентификатора может быть любой. Ключевые слова,
имеющие в языке С++ особое значение, применять в ка¬
честве идентификаторов нельзя.
Вот такие идентификаторы не годятся:
♦ 2iii — идентификатор не должен начинаться с
цифры;
♦ Вася — русские буквы не годятся, даже если они
похожи на латинские;
♦ No No No — пробелы внутри идентификатора
недопустимы;
♦ U-u-u!? — здесь использованы символы знаков
препинания, которые нельзя вставлять в иден¬
тификатор;
♦ if — это ключевое слово (с ним мы познакомим¬
ся позже), а не идентификатор переменной.
А теперь попробуйте сами определить, какие из следу¬
ющих строк можно использовать в качестве идентифи¬
каторов (ключевых слов среди них нет):
YesNo
СОВЕТ
О!
Y00000Y
3points
L01234567 89012345678901234567890
Agent 007
helllo
ЫпггпЫ
Идентификаторы объектов и свойств
Но мы хотим работать не просто с переменными, а с
объектами. Есть ли разница? Нет. Каждый объект
164
имеет идентификатор. Он совпадает с именем, которое
система С++ Builder дает объекту при создании. Это имя
можно сразу же использовать, не задумываясь о том,
откуда оно взялось.
Например, в наших программах встречались объекты
Label 1 и Button 1. Эти же имена можно использовать для
обращения к объектам из программы.
Но мы помним, что все объекты обладают свойствами.
Если объект — это переменная, то что такое свойство?
Свойство — тоже переменная! Переменная внутри
переменной? Да.
Представьте себе, что переменной-объекту выделена
некая область памяти. Она может быть поделена на
«кусочки», некоторым из которых тоже присвоены
имена. Более того, свойства могут также быть объек¬
тами и иметь свои свойства. Это означает, что отведен¬
ные им участки памяти поделены дальше.
«Точечная» запись
А как в программе указать, что речь идет о свойстве?
С объектами все просто: каждый объект имеет имя
(идентификатор), не такое, как у других объектов. А к
свойствам это не относится — одно и то же свойство есть
у всех однотипных объектов.
С другой стороны, мы и сами хотим указать, к какому
объекту относится такое свойство. В этом случае при¬
меняют записьчерез «стрелочку» имениобъектаииме-
ни свойства, например:
Label1->Caption — свойство Caption объекта Label1.
Как видите, эту запись удобно читать справа налево.
Если свойство само является объектом, то может пона¬
добиться более длинная запись, например:
165
Form->Font->ltalic — свойство Italic свойства Font объекта
Form.
Почему такую запись мы называем «точечной»? Пото¬
му, что в большинстве других языков программирова¬
ния для разделения объекта и его свойства использу¬
ется точка. В языке С++ могут использоваться и точка,
и «стрелочка», но в разных случаях. Пока что можно
просто запомнить, что, когда речь идет о свойствах и
методах объектов, созданных на основе компонентов,
необходимо использовать «стрелочку».
Попробуйте сами записать идентификатор для свой¬
ства Left объекта, на который указывает свойство Parent
объекта Label1.
f?lQBQ 1 4
Типы QQHHblX
Сколько получится, если к трем морков¬
кам прибавить два окуня? Все знают,
что на такой вопрос Нет правильного
ответа. Но если наша программа суме¬
ет задать компьютеру такой вопрос,
то она какой-то onwem получит. Что¬
бы мы не могли сложить груши с пугови¬
цами или умножить букву «Б>> на 2>
любая переменная относится к опреде¬
ленному типу данных
Данные s памяти
Итак, с именами переменных, иначе говоря, с их иден¬
тификаторами, мы разобрались. Не каждый сразу пони¬
мает, что такое переменная в программировании, зато
с идентификаторами переменных все разбираются
очень быстро. Это любимый вопрос школьников на эк¬
заменах по информатике.
Теперь мы погрузимся немножко дальше в микросхе¬
мы компьютера и посмотрим, как хранятся данные в
ячейках памяти, а для этого познакомимся с некото¬
рыми простейшими типами данных.
Вот несколько вопросов, на которые мы сейчас поищем
ответ.
1. Сколько ячеек памяти необходимо, чтобы запом¬
нить число 25?
2. Сколько ячеек необходимо, чтобы запомнить чис¬
ло 525?
3. Сколько ячеек необходимо, чтобы запомнить чис-
ло л = 3,1415926?
167
4. Сколько ячеек необходимо, чтобы запомнить сло¬
во «Компьютер»?
5. Сколько ячеек необходимо, чтобы запомнить
текст романа «Война и мир»?
Биты и байты. Одна простейшая ячейка оперативной
памяти компьютеров называется байтом. Но байт — это
не самая маленькая единица информации — он состоит
из восьми отделений — битов. Биты имеют одно из двух
состояний: электрический заряд либо есть, либо нет.
Если он есть, говорят, что бит включен или установ¬
лен, а если его нет, то говорят, что бит выключен либо
сброшен. Считается, что если заряд есть, то значение
бита равно единице, а если его нет, то оно равно нулю.
Так, например, если надо обнулить всю ячейку памя¬
ти, ее проводники заземляют, и заряды из всех битов
стекают, как говорят, «на землю». А если надо заслать
в ячейку памяти какое-то число, ее проводники под¬
ключаются к проводникам процессора, и заряды из
ячеек процессора копируются в ячейку памяти. Управ¬
ляет всеми этими подключениями и отключениями сам
процессор. В этом деле принимают участие также и
некоторые другие устройства материнской платы ком¬
пьютера.
Что может хранить байт. Скажем сразу: байт может
хранить только состояние своих битов, да и то лишь до
тех пор, пока идет подзарядка ячеек, то есть пока ком¬
пьютер включен. Если же компьютер выключить, то
168
Байт
. Этот бит выключен
Этот бит включен
заряды из ячеек улетучатся за десятые доли секунды.
При этом оперативная память обнулится или, как гово¬
рят, очистится.
Прямо скажем: хранительные способности байта весьма
и весьма скудны. Однако не будем расстраиваться, все-
таки байт не прост: у него целых восемь битов.
Для начала простая задачка: если у бита есть два воз¬
можных состояния, то сколько состояний у двух битов?
Решение лучше всего искать на примере. Как видно из
рисунка, у двух битов возможны четыре разных состо¬
яния.
Теперь следующая задачка: сколько возможно состоя¬
ний у трех битов? Посмотрим на примере. Как видно
из рисунка, у трех битов возможны восемь разных со¬
стояний.
Дальше, наверное, можно не проверять. Вы уже, дол¬
жно быть, поняли, что добавление одного бита увели¬
чивает количество возможных состояний в два раза.
Четыре бита имеют 16 различных состояний, а восемь
битов имеют: 2 x 2 x 2 x 2 x 2 x 2 x 2 x 2 = 256 различных
состояний.
Так что же можно выразить одним байтом? Оказыва¬
ется, им можно выразить 256 различных величин, на¬
пример целых чисел от 0 до 255.
169
0000 0000 = 0
0000 0001 = 1
0000 0010 = 2
0000 0011 = 3
1111 1110 = 254
1111 1111 = 255
Вот и ответ на наш первый вопрос: « Сколько ячеек памя¬
ти надо отвести для хранения числа 25?» Оказывается,
достаточно одной ячейки памяти размером в один байт.
Что можно выразить двумя байтами. Конечно, если бы
компьютер мог работать только с целыми числами, да
еще с такими маленькими (от 0 до 255), то он мало на
что был бы пригоден. Но ведь для хранения чисел мож¬
но использовать не одну ячейку памяти, а пару сосед¬
них ячеек. Тогда у них будет не 8, а 16 бит. Такая еди¬
ница хранения называется словом.
Мы не будем заставлять вас считать, сколько состоя¬
ний могут иметь 16 бит. Поверьте на слово: 65 536.
Значит, парой байтов можно записать любое целое чис¬
ло от 0 до 65 535. Вот вам и ответ на наш второй воп¬
рос: «Сколько ячеек памяти необходимо для хранения
числа 525?» Как видите, одной ячейки недостаточно,
а двух хватает, и даже с большим запасом.
Для целых чисел от нуля до четырех миллиардов
можно использовать четыре соседних ячейки памяти
(32 бита) — это так называемое двойное слово. А если
число еще больше, то можно отдать под его хранение
восемь соседних ячеек (64 бита) — так называемое
учетверенное слово.
Так можно было бы двигаться и дальше, но на практи¬
ке необходимость в целых числах еще большего раз¬
мера не встречается.
170
Как записать действительное число. С целыми числами
мы уже разобрались, но из математики вы знаете, что
числа бывают не только целыми — они могут быть дей¬
ствительными, то есть отрицательными, да к тому же
еще и дробными. Хранение действительных чисел в
компьютере организовано посложнее, чем целых. Мы
его рассматривать не будем, а просто скажем, что для
такого числа обычно отводится 10 соседних ячеек па¬
мяти (10 байтов или 80 бит).
Бывают случаи (в основном у физиков), когда надо под¬
считать что-то с очень высокой точностью — тогда для
хранения действительного числа может отводиться
20 ячеек памяти. Работа с такими числами называется
вычислениями с двойной точностью. Ну и наконец, в
особых случаях, когда нужна еще более высокая точ¬
ность, под одно действительное число может отводиться
40 байтов (страшное расточительство!). На такие вычис¬
ления с учетверенной точностью способны только спе¬
циально написанные программы. Вот мы и ответили
на наш третий вопрос о том, сколько памяти отводится
для запоминания числа 7t — десять соседних ячеек.
Как хранятся буквенные символы. Чтобы не томить
вас исследованиями, сразу скажем, что для хранения
одного символа (неважно какого: буквы, цифры или
знака препинания) достаточно одного байта. Правда,
это не относится к китайским иероглифам.
В самом деле, в русском алфавите всего 33 буквы. Доба¬
вим сюда еще прописные — будет 66. Добавим 26 букв
английского алфавита и столько же прописных — по¬
лучится 118. Можно добавить еще десять разных цифр
от 0 до 9 плюс разные знаки препинания и знаки ариф¬
метических операций — все равно все это легко умеща¬
ется в 256 значений, которые можно выразить одним
байтом.
171
— Так сколько же памяти надо, чтобы запомнить слово
«Компьютер»?
— Ответ прост. Надо посчитать, сколько в этом слове
букв, столько байтов памяти для его запоминания и
потребуется — всего 9 байтов, то есть даже меньше, чем
для запоминания числа к.
— А какими байтами записываются буквы, входящие
в слово «Компьютер?»
— Для русских букв существует несколько различных
систем кодирования. Одни и те же буквы в разных сис¬
темах могут записываться разными кодами. Самые рас¬
пространенные системы кодирования: Windows 1251
и КОИ-8Р (она используется в электронной почте). В сис¬
теме кодирования Windows 1251 это слово выглядит
так:
202 238 236 239 252 254 242 229 240
К о м п ь ю т e p
А в системе КОИ-8Р оно выглядит по другому:
235 207 205 208 216 192 212 197 210
К о м п ь ю т e p
— А как же быть с китайскими иероглифами?
— Поскольку общее число разных иероглифов измеря¬
ется тысячами, то для хранения одного такого символа
одного байта недостаточно — используется пара байтов.
Впрочем, к нашим задачам это не относится.
Как хранятся тексты. Простые тексты — это просто
наборы букв и других печатных символов. Для их хра¬
нения надо столько ячеек памяти, сколько символов в
тексте.
Правда, бывают еще тексты форматированные, то есть
оформленные, например, как текст этой книги. Инфор¬
мация о размере шрифта, его цвете и форме вставляется
172
между текстовыми символами невидимыми кодами —
тогда памяти для размещения текста надо в несколько
раз больше.
— А сколько памяти надо, чтобы поместить в нее роман
«Война и мир»?
— Если текст неформатированный, то надо подсчитать,
сколько в нем символов, включая пробелы и знаки пре¬
пинания — столько байтов и понадобится. Мы этого,
конечно, не делали, но думаем, что достаточно пример¬
но 4-5 мегабайтов (миллионов байтов). Если в вашем
компьютере установлено 64 мегабайта оперативной па¬
мяти, значит, все произведение Льва Толстого могло
бы поместиться в ней 10-15 раз, даже без необходимо¬
сти записи на жесткий диск.
Хранение переменных
А теперь вопрос-ловушка: сколько оперативной памя¬
ти необходимо для хранения переменной Account?
Правильный ответ: «Не знаю». Дело в том, что нам
только известно имя переменной, но мы ничего не зна¬
ем об ее значении. Хранится ведь не имя, а значение.
Имя — это просто адрес. Вот если бы мы знали, что
Account = 5, мы бы сказали, что для хранения значения
этой переменной достаточно одного байта, а если бы
нам сказали, что Account = 5.0, то меньше чем десятью
байтами не обойтись, ведь это число действительное, а
не целое. А если переменная Account представляет собой
сложный объект, то памяти может понадобиться во
много раз больше.
Память для хранения переменных распределяет ком¬
пилятор. После того как программа написана, компи¬
лятор начинает преобразовывать ее в машинный код.
При этом он просматривает исходный текст и находит
173
в нем те переменные, которые мы использовали. Да¬
лее он выделяет им столько ячеек памяти, сколько
нужно, в зависимости от того, какие данные в них со¬
держатся.
Обратите внимание на то, что компилятору совершенно
все равно, какова длина имени переменной. Он на это
не смотрит. Ему важно, к какому типу данных отно¬
сится значение переменной. Если это число целое —
ему будет отведено меньше байтов, чем действитель¬
ному числу.
Например, всем следующим переменным будет отве¬
дено по 10 байтов, вне зависимости от того, что у одних
переменных имя короткое, а у других — длинное:
pi = 3,1415926
r = 5,0
Length of_circle = 2*pi*r
s = pi * r * r
Отсюда надо сделать вывод, что «экономить» на длине
переменных не надо — это бесполезно. Давайте своим
переменным такие длинные имена, какие хотите, лишь
бы вам самим было понятно, что в них хранится.
Как компилятор узнает тип переменной
Итак, во время подготовки машинного кода програм¬
мы компилятор просматривает текст, выискивает в нем
переменные и отводит им места в оперативной памяти.
В общем, он создает таблицу, связывающую символь¬
ный адрес (имя переменной) и числовой адрес (номер
ячейки памяти, в которой будет храниться значение).
Если вам интересно узнать, каким образом он догады¬
вается о том, сколько ячеек памяти необходимо отвес¬
ти под ту или иную переменную, мы сейчас об этом рас¬
скажем.
174
Компиляторы разных языков программирования дей¬
ствуют по-разному. Здесь возможны разные подходы:
щедрый и скупой, демократический и академический.
Начнем с самого скупого, академического. Он появился
в одном из наиболее ранних языков программирования
АЛГОЛ-бО, которым пользовались в основном матема¬
тики. Этот язык требовал, чтобы в самом начале про¬
граммы автор специально указал, к какому типу отно¬
сится каждая из использованных переменных. Как
только автор придумывал новую переменную, ему необ¬
ходимо было вернуться в начало программы и четко
указать, что за данные в ней будут храниться: целые
числа, действительные или еще что-то. Если програм¬
мист забывал это сделать, компилятор сообщал об
ошибке и прекращал работу.
Такой подход требовал от программистов строгой дис¬
циплины. Им приходилось все продумывать заранее.
Они ворчали, но подчинялись, поскольку деваться им
было некуда.
Этот подход унаследовал и язык С++ и, соответственно,
система C++Builder, созданная на его основе. Сегодня те,
кто пишут программы на этом языке, уже не ворчат, а
наоборот, радуются. Дело в том, что современные про¬
граммы столь сложны и длинны, что жесткие требова¬
ния дисциплины со стороны компилятора нередко по¬
могают избегать нелепых и досадных ошибок.
Первым демократичным был язык FORTRAN — за это
его полюбили инженеры. Его компилятор не требовал
указывать, к какому типу относится та или иная пере¬
менная, а вместо этого было предложено оригинальное
решение. Компилятор считал, что все переменные,
идентификаторы которых начинаются с букв I, J, К, L,
M, N, имеют целые значения, а все остальные — дей¬
ствительные.
175
Если инженеру нужны были целые числа, он называл
переменныетак: nomer, metkaHT. п.,аеслидействитель-
ные, то, например, так: radius, dlina.
Многие считают, что все требования компиляторов по
объявлению типов данных, использованных в перемен¬
ных, происходят от скупости. Компилятор не хочет
отдать под хранение переменной ни на один байт боль¬
ше, чем ей на самом деле нужно.
Когда у компьютеров действительно очень мало опера¬
тивной памяти, это оправданно. Но в наши дни даже в
домашнем компьютере установлено намного больше
мегабайтов памяти, чем в крупнейшем вычислитель¬
ном центре 70-х годов. Кажется, что компиляторы могли
бы быть и посговорчивее. Им что, жалко отдать под хра¬
нение наших переменных побольше места, но не застав¬
лять нас думать?
Тем не менее, такие языки, как Pascal и С++, продолжа¬
ют настаивать на том, что каждая переменная должна
быть заранее объявлена. Сегодня это делается не ради
экономии, а именно чтобы заставить программиста
лишний раз подумать.
Считается, что это ему на пользу. Кстати, это одна из
причин, по которой язык Pascal многие считают идеальным
языком программирования для учебных заведений.
Есть и другие основания требовать обязательного опи¬
сания переменных. Представьте себе, что переменная
упоминается в программе двадцать раз и один раз вы
сделали в ее имени опечатку. Если система требует,
чтобы все переменные были объявлены заранее, то,
встретив незнакомую переменную, она тут же начинает
бить тревогу. Вы сразу же узнаете, в какой строке про¬
граммы обнаружена ошибка, и ее исправление может
занять буквально несколько секунд.
176
А теперь представьте себе, что описывать переменные
необязательно. Тогда компилятор, встретив незнако¬
мый идентификатор, просто решит, что это еще одна
переменная. Он отведет для нее место в памяти и будет
считать, что программа правильная. Но на самом-то
деле она неправильная. И вы увидите это, когда про¬
грамма не будет делать того, что должна (или наобо¬
рот, будет делать то, что не должна). И в этом случае
поиск той же самой простейшей опечатки может рас¬
тянуться на много часов, а то и дней.
О больших и маленьких буквах
О том, что русские буквы нельзя использовать в иден¬
тификаторах переменных, в именах объектов, в назва¬
нии свойств объектов (в значениях — можно) и в име¬
нах процедур и функций, мы уже знаем. Но мы пока
упустили из виду, где можно использовать маленькие
(строчные), а где — большие (прописные) буквы.
К этому вопросу разные языки программирования,
разные компиляторы подходят по-разному. Например,
в языке С++ строчные и прописные буквы считаются
различными, а в языках Pascal и Visual Basic — нет. На¬
пример, в языке С++ идентификаторы Big, BIG, big и biG
(придумай еще четыре варианта) считаются разными и
соответствуют разным переменным. Если мы действи¬
тельно захотим использовать такие переменные, нам
придется описать каждую из них по отдельности.
Кстати, аналогичное требование действует и в отноше¬
нии ключевых слов языка. Все ключевые слова, кото¬
рые определены в языке С++, записываются только
строчными буквами. С другой стороны, система С++
Builder определяет множество дополнительных функ¬
ций, объектов, типов данных. Их надо использовать
точно в таком виде, как они определены.
177
Но если строчные и заглавные буквы используются
несогласованно, вперемешку, программу становится
трудно читать. Поэтому каждый программист обычно
вырабатывает привычку применения больших и ма¬
леньких букв. Например, можно сделать первую букву
каждого слова в идентификаторе заглавной: TopLeftCorner
(ЛевыйВерхнийУгол). Заглавные буквы как бы разделя¬
ют слова вместо пробелов.
Объявление переменных
Итак, если мы хотим использовать какую-либо перемен¬
ную в программе на языке С++, эту переменую необ¬
ходимо сначала объявить. При объявлении переменной
надо, во-первых, указать ее идентификатор, а во-вто-
рых, написать, к какому типу данных она относится.
В языке С++ переменные могут быть глобальными и ло¬
кальными. Глобальные переменные доступны в любом
месте программы. Форма и все элементы управления
всегда объявляются как глобальные переменные. В то
же время, переменные не рекомендуется делать гло¬
бальными без особой необходимости, так как это часто
приводит к труднонаходимым ошибкам. Локальные
переменные существуют только внутри одной проце¬
дуры. Онисоздаются, когдаэтапроцедура вызывается,
и уничтожаются, когда ее работа заканчивается.
Как описать эти переменные? В языке С++ такое опи¬
сание не содержит никаких специальных ключевых
слов. Глобальные переменные описываются вне отдель¬
ных подпрограмм. Локальные — внутри соответству¬
ющей процедуры.
Описание переменной начинается с указания ее типа,
за которым идет идентификатор. Локальные перемен¬
ные описываются внутри соответствующей подпро¬
граммы, сразу после открывающей фигурной скобки.
178
Вот, например, как может быть описана переменная i,
в которой должно храниться целое число (тип int):
int i;
Основные типы переменных
Число типов данных, которые могут встретиться в про¬
грамме на языке С++, без преувеличения бесконечно.
Дело в том, что в случае необходимости мы можем опи¬
сывать собственные, уникальные, сколь угодно слож¬
ные типы данных, а также создавать новые объекты.
Но начать хочется с самого простого.
Мы только что познакомились с типом int, который слу¬
жит для описания переменных, хранящих целые чис¬
ла. Этот тип переменных используется чаще всего. Нам
придется использовать такие переменные очень часто.
Другой часто нужный тип данных — это логический
тип bool. Он описывает логические переменные. Логи¬
ческая переменная может принимать всего два значе¬
ния: true (Да) или false (Нет). К этому типу относится ре¬
зультат сравнения двух чисел, например выражение
5 < 3 имеет значение false (Нет).
Как записать в языке С++ слово «Компьютер»? Строки
символов записываются в двойных кавычках "Компью¬
тер". Для работы со строками служит особый тип дан¬
ных. В системе С++ Builder для строковых переменных
имеется специальный тип AnsiString. Переменной этого
типа можно присвоить любую строку. Многие свойства
компонентов относятся к этому типу.
А к какому типу относятся многочисленные объекты,
которые мы создаем, когда пишем программу? Пра¬
вильный ответ: объекты, созданные с помощью разных
компонентов, относятся каждый к своему типу. Но эти
179
типы не определены в самом языке, а заданы системой
С++ Builder.
Названия всех типов системы С++ Builder начинаются с
буквы Т. Например, кнопка(ВиНоп) принадлежит к типу
TButton. Аналогичное соглашение действует и в отноше¬
нии других компонентов.
Преобразование типов
А теперь вернемся к вопросу, с которого начиналась эта
глава. Язык С++ считает правильными только те дей¬
ствия, которые совершаются с объектами одного типа.
Например, если свойство объекта представлено чис¬
лом, то бесполезно и пытаться занести туда строку или
логическое значение. Максимум, чего можно при этом
добиться, — получить сообщение об ошибке.
Благодаря тому, что каждая переменная и свойство
относятся к определенному типу данных, мы защище¬
ны от множества случайных недосмотров. Но иногда
нам нужно именно такое преобразование. Например,
когда мы выводим что-то на экран, речь всегда идет о
строковых данных, но в программе эти данные могут
храниться в виде чисел.
Действительно, преобразовывать число в строку и стро¬
ку в число приходится очень часто. Для этого в системе
С++ Builder имеются специальные стандартные функ¬
ции. Преобразование строки в число выполняет функ¬
ция StrTolnt. Ее параметр — строка, представляющая
собой запись числа. Например, при вычислении
StrTolnt(" 123") получится целое число 123. Для обратно¬
го преобразования служит функция lntToStr. Результа¬
том вычисления выражения lntToStr( 123) будет строка
"123", то есть символы 1, 2 и 3, идущие друг за другом.
180
Управление при помощи таймера
Гггава 15
Многие программы делают что-то,
только когда получают команды. Одна¬
ко компьютерные игры обычно устрое¬
ны по-другому. Если герой игры будет
стоять и ничего не делать, тут же при¬
бегут враги. Поведением врагов управля¬
ет таймер — внутренние часы компью¬
тера. Сейчас мы создадим небольшую
программу, важнейшим объектом кото¬
рой будет системный таймер
Описание игры «Поймай кнопку!»
Мы уже знаем, что в системе С++ Builder есть не только
визуальные компоненты, но и невизуальные. Объекты,
созданные с их помощью, не видны в окне программы —
они работают «за кадром». Таймер — это еще один
невизуальный компонент, который есть в системе С++
Builder.
Таймер не создает элемента управления, но это не ме¬
шает ему иметь свои свойства и события. Он может вза¬
имодействовать с другими объектами программы. Для
таймера главное событие — истечение заданного интер¬
вала времени.
В этой главе мы создадим небольшую игру под назва¬
нием «Поймай кнопку». Командная кнопка будет у нас
беспорядочно прыгать в окне программы. Тому, кто
захочет щелкнуть на ней, придется нелегко. Перепры¬
гивать из одного места в другое кнопка будет по сигна¬
лу, полученному от таймера.
181
Для оформления окна этой программы нам потребуются
три объекта. Главный из них — прыгающая команд¬
ная кнопка. Еще одна кнопка позволит нам выйти из
программы. Объект таймер тоже принадлежит форме.
Он влияет на работу программы, но сам в программе
не виден, а на форме представляется своим заместите¬
лем — значком. Примерный вид окна формы для на¬
шей программы показан на рисунке.
Кнопка
выхода
Таймер
представлен
значком
Окно
программы
Прыгающая
кнопка
Вот что должно у нас получиться
Создаем прыгающую кнопку
Запустите систему С++ Builder и создайте новый проект.
Сохраните его в папке Таймер.
Проектирование формы начинаем с размещения на ней
бегающей кнопки и ее настройки. Выберите на
вкладке Standard (Стандартные) палитры компонен¬
тов компонент Button (Командная кнопка) и нарисуйте
кнопку в верхнем левом углу формы методом протяги¬
вания.
Задайте для нее следующие свойства.
♦ Caption (Заголовок). Введите надпись, которая
будет изображаться на кнопке, например Нажми
меня!
182
♦ TabStop (Доступна по клавише TAB). Это свойство
разрешает выбрать данный элемент управления
клавишей TAB. В этой игре клавиатурой пользо¬
ваться нельзя. Чтобы запретить такое «жульни¬
чество», задайте этому свойству значение false
(Нет).
♦ Visible (Видима). Сначала кнопка должна быть
невидима, чтобы пользователь не знал, где она
находится. Задайте этому свойству значение
false (Нет).
Теперь надо немножко подумать. Наша кнопка должна
перемещаться по окну. Конечно, можно выбирать ее
координаты совершенно случайно. Но давайте посту¬
пим по-другому. Пусть у нас будет девять мест, в кото¬
рых кнопка может появиться. Представьте себе, что на
нашей форме есть девять клеток, внутри которых может
находиться кнопка.
Клетки, по которым будет прыгать наша кнопка
Самое трудное — точно подобрать размеры кнопки и
клеток, чтобы окно выглядело аккуратным. В системе
С++ Builder все размеры задаются в экранных точках —
пикселах.
Зададим для кнопки значение высоты равным 30 —
свойство Height (Высота) — и значение ширины равным
80 — свойство Width (Ширина).
183
Расстояние между разными положениями кнопки дол¬
жно быть больше, чем ширина и высота кнопок. Пусть
оно составляет по горизонтали 100, а по вертикали 50.
Мы не можем сразу же присвоить эти значения каким-
то свойствам, но они понадобятся нам при программи¬
ровании.
Пусть в левом верхнем положении расстояние между
кнопкой и краями формы составляет 10 пикселов. Задай¬
те значение 10 для свойств кнопки Left (Слева) и Тор (Сверху).
Теперь мы можем подсчитать, какой размер должно
иметь окно программы. По горизонтали он составит
10+100+100+80+10=300. По вертикали это будет
10+50+50+30+10=150. Щелкните на форме и задайте
значение 300 для свойства формы ClientWidth (Внутренняя
ширина). По вертикали нужен запас — ведь в окне будет
еще закрывающая кнопка. Поэтому задайте для свой¬
ства ClientHeight (Внутренняя высота) значение 200. Чтобы
размер окна нельзя было изменить в ходе работы про¬
граммы, задайте для свойства BorderStyle (Тип границы)
значение bsSingle (Тонкая).
184
Создаем кнопку для закрытия окна
В этой книге мы обычно пренебрегали созданием необя¬
зательных элементов управления, таких, как кнопка
для закрытия окна. Мы делали это для того, чтобы про¬
грамма получалась проще и короче. Но на этот раз мы
не можем не сделать эту кнопку. Без нее игра станет
совершенно неинтересной.
Почему? А вот почему. Представьте себе нашу програм¬
му в работе без закрывающей кнопки. В ее окне всегда
будет только один видимый элемент — подвижная
кнопка. Следовательно, в ходе работы программы она
всегда будет активной (в таких случаях говорят, что
«она будет иметь фокус ввода»). А когда фокус ввода
попадает на командную кнопку, она обводится черной
рамкой и ее легко можно нажать клавишей ENTER. Вы¬
играть в такой игре было бы слишком просто — доста¬
точно нажать клавишу ENTER в любой момент.
Рамка вокруг кнопки означает, что кнопку можно
нажать с помощью клавиши ENTER
Чтобы лишить пользователя такой возможности лег¬
кой победы, нам нужен дополнительный элемент управ¬
ления, который заберет фокус ввода себе. Например,
им может быть кнопка выхода из программы. Выбе¬
рите компонент Button (Командная кнопка) в палитре
компонентов и нарисуйте кнопку в нижней час¬
ти формы методом протягивания. Задайте для свойства
185
Caption (Заголовок) значение Выход, а для свойства Default
(По умолчанию) задайте значение true (Да) — тогда фокус
ввода всегда будет принадлежать этой кнопке и клавиша
ENTER накрепко будет связана только с ней.
Сразу же зададим размеры и положение этой кнопки:
4 для свойства Left (Слева) — 110;
♦ для свойства Тор (Сверху) — 160;
4 для свойства Width (Ширина) — 80;
4 для свойства Height (Высота) — 30.
Теперь кнопка выхода имеет те же размеры, что и подви¬
жная кнопка, и расположена в центре нижней части окна.
Добавляем и настраиваем таймер
Пришло время добавить и настроить таймер. Нужный
нам компонент располагается на вкладке System (Сис¬
темные) палитры компонентов. Откройте эту вкладку и
щелкните на кнопке Timer (Таймер). Таймер надо распо¬
ложить за пределами кнопок. Этот объект отобра¬
жается на форме в виде значка фиксированного
размера. В окне работающей программы его не видно.
Настраиваем основное свойство таймера
Самое важное свойство таймера — Interval (Интервал). Оно
указывает, когда в следующий раз таймер должен сра¬
ботать. Промежуток времени задается в тысячных до¬
лях секунды — миллисекундах. Пусть наша кнопка
прыгает на новое место каждые полсекунды. Зададим
для этого свойства значение 500.
186
Приступаем к программированию
Попробуйте запустить программу, нажав клавишу F9.
В окне программы окажется единственная кнопка с
надписью Выход, но и она пока не работает.
|iEft Прыгающая кнопка Ht*1 P
Пока таймер не запрограммирован,
наша прыгающая кнопка не видна
Впереди у нас еще много работы. Начнем с того, что
запрограммируем прыжки кнопки по полю формы. Та¬
кие прыжки должны происходить по сигналу, посту¬
пающему от таймера. Сигнал таймера — это событие
Timer, единственное событие, связанное с таймером.
Чтобы создать заготовку процедуры Timer1Timer для об¬
работки этого события в окне кода, дважды щелкните
на значке таймера.
Что надо сделать при обработке этого со¬
бытия? Изменить положение кнопки.
Пусть все положения кнопки перенуме¬
рованы, как показано на рисунке. Нуме¬
рация начинается с нуля — в этом слу¬
чае программа получится проще. Обра¬
тите внимание на то, что если к номеру прибавить чис¬
ло 3, то мы оказываемся в следующей строке, а если
число 1, то в следующем столбце.
Это позволяет вычислить положение кнопки для пози¬
ции с заданным номером так:
187
Buttonl->Top =
10 + 50*(i
/
3);
Buttonl->Left
= 10 + 100*
(i
% 3);
При делении целого числа на целое (i/3) результат так¬
же будет целым числом. Остаток (или дробная часть
частного) отбрасывается. На практике мы обычно при¬
выкли делить все «до конца», и поэтому такой подход
может показаться непривычным. Однако он использу¬
ется в языках программирования довольно часто. Вот
несколько примеров такого деления:
6/3 = 2
7/3 = 2
8/3 = 2
9/3 = 3
10/3 = 3
Знак операции %, скорее всего, вам тоже не знаком. Он
вычисляет остаток от деления одного числа на другое.
Вот несколько примеров:
6 % 3 = 0
7 % 3 = 1
8 % 3 = 2
9 % 3 = 0
Переменная i содержит номер клетки. Ее описание надо
вставить сразу после открывающей фигурной скобки.
С помощью умножения и сложения мы переходим от
номера клетки к положению кнопки на форме:
int i;
Значение переменной i надо выбрать случайным обра¬
зом. Это надо сделать до написанных выше операторов.
Кроме того, надо сделать кнопку видимой. Для этого
напишем еще два оператора.
188
i = random(9);
Buttonl->Visible - true;
Теперь процедура обработки событий таймера готова.
В целом она выглядит так:
void ___fastcall TForml::TimerlTimer
(TObject *Sender)
{
int i;
i = random(9);
Buttonl->Visible = true;
Buttonl->Top = 10 + 50*(i / 3);
Buttonl->Left = 10 + 100*(i % 3);
}
Но так как мы использовали функцию random, в нача¬
ло нашей программы необходимо добавить строку:
#include <stdlib.h>
Программируем прыгающую кнопку
Выберите на форме нашу прыгающую кнопку (объект
Button1). Дважды щелкните на ней — и система С++
Builder создаст процедуру Button1Click для обработки щел¬
чка на прыгающей кнопке. Если такой щелчок произо¬
шел, значит, игра закончилась победой. Чтобы сооб¬
щить об этом, изменим надпись на кнопке. Кроме того,
отключим ее, чтобы на ней больше нельзя было щел¬
кать. Это можно сделать так:
void fastcall TForml::ButtonlClick
(TObject *Sender)
{
Buttonl->Caption = "Готово!";
Buttonl->Enabled = false;
}
189
Этого достаточно? Нет. Таймер продолжает работать,
а значит, не более чем через полсекунды кнопка опять
исчезнет с экрана. Поэтому нам надо таймер отклю¬
чить. Это можно сделать, изменив свойство Enabled
(Включен), которое показывает, включен ли таймер:
Timerl->Enabled = false;
Последние штрихи
Программа уже почти готова, остались последние
штрихи. Во-первых, мы еще не запрограммировали
кнопку Выход. Дважды щелкните на ней и впишите в
текст созданной процедуры единственный оператор:
Close();
Во-вторых, надо сделать так, чтобы случайные числа
не повторялись. Для этого надо создать процедуру обра¬
ботки загрузки формы FormCreate. Добавьте в нее опе¬
ратор:
randomize();
Подведем итоги
Все, наша программа готова. Это уже не такая малень¬
кая программа, как раньше. В ней участвуют четыре
подпрограммы. Посмотрим написанный нами текст в
целом.
190
void fastcall TForml::Button2Click
(TObject *Sender)
Close();
Запустите программу, нажав клавишу F9. Она должна
работать именно так, как мы задумали. Закройте окно
программы и повторите запуск еще раз. Убедитесь, что
путь прыгающей кнопки изменился.
Кстати, полсекунды, отведенные нами на щелчок, —
это короткий срок. Далеко не каждому удастся догнать
кнопку за это время. В этом случае игроку может по¬
мочь то, что кнопка иногда задерживается на одном
месте. Такое случается, когда наш генератор случай¬
191
void fastcall TForml::TimerlTimer
(TObject *Sender)
{
int i;
i = random(9);
Buttonl->Visible = true;
Buttonl->Top = 10 + 50*(i / 3);
Buttonl->Left =10 + 100*(i % 3);
}
void fastcall TForml::ButtonlClick
(TObject *Sender)
{
Buttonl->Caption = "Готово!";
Buttonl->Enabled = false;
Timerl->Enabled = false;
}
void fastcall TForml::FormCreate
(TObject *Sender)
{
randomize();
}
ных чисел, основанный на функции random, выдает два
или более раз подряд одно и то же число.
Мы пока не предприняли никаких мер, чтобы избежать
совпадения старого и нового положений кнопки, но
сделать это можно. Просто мы еще знакомы не со всеми
операторами С++ Builder и не научили программу «ду¬
мать». Кроме того, очень хитрый игрок может не до¬
гонять кнопку, а ждать, пока она сама придет туда,
куда он поместил указатель мыши. С этим тоже можно
побороться, но тогда программа будет намного слож¬
нее. Впрочем, компьютерные игры — это одни из са¬
мых сложных программ. Программист должен быть
изобретательнее самого хитрого игрока.
Домашнее задание
Если у вас есть младший братишка или сестренка, кото¬
рым непросто догнать прыгающую кнопку, попробуйте
изменить игру так, чтобы они могли сами настраивать
ее скорость. Для этого можно создать еще две команд¬
ные кнопки: Медленнее и
Быстрее. Щелчок на одной
из них будет увеличивать
значение свойства Timer1->
Interval, например на 100
миллисекунд, анадругой —
уменьшать его, например в
два раза.
Обратите внимание на то, что уменьшать значение интервала на
100 миллисекунд нельзя, потому что тогда оно может стать рав¬
ным нулю после нескольких щелчков на кнопке Быстрее. Таймер
отключится, и кнопко вообще перестанет прыгать. Именно поэто¬
му мы и предложили уменьшать значение интервала не вычита¬
нием, а делением.
192
fhosQ 16
Учим программу думать
В этой главе мы научим наши програм¬
мы анализировать текущую ситуацию,
сравнивать различные величины и в зави¬
симости от результата принимать ре¬
шения
Искусственный интеллект в компьютерных играх
Вы не знаете, как человек думает? Нет? Мы тоже. Чест¬
но говоря, этого до конца не знают даже те ученые,
которые этим занимаются всю свою жизнь. Загадка че¬
ловеческого мышления — это одна из самых непознан¬
ных тайн природы.
Хорошо, давайте возьмем вопрос попроще, например
такой: «Как человек ходит?» Вы думаете, что он ходит
ногами? Увы, это не совсем так. При ходьбе голова играет
роль не менее важную, чем ноги.
Когда человек идет, у него одна нога стоит на земле, а
вторая находится в воздухе. Стоять в таком положении
невозможно, и человек должен упасть. Но упасть он не
успевает, потому что подставляется другая нога, и
падение начинается заново. В общем, наша обычная ходь¬
ба — это бесконечное падение.
Мы сами того не замечаем, как при каждом шаге наш
мозг получает сигнал о том, что организм падает. В ответ
на этот сигнал мозг принимает решение. Сначала он
решает: падать или не падать. Как правило, падать ему
не хочется — в памяти надежно хранятся неприятные
воспоминания от прежних падений. Потом мозг решает,
что делать: подставить ногу или зацепиться за что-то
7—1619
193
рукой? Если принято решение подставить ногу, встает
вопрос: какую? Обычно подставляется свободная нога,
но если она недоступна (за что-то зацепилась), мозг может
дать команду опорной ноге, и она совершит прыжок.
И наконец, после того как нога выбрана, выбирается
место, куда ее следует поставить.
Каждый шаг, который мы выполняем в жизни, связан
со множеством проверок, сравнений и выборов. Наш
мозг непрерывно обрабатывает ситуацию, сравнивает
ее с той, которая запомнена из предшествующего опы¬
та, и ежесекундно принимает десятки, а может быть,
сотни, если не тысячи решений. Мы над этим не заду¬
мываемся и считаем, что ходим ногами, а на самом деле
даже простая ходьба требует наличия памяти и напря¬
женного мышления.
Компьютер, конечно, работает совсем не так, как мозг
человека, и компьютеры никогда не смогут думать так,
как думает человек. Но программы для компьютеров
составляют люди, и они могут сделать их такими, чтобы
компьютер, работающий по программе, имитировал
мышление путем запоминания, сравнения и выбора.
Такая имитация называется искусственным интел¬
лектом.
В компьютерных играх искусственный интеллект
встречается очень часто. Конечно, в разных «бегал-
ках», «прыгалках» и «стрелялках» он не нужен, но
когда компьютер играет против игрока, им управляют
процедуры искусственного интеллекта. Особенно часто
это встречается в стратегических играх, где искусст¬
венный интеллект управляет войсками и их снабже¬
нием. Блоки искусственного интеллекта обязательно
есть в авиационных имитаторах — они управляют тем,
как противник заходит на цель и уходит от самонаво-
дящихся ракет. Даже в автогонках работает искусст-
194
В компьютерных играх искусственный интеллект управляет
объектами, представляющими противников
венный интеллект — он управляет автомобилями сопер¬
ников, находящимися на трассе, и рассчитывает траек¬
торию движения машин с точностью до сантиметров.
Как компьютер «размышляет»
Когда утром звонит будильник, мы можем встать. Но
можем перевернуться на другой бок и спать дальше.
Решение мы принимаем сами, хотя и не всегда пра¬
вильно. Компьютер такой свободы лишен. Он выпол¬
няет предписанные ему инструкции одну за другой и
не может решить что-либо «сам». Но задач, которые
можно решить, выполняя команды лишь в том поряд¬
ке, как они записаны, очень мало. Для сложных задач
программу приходится делать гибкой.
Допустим, мы написали программу для ведения таб¬
лицы футбольного чемпионата. В нее вводятся только
результаты матчей, а потом она сама должна выяснить,
какая команда выиграла, и подсчитать текущее число
очков. Для этого надо сравнить число голов, забитых
195
командами. В зависимости
от того, кто выиграл, а кто
проиграл, придется действо¬
вать по-разному.
Если первая команда забила
больше голов, она получит
три очка, а вторая команда —
ноль. Если вторая команда
забила больше голов, то три
очка получит она. И нако¬
нец, если не выполнились
оба условия, значит, коман¬
ды забили поровну мячей и
должны получить по одному
очку за ничью.
Как видите, чтобы програм¬
ма могла справиться с такой
задачей, ей придется зани¬
маться проверками условий
и выполнять различные дей¬
ствия, в зависимости от того,
соблюдены эти условия или
нет.
Все языки программирова¬
ния имеют специальные опе¬
раторы для того, чтобы про¬
грамма могла разветвляться
и в зависимости от того, вы¬
полняется заданное условие
или нет, работать по-разно-
му. Такой оператор, вызыва¬
ющий ветвление програм¬
мы, называется условным
оператором.
196
Условие
Главная часть условного оператора — это условие.
Условие — это логическое выражение со значением true
(Да) или false (Нет). Условие истинно, если его значение —
true (Да), и ложно в противном случае.
В условие входят два выражения, соединенных знаком
равенства или неравенства. Всего таких знаков шесть.
Они приведены в таблице.
<
меньше
>
больше
==
равно
й
не равно
<=
меньше или равно
>=
больше или равно
Как вы видите, некоторые знаки записываются двумя
символами, но они все равно считаются единым целым.
vV^/ Обратите вниллание на то, что условие равенства записывается
mm двумя последовательными знаками равенства. Один знак равен¬
ства определяет оператор присваивания. Пропуск второго знака
равенства в языке С++ нередко порождает очень неприятные
труднонаходимые ошибки.
Смысл этих знаков не совсем тот, что в математике.
Если в учебнике математики написано x < 5, обычно
имеется в виду, что значения переменной x должны
быть меньше пяти.
Если в языке С++ написано, что x < 5, то надо помнить,
что x — это переменная (адрес), в которой уже хранится
какое-то значение. Мы не утверждаем, что оно мень¬
ше пяти, а только проверяем этот факт. Если значение
действительно меньше 5, то все выражение получает
значение true (Да), в противном случае — значение false
(Нет).
197
В математике мы требуем выполнения условия. В языках програм¬
мирования, в том числе и в С++, мы только проверяем, истинно
оно или нет.
Простейший условный оперотор
Пусть нам нужно увеличить значение переменной x на
единицу, если ее текущее значение меньше 5. Это можно
сделать так:
Ключевое
слово
Исполнимый
оператор
Что все это означает? Ключевое слово if (в переводе —
если) стоит в начале условного оператора. Сразу после
него в скобках идет условие. После закрывающей скоб¬
ки стоит оператор, который надо выполнить.
Если условие истинно, оператор выполняется, а если
ложно — пропускается. Вот как зависит результат вы¬
полнения условного оператора от значения переменной.
Первоначальное
значение
переменной x
Значение
условия
Оператор
x = x + 1
Итоговое
значение
переменной x
3
true
Выполняется
4
4
true
Выполняется
5
5
false
Пропускается
5
6
false
Пропускается
6
Многострочная запись
В зависимости от справедливости условия оператор
выполняется или пропускается. А как быть, если таких
198
. Условие
операторов несколько? Допустим, нам надо увеличить
на единицу сразу две переменные: x и у, если значение
переменной x меньше 5.
Язык С++ разрешает записать после условия только
один оператор. Но этот оператор может быть состав¬
ным. Группа операторов, стоящая между открываю¬
щей и закрывающей фигурными скобками, рассматри¬
вается как единый оператор. Иногда фигурные скобки
называют «операторными». В этом случае наш опера¬
тор будет выглядеть так:
if (x < 5) {
x = x + 1;
у = у + 1;
}
Обратите внимание на то, что после закрывающей фи¬
гурной скобки не требуется (а иногда и нельзя) ставить
точку с запятой.
Если условие истинно, то выполняются операторы при¬
сваивания; если оно ложно, то управление передается
на оператор, следующий за закрывающей фигурной
скобкой.
И ga, и нет
Часто какие-то действия надо выполнить, если усло¬
вие ложно. В этом случае незачем проводить две про¬
верки. Можно обойтись одной, хотя и более сложной
записью условного оператора. Пусть, например, пере¬
менную x надо увеличить на единицу, если ее значение
меньше 5, а в противном случае — наоборот, умень¬
шить. Это записывается так:
if (x < 5) x = x + 1;
else x = x - 1;
199
Если условие истинно, выполняется оператор, который
идет сразу после условия. Если условие ложно, то вы¬
полняется оператор, идущий после ключевого слова else.
В нашем примере в каждой из этих областей стоит по
одному оператору. Если их должно быть несколько, не¬
обходимо использовать фигурные операторные скобки.
В данном случае перед ключевым словом else стоит точ¬
ка с запятой. Если при истинности условия надо вы¬
полнить несколько операторов, то ставить точку с за¬
пятой между закрывающей фигурной скобкой и
оператором else нельзя.
Цепочка условных операторов
Мы уже видели три способа записи условных операто¬
ров, но и это еще не все. Пусть, если значение перемен¬
ной x меньше 5, его надо увеличить на 1, а если оно лежит
в интервале от 5 до 10, то его надо умножить на 2. Мы
уже знаем, как это записать:
if (x < 5) x = x + 1;
else if (x < 10)
x = x * 2;
Здесь один условный оператор вложен внутрь другого.
Такая запись совершенно правильна и довольно нагляд¬
на. Принято записывать оператор if в той же строке, что
и оператор else, так как это делает структуру программы
более понятной.
Такого рода конструкцию часто делают довольно длин¬
ной. При такой записи может быть выполнен только
один из операторов, входящих в такую сложную услов¬
ную цепочку. Например, показанный далее условный
оператор выполняет разные действия в зависимости
от значения переменной x:
200
Оператор выбора
Последний пример имеет еще одну особенность — мы
проверяем только значение переменной x. В таком слу¬
чае можно использовать оператор выбора case. Он обес¬
печивает еще более наглядную запись. Давайте сначала
перепишем этот пример в виде оператора выбора, а по¬
том обсудим его.
201
if (x == 1)
x = x + 1 ;
else if (x == 2)
x = x * 2;
else if (x < 5)
x = x + 3;
else if (x < 10)
x = x * 3;
else
x а 10;
switch (x) {
case 1:
x = x + 1;
break;
case 2:
x = x * 2;
break;
case 3:
case 4:
x = x + 3;
break;
case 5:
case 6:
case 7:
case 8:
case 9:
x = x * 3;
break;
default:
x = 10;
}
Если переменная x содержит целое положительное число,
то этот оператор делает в точности то же, что и преды¬
дущий. Начинается он с оператора switch (...), в котором
указывается выражение, подлежащее проверке. В на¬
шем случае это переменная x. Далее может быть указа¬
но произвольное число операторов case, содержащих
некоторое значение. После значения должно идти дво¬
еточие.
Проверки производятся последовательно. Как только
нужное значение найдено, управление передается на
оператор, идущий после соответствующего оператора
саэе.Последующие операторы выполняются подряд (до¬
полнительные пункты case игнорируются).
Но чаще всего, как и в нашем примере, в каждом из
случаев требуется выполнить свои операторы. В этом
случае необходимо добавить после них оператор:
break;
При этом выполнение оператора выбора будет завер¬
шено, а управление передается на оператор, идущий
после закрывающей фигурной скобки.
Если текущее значение проверяемого выражения найти
не удалось, выполняется оператор, стоящий после клю¬
чевого слова default. Как и в обычном условном опера¬
торе, эта часть оператора выбора может отсутствовать.
Вот теперь мы познакомились со всеми видами услов¬
ных операторов (хотя и не со всеми способами измене¬
ния порядка выполнения команд) и можем использо¬
вать их в своих программах.
202
Глава 17
Таблица умножения
Компьютеры предназначены для авто¬
матизации математических вычисле¬
ний. Таблица умножения, которую мы
сейчас запрограммируем, для компью¬
тера — очень проспит задача. Но мы ее
усложним. Сомножители, участвующие
в операции, не вводятся с клавиатуры, а
выбираются графическим способом — с
помощью элемента управления, кото¬
рый называется движком
Описание программы
Движок — это новый для нас элемент управления. Он
состоит из шкалы и движущегося по ней бегунка. Бегу¬
нок передвигают методом перетаскивания или при
помощи клавиш управления курсором. Для двух
сомножителей нам потребуются два движка. Первым
движком выбирается первый сомножитель, а вторым —
второй сомножитель. При этом значение, установлен¬
ное движком, можно увидеть в поле, которое сделано с
помощью объекта Надпись.
Значение произведения тоже представлено в виде над¬
писи, но его мы еще особо выделим рамкой. Для пере¬
хода от таблицы умножения к таблице квадратов и об¬
ратно применим специальный флажок. Когда он
включен, второй движок становится ненужным, ведь
квадрат числа — это произведение двух одинаковых
чисел, а их можно задать и одним движком.
Общий вид окна программы должен получиться при¬
мерно такой, как показано на рисунке.
203
Вот что должно у нас получиться
Размещаем движки
Запустим систему С++ Builder и создадим новый проект.
Сохраним его в папке Умножение. Значок TrackBar
(Движок) для создания движков располагается на
панели Win32 палитры компонентов.
Щелкните на этом значке, а затем нарисуйте движок
на форме методом протЯгивания. Движок — довольно
крупный объект и занимает много места. Подобрать
размер области движка можно с помощью маркеров из¬
менения размеров. Затем поместите на форму второй
движок прямо под первым.
Форма с движками
204
Движки /
Флажок
переключения
режима /
Окно программы
Значения
_сомножителей
Значение
произведения
Настраиваем движки
Пусть наша графическая таблица умножения будет
работать только с однозначными и двузначными чис¬
лами. То есть значения сомножителей могут изменяться
от 2 до 99. Нам надо так настроить свойства движков,
чтобы ими было удобно пользоваться. Оба движка дол¬
жны работать совершенно одинаково, так что их свой¬
ствам можно задать одинаковые значения. Поочередно
выбрав движки, изменим с помощью Инспектора объек¬
тов следующие их свойства.
♦ Min (Минимум). Минимальное значениедвижка.
Оно достигается, когда бегунок находится на
левом краю полосы. Зададим здесь значение 2.
♦ Мах (Максимум). Максимальное значение движ¬
ка — бегунок на правом краю полосы. Зададим
для этого свойства значение 99.
♦ Position (Положение). Это свойство задает текущее
положение бегунка. При движении оно изме¬
няется автоматически. С его помощью удобно
задать начальное положение бегунка. Пусть
сначала бегунок находится в крайнем левом по¬
ложении. Укажем для свойства значение 2.
♦ LineSize (Малое изменение). Бегунок не обязательно
перетаскивать с помощью мыши. Его можно дви¬
гать и курсорными клавишами: ВЛЕВО и ВПРАВО,
ВВЕРХ и ВНИЗ. При этом значение, заданное
движком, изменяется на величину, заданную
этим свойством. Здесь надо задать минималь¬
ное значение: 1.
♦ PageSize (Постраничное изменение). Бегунок также
можно двигать клавишами PAGE UP и PAGE DOWN.
Тот же эффект дает щелчок левой кнопкой
мыши на полосе движка рядом с бегунком. Зна¬
205
чение движка при этом изменяется на величи¬
ну, заданную этим свойством. Давайте зададим
здесь значение 7.
♦ Frequency (Частота засечек). Это свойство указы¬
вает, как плотно будут располагаться засечки
на полосе движка. Засечки помогают следить
за положением бегунка. Давайте укажем здесь
число 7, такое же, как значение свойства PageSize
(Постраничное изменение). В этом случае по щелч¬
ку на полосе бегунок будет перепрыгивать от од¬
ной засечки к следующей.
Вид движка после настройки частоты засечек
Для второго движка все настройки задайте точно таки¬
ми же.
Добавляем рамку
Заключим произведение в рамку. Рамка — это новый
для нас элемент управления, до сих пор мы таким ком¬
понентом не пользовались. Рамка может иметь свой за¬
головок и окружать группу других элементов управ¬
ления. Мы добавляем рамку просто для красоты.
Для этого служит компонент GroupBox (Рамка). Вы¬
берите его на вкладке Standard (Стандартные) палит¬
ры компонентов и нарисуйте рамку на форме методом
протягивания. Можно сразу поместить ее ниже и пра¬
вее движков. Позже мы уточним положение рамки.
Заголовок рамки, принятый по умолчанию, нам не под¬
ходит. Измените свойство Caption (Заголовок). Пусть над
рамкой будет написано Произведение. Заголовок рамки
всегда находится возле ее левого верхнего угла. Изме¬
нить его положение нельзя.
206
Добавляем надписи
Надписи, как мы знаем, создаются при помощи
компонента Label (Надпись). Нам потребуются три
надписи: две для сомножителей и одна для произведе¬
ния. Выберите компонент Label (Надпись) и нарисуйте
надпись на форме методом протягивания. Первую над¬
пись надо разместить справа от верхнего движка.
Остальные надписи добавляются на форму точно так
же. Вторую надпись поместите рядом со вторым движ¬
ком, а третью расположите внутри заготовленной рамки.
Чтобы числа выглядели красиво, выровняйте правые
границы надписей по одной вертикальной линии.
Настраиваем надписи
Начнем настройку созданных объектов. Числа, кото¬
рые должны отображаться в надписях, могут быть одно¬
значными, двузначными, трехзначными и четырех¬
значными. Хотелось бы, чтобы отведенное им место не
зависело от длины числа. Для этого надо, чтобы значе¬
ние свойства AutoSize (Автоподбор) для всех трех надпи¬
сей было равно false (Нет).
Поочередно выбрав каждую из надпи¬
сей, задайте нужное значение с помо¬
щью раскрывающегося списка.
Уточните с помощью маркеров размеры каждой над¬
писи. Еще нам надо изменить их текст. Так как наши
движки установлены в крайнее левое положение, на¬
чальные значения множителей равны 2, а начальное
значение произведения равно, соответственно, 4. Вве¬
дем нужные значения в свойство Caption (Заголовок) каж¬
дой из надписей.
Форма все еще не очень красива, так как наши числа
примыкают не к правому, а к левому краю области над¬
писи. Но это легко исправить, изменив значение свой-
207
ства Alignment (Выравнивание) для объек¬
тов-надписей. Задайте для всех трех
надписей значение taRightJustify (Выравни¬
вание по правому краю).
Добавляем и настраиваем флажок
Выберите компонент CheckBox (Флажок) на панели компо¬
нентов и нарисуйте флажок под движками. В получен¬
ном объекте нас устраивает все, кроме подписи. Поэто¬
му измените свойство Caption (Заголовок) — введите
подпись Квадрат. Можно также изменить значе- I
ние свойства Alignment (Выравнивание). Если выбрать зна¬
чение taLeftJustify (Выравнивание по левому краю), то метка
флажка окажется не справа, а слева от подписи.
Теперь форма готова, и мы можем приступать к про¬
граммированию.
Готовая форма для программы
Программируем работу движков
Движки в этой программе — это основные элементы
управления. Для них надо предусмотреть два режима
работы. Когда флажок сброшен, движки работают неза¬
висимо. Однако если флажок установлен, то сомножи¬
тели должны совпадать, а это значит, что оба движка
должны всегда иметь одно и то же значение, а их бе¬
гунки — одно и то же положение.
208
Первый режим (с выключенным флажком) кажется
более простым, так что давайте сначала запрограмми¬
руем его.
Движок — это как бы счетчик, но не кнопочный, а гра¬
фический. Его самое важное свойство — текущее поло¬
жение. При этом совершенно неважно, как было изме¬
нено состояние движка: перетаскиванием бегунка или
щелчком на полосе, а может быть, клавишами ВПРАВО —
ВЛЕВО или PAGE UP - PAGE DOWN.
Важно только новое значение. Поэтому нам надо обра¬
ботать событие Change (Изменение). Оно возникает вся¬
кий раз, когда положение бегунка меняется.
Выберите верхний движок на форме. В окне Инспектора
объектов выберите вкладку Events (События). Дважды щелк¬
ните напротив события OnChange (При изменении).
Наш объект-движок называется TrackBar1. Система С++
Builder создаст процедуру TrackBar1Change для обработки
этого события.
Наша программа должна менять только надписи, но не
надо забывать, что если поменяется хотя бы один из
сомножителей, то изменится и произведение. Так, если
поменяется положение первого бегунка, то изменять
надо свойстванадписей Label1->Caption и Label3->Caption.
Аналогично, если изменится положение второго бегун¬
ка, то изменять надо как свойство Label2->Caption, так и
свойство Label3->Caption.
Все это нам знакомо, но здесь есть одна трудность: над¬
пись содержит строку текста, а положение движка — это
число. Это не страшно — преобразовать число в строку
может стандартная функция lntToStr.
Итак, нам надо изменить две надписи, а значит, доба¬
вить два оператора:
8—1619
209
Для обработки изменения второго движка надо создать
аналогичную процедуру-обработчик. Выберите в окне
формы движок TrackBar2. С помощью Инспектора объектов
создайте обработчик для события OnChange (При изме¬
нении).
Нам нужно сделать почти то же, что и раньше. Разница
в том, что мы меняем содержимое другой надписи, ис¬
ходя из значения другого движка. Вот как выглядят
нужные операторы:
Label2->Caption = IntToStr(TrackBar2->Position);
Label3->Caption =
IntToStr(TrackBarl->Position *
TrackBar2->Position);
Проверяем программу
Самое важное мы уже сделали. Программу можно за¬
пустить, нажав клавишу F9. Таблица умножения рабо¬
тает — вычисляет произведение двух целых чисел.
Более того, мы уже можем устанавливать и сбрасывать
флажок — это его базовая функция. Однако никакого
эффекта на работу программы это пока не оказывает.
Эти операторы
добавили мы сами
210
*Lvoid fastcall TForml::TrackBarlChange
(TObject *Sender)
- {
Labell->Caption = IntToStr(TrackBarl->Position);
Label3->Caption =
IntToStr(TrackBarl->Position *
_ TrackBar2->Position);
Эти операторы
ввела система
Флажок надо обработать особо. Сейчас мы этим и зай¬
мемся. Закройте окно программы щелчком на закры¬
вающей кнопке.
Программируем влияние флажка
Если флажок установлен, бегунки должны всегда
иметь одинаковые положения. При этом какой бы бегу¬
нок ни двигался, второй должен двигаться точно так
же (в таких случаях говорят, что они должны двигаться
синхронно). Это значит, что мы должны добавить про¬
цедуру обработки щелчка на флажке, а также изменить
процедуры обработки движков с учетов влияния флажка.
Выберите в раскрывающемся списке в верхней части
окна Инспектора объектов объект CheckBox 1 (это и есть фла¬
жок). С помощью вкладки Events (События) создайте про¬
цедуру обработки щелчка CheckBox1Click. Привести
движки к одинаковому виду можно, просто задав поло¬
жение второго движка таким же, как положение пер¬
вого. Для этого нужен оператор:
TrackBar2->Position = TrackBarl->Position;
Событие Click (Щелчок) происходит как при установке,
так и при сбросе флажка. Но в этом нет ничего страш¬
ного, так как при сбросе флажка значения движков
уже равны между собой и этот оператор ничего не ис¬
портит.
fl почему мы не меняем содержимое надписей? Потому что собы¬
тие Chonge (Изменение) произойдет при любом изменении зна¬
чения движка, в том числе программном. У нас уже есть обработ¬
чик этого события — он и выполнит всю работу.
Эта процедура готова. Теперь надо внести изменения в
обработчики события Change (Изменение) для движков.
Эти процедуры находятся рядом в окне кода.
211
Выяснить, установлен флажок или сброшен, можно с
помощью свойства Checked (Установлен). Оно содержит
логическое значение: если флажок установлен — true
(Да) , a если сброшен — false (Нет). Мы уже знаем, что
для исполнения проверок в программах используется
условный оператор:
i f (CheckBoxl->Checked)
Если флажок установлен, приводим второй движок к
значению первого:
TrackBar2~>Position = TrackBarl->Position;
Такие же операторы надо вставить и во вторую проце-
ДУРУ обработки. Единственное отличие состоит в том,
что теперь первый движок приводится в соответствие
со вторым:
if (CheckBoxl->Checked)
TrackBarl->Position
TrackBar2-> Pos i tion;
Программа готова
Все! Программа готова. Запустите ее, нажав клавишу
F9. Проверьте, как работают движки и как влияет на
работу программы установка и сброс флажка. Испытайте
разные способы управления движками.
Если флажок сброшен, движки работают по отдельности,
а если он установлен, то их значения всегда одинаковы
212
А теперь давайте посмотрим полный текст программы.
void fastcall TForml::TrackBarlChange
(TObject *Sender)
{
Labell->Caption = IntToStr(TrackBarl->Position);
Label3->Caption =
IntToStr (TrackBarl->Position
* TrackBar2->Position);
i f (CheckBoxl->Checked)
TrackBar2->Position = TrackBarl->Position;
}
void fastcall TForml::TrackBar2Change
(TObject *Sender)
{
Label2->Caption = IntToStr(TrackBar2->Position);
Label3~>Caption =
IntToStr(TrackBarl->Position *
TrackBar2->Position);
if (CheckBoxl->Checked)
TrackBarl->Position = TrackBar2->Position;
}
void fastcall TForml::CheckBoxlClick
(TObject *Sender)
{
TrackBar2->Position = TrackBarl->Position;
}
Сомосгоятельноя робота
1. Подумайте, что можно изменить в свойствах
объектов, чтобы программа могла находить про-
213
изведения не только двузначных, но и трехзнач¬
ных чисел, например от 2 до 199.
2. Подумайте, что надо изменить в коде программы
и как это сделать, чтобы вместо произведения двух
чисел наша программа вычисляла их сумму.
3. Подумайте, что надо изменить в свойствах объек¬
тов, если программа будет вычислять сумму вме¬
сто произведения.
Глава 18
Движение no координатам
В этой главе мы научимся использовать
полосы прокрутки в своих программах.
Обычно их применяют для прокрутки
содержимого, которое не помещается в
окне. Но программист может их ис¬
пользовать и для каких-то иных целей
Описание программы
Полоса прокрутки — довольно сложный элемент уп¬
равления. Он содержит:
♦ собственно полосу прокрутки;
♦ ползунок, который движется по ней;
♦ две концевые кнопки со стрелками.
Окно программы Блокнот с полосами прокрутки
Если щелкнуть на одной из концевых кнопок, содер¬
жимое окна прокручивается на одну «строку». Если же
щелкнуть на полосе прокрутки рядом с ползунком, про¬
215
_ Ползунок
■ Полоса
прокрутки
Концевые кнопки
изводится прокрутка на одну «страницу». Перетащив
ползунок вдоль полосы прокрутки, можно быстро пе¬
рейти в нужное место.
Слова «строка» и «страница» не случайно взяты в кавычки. Это
понятия условные. Смысл вкладывается в них, когда мы пишем про¬
грамму. Например, в программе Блокнот «строка» — это действи¬
тельно строка текста, а размер «страницы» соответствует высоте
рабочей области окна. Но в другой программе смысл этих понятий
может бьггь другим.
Мы применим полосы прокрутки для перемещения фи¬
гур по окну программы. Вертикальная полоса будет
управлять движением по вертикали, горизонтальная —
по горизонтали. Чтобы было интереснее, у нас будет две
фигуры. Наводя на них указатель мыши, можно выби¬
рать, какая из фигур связана с полосами прокрутки.
В этом проекте мы используем четыре основных объек¬
та: две полосы прокрутки и две фигуры. Но кроме них
нам потребуются дополнительные объекты, с помощью
которых мы ограничим область движения фигур в
окне. Примерный вид окна готовой программы пока¬
зан на рисунке.
Вертикальная
полоса
прокрутки
Горизонтальная
полоса
прокрутки
Подвижные
фигуры
Область
движения
Bom что должно у нас получиться
216
Окно
программы
Определяем область движения
Запустим систему С++ Builder и создадим новый проект.
Сохраним его в папке Координаты.
Область движения фигур ограничим с помощью панели.
На вкладке Standard (Стандартные) палитры компо¬
нентов выберите компонент Panel (Панель) и нари¬
суйте на форме квадратную панель методом протяги¬
вания. Поместите ее ближе к левому верхнему углу
формы, но так, чтобы между нею и границами формы
оставались зазоры слева и сверху. В них мы впослед¬
ствии поместим полосы прокрутки.
Сразу же зададим размеры панели с помощью свойств
Height (Высота) и Width (Ширина). Задайте для этих свойств
значение 161. Нечетное число выбирать предпочти¬
тельнее — так легче определить координаты середины
рамки. Нужная нам область теперь ограничена види¬
мой панелью.
В центре панели располагается подпись — текст Panel1.
Она нам не нужна, поэтому оставьте значение свойства
Caption (Заголовок) пустым.
Добавляем полосы прокрутки
В системе С++ Builder горизонтальная и вертикальная по¬
лосы прокрутки задаются одним и тем же компонен¬
том. Начнем с горизонтальной полосы. Выбери¬
те в палитре компонентов значок Scrollbar (Полоса про¬
крутки) и нарисуйте полосу прокрутки над рамкой методом
протягивания мыши. Если полоса легла не совсем точ¬
но, перетащите ее в нужное место. Ширина полосы про¬
крутки должна быть такой же, как размер рамки. Если
надо, измените свойство Width (Ширина).
Далее нам нужна вертикальная полоса прокрутки. Она
создается точно так же, но при этом получается еще
одна горизонтальная полоса прокрутки. В этом нет ни¬
217
чего страшного. Перейдите в окно Инспектора объектов,
найдите свойство Kind (Вид) и выберите для него в рас¬
крывающемся списке значение sbVertical. Полоса про¬
крутки повернется и станет вертикальной. Расположите
ее слева от рамки и настройте высоту и ширину.
Форма с панелью и полосами прокрутки
Настраиваем полосы прокрутки
Полосы прокрутки надо настроить. Нам придется изме¬
нить довольно много их свойств. Начнем с теоретической
подготовки. Размер нашей панели составляет 161 пик¬
сел, но эта величина учитывает и границы панели. Чтобы
фигура не «наезжала» на эти границы, предусмотрим
отступ в 5 пикселов от краев.
Пусть наши подвижные фигуры имеют размер в 11 пик¬
селов. Мы управляем положением их левого верхнего
угла. Минимальное значение координат может быть
равно 5, а максимальное: 161 - 11 - 5 = 145.
Начнем с настройки горизонтальной полосы прокрутки.
Выберем ее щелчком. С помощью Инспектора объектов
надо изменить такие свойства:
♦ Min (Минимум). Минимальное значение для по¬
лосы прокрутки, когда ползунок находится на
левом краю. Зададим здесь число 5.
218
♦ Мах (Максимум). Максимальное значение для
полосы прокрутки, когда ползунок находится
на правом краю. Зададим здесь число 145.
♦ Position (Положение). Текущее положение ползунка
будет меняться в ходе работы полосы прокрутки.
Сначала здесь задается начальное положение
ползунка. Поместим ползунок в середину полосы.
Нужное значение равно 76. Ползунок тут же
займет среднее положение.
Положение ползунка на полосе прокрутки определяется
свойством Position (Положение )
♦ SmallChange (Малое изменение). Сдвиг ползунка
при «построчном» листании. Зададим здесьзна-
чение 2. Можно было бы разрешить и движе¬
ние только на один пиксел, но оно будет мало¬
заметным.
♦ LargeChange (Большое изменение). Сдвиг ползунка
при «постраничном» листании. Здесь разумно
задать значение 20.
Теперь надо настроить вертикальную полосу прокрутки.
Выберите ее на форме. Задайте для перечисленных
свойств те же значения, что были введены для гори¬
зонтальной полосы.
219
&
Добавляем подвижные фигуры
Мы знаем, что добавленные фигуры будут подвижными,
а система С++ Builder — пока что нет. Эти операции мы
запрограммируем потом. Начнем пока просто с изобра¬
жения фигур.
Выберите в палитре компонентов вкладку Additional (До¬
полнительные). Щелкните на значке Shape (Фигура)
и нарисуйте фигуру на панели методом протяги¬
вания. Заметьте, мы пока не говорим о том, что это за
фигура. Это может быть круг, квадрат или еще что-то.
Форма фигуры — это одно из ее свойств, а мы их еще
не настраивали.
Приступаем к настройке свойств фигуры. Зададим ее
размеры — свойства Height (Высота) и Width (Ширина) —
равными 11. Будем считать, что первая фигура — актив¬
ная. Ее положение нужно согласовать с положениями
ползунков на полосах прокрутки. Для этого надо задать
значение 76 для свойств Left (Слева) и Тор (Сверху). Фигура
переместится в центр рамки.
Настроим форму фигуры. Для свойства
Shape (Форма) выберите в раскрываю¬
щемся списке значение stCircle (Круг).
Наконец, зададим цвет фигуры. Развер¬
ните сложное свойство Brush (Кисть) и
выберите для подсвойства Color (Цвет) значение clAqua
(голубой цвет).
Создадим вторую фигуру. Ее свойства должны быть
почти такими же, как у первой. Поэтому проще всего
создать копию фигуры с помощью буфера обмена. Убе¬
дитесь, что первая фигура все еще выбрана, и нажмите
клавиши CTRL + С. Вставить фигуру надо на панель, а
не просто на форму. Поэтому выберите панель щелч¬
ком на ней и нажмите комбинацию клавиш CTRL + V.
220
Система С++ Builder создаст копию объек¬
та и автоматически присвоит ему новое
имя — Shape2. Перетащите ее на любое
место панели.
Кроме того, давайте изменим цвет и
форму созданного объекта, чтобы не пу¬
тать фигуры на экране. Выберите для свойства Brush->
Color (Цвет кисти) значение clFuchsia (фиолетовый цвет), а
в качестве формы задайте значение stSquare (Квадрат) для
свойства Shape (Форма).
Дополнительные переменные
Мы поместили на форму все нужные объекты и можем
приступать к программированию. Наша программа
должна выполнять два основных действия: выбор
фигуры при наведении мыши и перемещение текущей
фигуры с помощью полос прокрутки.
Мы заговорили про какую-то «текущую» фигуру. Это
одна из двух наших фигур. Но откуда программе знать,
какая из фигур текущая? Мы должны найти способ
сообщить ей об этом. Для этого введем дополнительную
переменную. Так как у нас всего две фигуры, доста¬
точно создать логическую переменную. Назовем ее num.
Если она имеет значение true (Да), то текущей считается
первая фигура. Значению false (Нет) соответствует вто¬
рая фигура. Эта переменная должна быть доступна во
всех процедурах. Поэтому ее необходимо описать в
самом начале области кода.
Переключитесь на окно кода и прокрутите его вверх,
пока не увидите описание переменной формы (TForm 1
*Form1;). Введите после него следующее описание пе¬
ременной:
bool num;
Панель с
фигурами
221
Оно объявляет программе, что переменная num явля¬
ется логической (boolean).
При загрузке формы надо присвоить этой переменной
начальное значение. Щелкните на форме вне располо¬
женных на ней объектов. Перейдите в окно Инспектора
объектов, выберите вкладку Events (События) и дважды
щелкните против события OnCreate (При создании). Сис¬
тема С++ Builder создаст заготовку процедуры FormCreate
для его обработки. Впишите в нее оператор:
num = true;
Управляем полосами прокрутки
Способ, которым пользователь программы будет испол¬
нять прокрутку, для нас значения не имеет. Важно
только, что ползунок сдвинулся. При этом возникает
событие Change (Изменение). Его нам и надо обработать.
Выберите в окне формы горизонтальную полосу про¬
крутки ScrollBar1. С помощью Инспектора объектов создайте
процедуру ScrollBar1Change для обработки события
OnChange (При изменении). В нее надо добавить операторы
для перемещения текущей фигуры. А это зависит от
того, какая фигура текущая. Нам потребуется услов¬
ный оператор:
Точно так же запрограммируем обработку события
Change (Изменение) для вертикальной полосы прокрутки.
Операторы, которые мы добавим, должны быть такими:
222
if (num)
Shapel->Left = ScrollBarl->Position;
else
Shape2->Left = ScrollBarl->Position;
if (num)
Shapel->Top = ScrollBar2->Position;
else
Shape2->Top = ScrollBar2->Position;
Эти операторы изменяют свойства Left и Тор для теку¬
щей фигуры. Левый верхний угол фигуры (а значит, и
вся фигура целиком) оказывается в другом месте.
Выбираем фигуру
Еще мы хотим выбирать фигуру при помощи мыши.
При наведении на фигуру указателя должно произойти
следующее.
1. Эта фигура станет текущей.
2. Текущаяфигурастанетголубой, а «пассивная» —
фиолетовой.
3. Ползунки полос прокрутки настроятся на теку¬
щую фигуру.
Чтобы определить положение указателя, используют
событие OnMouseMove (При движении мыши). Выберите
первую из наших фигур в окне формы и с помощью
Инспектора объектов создайте процедуру Shape1MouseMove
для его обработки.
Вызов этой процедуры означает, что указатель наведен
на фигуру. В этом случае надо изменить цвета фигур и
значение переменной num.
Если указатель наведен на первую фигуру, запишем
такие операторы:
Shapel->Brush->Color = clAqua;
Shape2->Brush->Color = clFuchsia;
num = true;
223
\4LV 6 этих операторах цвета заданы знакомыми нам константами. Они
в* определяют голубой (oqua) и фиолетовый (fuchsia) цвета.
Наконец, для полос прокрутки изменим значение свой¬
ства Position (Положение). При этом ползунки передви¬
нутся:
ScrollBarl->Position = Shapel->Left;
ScrollBar2->Position = Shapel->Top;
Мы изменили свойство Position (Положение) из програм¬
мы — при этом также возникает событие Change (Изме¬
нение). В данном случае этот факт никак внешне не
проявится. Подумайте, почему это так.
Дальше нам нужно сделать то же самое для второй фи¬
гуры. Создайте процедуру Shape2MouseMove с помощью
Инспектора объектов. Ее содержание практически такое
же, как у первой фигуры:
Программа готова
Все! Программа готова. Давайте посмотрим на ее текст,
записанный полностью.
void fastcall TForml: :ScrollBarlChancre
(TObject *Sender)
{
if (num) Shapel->Left = ScrollBarl->Position;
else Shape2->Left = ScrollBarl->Position;
}
224
Shapel->Brush->Color = clFuchsia;
Shape2->Brush->Color = clAgua;
num = false;
ScrollBarl->Position = Shape2->Left;
ScrollBar2->Position = Shape2~>Top;
void fastcall TForml::ScrollBar2Change
(TObject *Sender)
{
if (num) Shapel->Top = ScrollBar2->Position;
else Shape2->Top = ScrollBar2~>Position;
}
void fastcall TForml::ShapelMouseMove
(TObject *Sender, TShiftState Shift,
int X, int Y)
{
Shapel->Brush->Color = clAqua;
Shape2->Brush->Color = clFuchsia;
num = true;
ScrollBarl->Position = Shapel->Left;
ScrollBar2->Position = Shapel->Top;
void fastcall TForml::Shape2MouseMove
(TObject *Sender, TShiftState Shift,
int X, int Y)
Shapel->Brush->Color = clFuchsia;
Shape2->Brush->Color = clAqua;
num = false;
ScrollBarl->Position = Shape2->Left;
ScrollBar2->Position = Shape2->Top;
void fastcall TForml::FormCreate(TObject *Sender)
num = true;
225
Запустите программу, нажав клавишу F9. Проверьте,
все ли работает так, как задумано. Попробуйте пере¬
местить ползунки полос прокрутки при помощи мыши.
Если щелкнуть непосредственно на ползунке, полоса
прокрутки становится активной. Посмотрите, как она
после этого реагирует на клавиши ВВЕРХ и ВНИЗ, ВЛЕВО
и ВПРАВО, PAGE UP и PAGE DOWN.
Программа в работе
Осваиваем компьютерную
графику
Современные программы почти обязаны
иметь компьютерную графику, но рабо¬
та с нею — дело не столько сложное,
сколько хлопотное. Программист дол¬
жен внимательно следить за координа¬
тами объектов и отслеживать положе¬
ние указателя мыши. В предыдущей
главе мы уже столкнулись с расчетом
координат объектов. В этой главе мы
закрепим полезные навыки
Начнем с конца
В нашей новой программе мы нарисуем светофор с тре¬
мя лампочками, способными реагировать на наведение
указателя мыши. Когда указатель наведен на лам¬
почку, она меняет свой цвет. Примерный вид окна про¬
граммы, которая у нас должна получиться, показан на
рисунке.
Окно
программы
Горящая
лампочка
Погашенные
лампочки
Вот что должно у нас получиться
Для этой программы нам требуется только один ком¬
понент — Shape (Фигура). С его помощью можно рисо¬
вать на форме простые геометрические фигуры: круги,
прямоугольники, овалы. Круглуюлампочку светофора
мы сделаем именно так.
Форма и компоненты
Запустите систему С++ Builder и создайте новый проект.
Сохраните его в папке Светофор. На форме нам надо раз¬
местить объекты — лампочки светофора. Выберите на
вкладке Additional (Дополнительные) палитры компо¬
нентов значок Shape (Фигура) и нарисуйте объект
на форме методом протягивания. Первую фигуру по¬
местите в левый верхний угол формы. Новая фигура
изображается на форме в виде белого прямоугольника
с черной границей.
&
Новая фигура, добавленная на форму
Добавим на форму еще две фигуры. Разместим их ниже
первой. Постарайтесь, чтобы у них были примерно оди¬
наковые размеры. Интервалы между фигурами тоже
лучше сделать одинаковыми.
Настройка фигур
Наши фигуры совсем не похожи на лампочки свето¬
фора. Лампочки должны быть круглыми, а не прямо¬
228
угольными. Не следует забывать и о цвете. Начнем с
самой верхней фигуры. Щелкните на ней, чтобы она
стала выделенной.
Сначала превратим прямоугольник, ограничивающий
фигуру, в квадрат. Для этого изменим значения свойств
Width (Ширина) и Height (Высота). Дадим и тому и другому
свойству значение 61. Теперь позаботимся о форме
фигуры. Она задается свойством Shape (Форма). Чтобы
наша лампочка стала круглой, выберем для этого свой¬
ства значение: stCircle (Круг).
Фигура состоит из контура и внутренней области, кото¬
рые можно раскрасить в разные цвета. Параметры кон¬
тура определяет свойство Pen (Контур). Это составное
свойство, которое можно развернуть в Инспекторе объек¬
тов щелчком на значке «+». Цвет контура определяется
подсвойством Color (Цвет). Щелкните на нем и выберите
в раскрывающемся списке значение clRed (красный
цвет).
Задаем цвет рамки фигуры
Параметры закраски фигуры задаются свойством Brush
(Кисть). Здесь нас ждут некоторые сложности. Дело в
том, что мы хотим, чтобы наши фигуры первоначаль-
229
но не были закрашены. Прозрач¬
ность фигур задается подсвойством
Style (Стиль).
Чтобы фигура была прозрачной,
надо выбрать для него значение
bsClear (Прозрачный). Но цвет закрас¬
ки (свойство Color) система С++ Builder
в этом случае не сохраняет! Нам при¬
дется запомнить этот факт и учесть
его при написании программы.
Итак, мы будем включать и выключать лампочки, из¬
меняя свойство Brush->Style (Стиль заливки) и одновремен¬
но корректируя цвет заливки — свойство Brush->Color.
Точно так же настроим остальные фигуры: Shape2 и
Shape3. Они должны различаться только цветом. Для
второй лампочки выберем желтый цвет (clYellow), а для
третьей (самой нижней) — ярко-зеленый (clLime).
Выравниванием
лампочек мы
займемся позже
Форма после добавления и настройки фигур
Первая проверка
Окно программы практически готово. Давайте посмот¬
рим, что у нас получилось. Чтобы запустить програм¬
му, нажмите клавишу F9.
230
Наш светофор уже можно увидеть на экране. Правда,
он пока что не работает: лампочки вообще не горят. Да
это и немудрено — мы ничего для этого еще не сделали.
Закройте программу щелчком на закрывающей кнопке
справа в строке заголовка окна или комбинацией кла¬
виш ALT + F4. Система С++ Builder вновь вернет на экран
окно формы.
Программируем лампочки
Мы знаём, что элементы управления могут реагировать
на события. Эти события возникают при работе с про¬
граммой. Мы уже знаем, что фигуры могут реагировать
на наведение указателя мыши. Но нас ждет одна непри¬
ятная тонкость.
Обратите внимание на то, что даже если лампочка у нас
круглая, маркеры изменения размера располагаются
по сторонам прямоугольника. То есть система С++ Builder
продолжает считать наши фигуры прямоугольными.
Это означает, что при наведении указателя мыши на
уголок фигуры программа будет считать, что лампоч¬
ку надо зажигать. Это нехорошо.
Можем ли мы что-то с этим сделать? Да. Нам придется
вручную (точнее говоря, программно) отслеживать по¬
ложение указателя мыши в пределах лампочки и «лич¬
но» принимать решение об их включении.
Это можно сделать разными способами. Например, да¬
вайте отключим все наши лампочки, задав их свой¬
ствам Enabled (Включен) значение false (Нет). Их внешний
вид от этого не изменится.
Что теперь произойдет? Для отключенных объектов
никаких событий не возникает. Ачто произойдет, если
указатель мыши будет наведен на лампочку? Кому,
какому объекту будет принадлежать событие MouseMove
(Движение мыши)? Оно будет передано другому объекту,
231
контейнеру, в котором располагается фигура. В нашем
случае это сама форма.
Форма — это тоже объект программы, обладающий
свойствами и способный реагировать на события. Щелк¬
ните на ней вне фигур, откройте в окне Инспектора объектов
вкладку Events (События) и дважды щелкните напротив
события OnMouseMove (При движении мыши). Это событие
возникает, когда указатель мыши наведен на форму,
то есть на окно программы.
Система С++ Builder создаст обработчик этого события —
процедуру FormMouseMove. Эта процедура отличается от
тех, с которыми мы имели дело до сих пор. В нее пере¬
даются дополнительные параметры: Shift — указывает,
не была ли при перемещении нажата клавиша SHIFT,
CTRL или ALT; X — горизонтальная координата указате¬
ля мыши; Y — вертикальная координата указателя.
void fastcall TForml::FormMouseMove
(TObject *Sender, TShiftState Shift,
int X, int У)
Поскольку в нашей программе нажатие клавиш не
предполагается, нас интересуют
только параметры X и Y. Они
определяют положение указате¬
ля мыши на форме, считая от ее
левого верхнего угла. Они помо¬
гут узнать, наведен ли указа¬
тель на одну из фигур, и если да,
то на какую.
Что произойдет при
наведении указателя
Теперь давайте подумаем, что должна делать процеду¬
ра FormMouseMove. Можно предложить следующий под¬
232
ход. Проверим, наведен ли указатель на первую лам¬
почку. Если да, зажигаем ее, если нет — гасим. Выпол¬
няем ту же проверку для второй и третьей лампочки.
Так как одновременно навести указатель на две или три
лампочки не получится, то и зажечь несколько лампо¬
чек вместе нельзя. Если указатель вообще не наведен
на лампочки, все они погаснут.
Чем хорош такой подход? Тем, что для каждой лам¬
почки выполняются одни и те же действия. Это значит,
что мы можем оформить их в виде одной функции. В ре¬
зультате работы функции мы узнаем, должна лампочка
гореть или нет. Эти данные мы используем для выбора
способа заполнения лампочки краской.
Мы знаем, что для сплошной закраски надо задать
свойству Brush->Style значение bsSolid, а для гашения
лампочки — значение bsClear. Идентификаторы bsSolid
и bsClear — это константы.
Константы записываются точно такими же идентифи¬
каторами, как и переменные. Разницанебольшая: зна¬
чение переменной в ходе работы может меняться, а зна¬
чение константы всегда остается постоянным. При
этом значение константы подставляется уже на этапе
компиляции программы, так что специально хранить
константы обычно не требуется.
Если, например, s вашей программе часто встречается число n, то
имеет смысл присвоить ему имя константы, например Pi, один раз
задать ей значение 3,1415926 и в будущем все вычисления про¬
изводить умножением на константу Pi, а не на число 3,1415926.
В системе С++ Builder заранее определено множество кон¬
стант на все случаи жизни. Например, возможные цве¬
та и многое другое задают при помощи констант. Они
имеют стандартные имена, и когда нам надо, напри¬
мер, задать или изменить цвет объекта, мы можем при¬
своить значение нужной константы нужному свойству.
233
Пусть наша функция (назовем ее OnShape) и возвраща¬
ет значения констант, описывающих способ заполне¬
ния фигуры.
Осталось решить, какие параметры мы передадим в
функцию. Во-первых, надо указать, для какой фигуры
производится проверка. Во-вторых, нужно также пере¬
дать в функцию координаты указателя мыши.
Мы решили, как обращаться к функции. Значение,
полученное от функции, можно сразу присвоить свой¬
ству фигуры, отвечающему за способ заполнения. Кроме
того, перед этим надо задать цвет заполнения, так как
мы знаем, что при гашении лампочки он не сохраняется.
Включить или выключить первую лампочку можно
так:
Shapel->Brush->Color = clRed;
Shapel->Brush->Style = OnShape(Shapel, X, Y);
Похожие операторы надо записать и для других фигур:
Эти операторы и составят содержимое процедуры
FormMouseMove.
Нисходящее и восходящее программирование:
раздел для любознательных
Как хитрые фокусники, мы выразили свойства наших
фигур через функцию OnShape, а саму-то эту функцию
пока не написали! Она еще не существует.
И в этом нет ничего страшного, пока программа не запу¬
щена. Мы просто должны держать в уме, что эту функ¬
234
Shape2->Brush->Color = clYellow;
Shape2->Brush->Style = 0nShape(Shape2, X, Y);
Shape3->Brush->Color = clLime;
Shape3->Brush->Style = OnShape(Shape3, X, Y);
цию надо не забыть создать. Такой порядок програм¬
мирования называют нисходящим. Сначала програм¬
мист записывает операторы, используя процедуры и
функции, которых нет в природе, потом он создает их
и внутри опять может использовать подпрограммы,
которых еще нет. Так он действует до конца, пока не
дойдет до самой последней процедуры, которая где-то
используется.
В противоположность нисходящему порядку програм¬
мирования, существует восходящий порядок, когда
сначала создают самые мелкие процедуры и функции,
потом их используют в более крупных и так далее, пока
не дойдут до самого верхнего уровня.
Каждый сам решает, какого порядка ему лучше при¬
держиваться. Обычно бывает так: если программист
может держать в уме всю программу целиком и, как
шахматист, способен на несколько ходов вперед про¬
считывать свою работу, он предпочитает нисходящий
порядок. Если же мелких процедур очень много и все
они в голове не укладываются, он может начинать ра¬
боту с них — тогда порядок будет восходящим.
На самом деле реальная жизнь всегда оказывается
сложнее, чем теоретические идеи, и очень часто про¬
граммисту приходится что-то делать в нисходящем
порядке, а что-то — в восходящем. В общем, это дело
творческое, и по мере набора опыта каждый сам при¬
думывает себе такую модель, которая наиболее удобна
лично ему.
Попадание в фигуру
Итак, нам надо не забыть написать функцию OnShape.
Автоматически система С++ Builder умеет создавать только
обработчики событий, поэтому эту процедуру нам при¬
дется писать целиком. Наша функция вызывается из
235
процедуры FormMouseMove и поэтому, согласно требова¬
нию языка С++, должна быть описана раньше.
В окне кода переведите указатель мыши выше ранее
созданной процедуры FormMouseMove. Первый оператор
функции, ее описание, запишите так:
TBrushStyle __fastcall OnShape
(TShape *sh, int X, int Y)
Первое слово в этой строке — это тип значения, воз¬
вращаемого функцией. Чтобы узнать, к какому типу
относятся стили закраски, необходимо обратиться к
справочной системе. Здесь мы привели готовый ответ —
тип TBrushStyle.
Ключевое слово fastcall описывает способ вызова функ¬
ции для компилятора. Этот вариант является предпоч¬
тительным для всех подпрограмм, используемых в сис¬
теме С++ Builder.
Далее идет имя функции, а после него в скобках — фор¬
мальные параметры. Они показывают, какие данные
мы передаем в функцию. При описании параметров
надо указать их тип и имя.
Если вас смущает обозначение TShape, то знайте, что
это стандартное обозначение типов данных для компо¬
нентов. Чтобы перейти от названия компонента к типу
данных, достаточно добавить букву T (от слова type — тип).
Тип параметра указывается перед именем параметра.
Параметры разделяются запятой. После того как все
параметры перечислены, скобки надо закрыть. Ставить
точку с запятой после закрывающей скобки нельзя.
Теперь давайте в следующую строку введем открыва¬
ющую фигурную скобку, затем оставим одну пустую
строку и затем укажем закрывающую фигурную скоб¬
ку. Наше описание процедуры теперь выглядит так:
236
Если посмотреть повнимательнее, то легко увидеть, что
это практически точно такая же заготовка, какую сис¬
тема С++ Builder создает для процедур обработки собы¬
тий. Далее мы можем действовать по трафарету.
Правда, заметим, что нам придется вводить дополни¬
тельные переменные, которые в языке С++ необходи¬
мо описать. Мы отложим описание переменных до того
момента, когда наша функция будет готова.
Итак, давайте определим, наведен ли указатель на фи¬
гуру. Сначала вычислим радиус фигуры. Он составляет
половину ее высоты или ширины:
r = sh->Width / 2;
Теперь найдем координаты центра фигуры и присвоим
их вспомогательным переменным. Чтобы найти коор¬
динаты центра фигуры, прибавим к значениям левой
и верхней границ величину радиуса:
cx = sh->Left + r;
су = sh->Top + r;
Далее вычислим квадрат расстояния от центра и тоже
присвоим его значение вспомогательной переменной:
d2 = (X - cx) * (X - cx) + (Y - су) * (Y - су) ;
Нам надо вычислить значение функции. По правилам
языка С++ нужное значение надо указать в операторе
return. Этот оператор прекращает работу подпрограммы
237
TBrushStyle fastcall OnShape
(TShape *sh, int X, int Y)
{
и возвращает управление в вызывающую программу.
Например, если указатель мыши наведен на лампоч¬
ку, нам надо написать оператор:
if (d2 < r*r) return bsSolid;
Такое значение цвета функция вернет, когда указатель
мыши находится в пределах фигуры. Если же он нахо¬
дится вне фигуры, то данное условие ложно и фигуру
надо сделать прозрачной:
return bsClear;
Осталось описать все использованные нами в процедуре
OnShape переменные. Нужное описание надо вставить
сразу после открывающей фигурной скобки. Вот как
должен выглядеть этот оператор:
int r, cx, су, d2;
Программа готова
Все! Программа готова! Запустите ее, нажав клавишу
F9. Обратите внимание на то, что наши лампочки дейст¬
вительно круглые — если навести указатель мыши на
«угол» лампочки, она не загорится.
Еще раз подчеркнем, что это наше немалое достижение, так как
обычно все элементы управления прямоугольные. В операцион¬
ной системе UJindouus вы не найдете элементов управления с за¬
кругленными углами, а мы их создали.
Вот как выглядит написанный нами текст программы
в окне кода:
TBrushStyle fastcall OnShape
(TShape *sh, int X, int Y)
{
int r, cx, су, d2;
238
r = sh->Width / 2;
cx = sh->Left + r;
су = sh->Top + r;
d2 = (X - cx) * (X - cx) + (Y - су) * (Y - су);
if (d2 < r*r) return bsSolid;
return bsClear;
>
void fastcall TForml::FormMouseMove
(TObject *Sender, TShiftState Shift,
int X, int Y)
{
Shapel->Brush->Color = clRed;
Shapel~>Brush->Style = OnShape(Shapel, X, Y);
Shape2->Brush->Color = clYellow;
Shape2->Brush->Style = 0nShape(Shape2, X, Y);
Shape3~>Brush~>Color = clLime;
Shape3->Brush->Style = 0nShape(Shape3, X, Y);
Приведем окно s порядок
Программа готова и работает, но смотреть на ее окно
не очень приятно. Мы стремились к тому, чтобы «все
заработало», и не уделили должного внимания мело¬
чам. Следы неаккуратности портят впечатление от про¬
граммы. Поэтому давайте вернемся к ней и уберем все
явные дефекты.
Обратим внимание на окно программы. Нам надо:
4 выбрать размер окна (сейчас в нем слишком
много свободного места);
239
♦ изменить заголовок окна (Form1 — явно не под¬
ходит);
♦ проверить фоновый цвет окна (мы здесь поло¬
жились на систему С++ Builder);
♦ запретить изменение размеров окна при работе
программы (чтобы огни светофора не пропадали
из виду).
Все это можно сделать без программирования, пользу¬
ясь только возможностями Инспектора объектов.
Настраиваем размер окна. Размер окна определяется
свойствами формы Height (Высота) и Width (Ширина). Щелк¬
ните напротив этих свойств в правом столбце таблицы
и задайте, соответственно, значения 300 и 120. Обра¬
тите внимание на то, как при этом изменился размер
окна формы. Его можно изменить также протягивани¬
ем мыши, и мы знаем, как это делается, но с помощью
Инспектора объектов можно задать не приблизительные,
а точные значения.
Меняем заголовок окна. Заголовок окна — это значе¬
ние свойства Caption (Заголовок). Щелкните на значении
этого свойства и введите новый заголовок: Светофор.
Мы уже знаем, что здесь можно применять русские бук¬
вы. Заголовок немедленно появится в окне формы.
Удаляем лишние кнопки. Окно у нас теперь стало таким
узким, что кнопки управления его размером, присут¬
ствующие в строке заголовка, мешают увидеть заголо¬
вок окна целиком. На самом деле они нам не очень-то
и нужны.
Отображение стандартных кнопок за¬
дается составным свойством Borderlcons
(Служебные кнопки). Разверните его
щелчком на значке « + ». Для под-
свойств biMinimize (Сворачивание) и biMaximize (Разворачи-
240
вание) задайте значение false (Нет). На форме ничего не
изменится, но при запуске программы лишние кнопки
исчезнут.
Меняем фоновый цвет окна. Фоновый цвет окна зада¬
ется с помощью свойства Color (Цвет). Здесь сейчас задан
стандартный серый цвет, который можно оставить без
изменений.
Запрещаем изменение размеров окна. Можно или нет
изменить размеры окна программы, зависит от типа его
рамки. Этот тип задается свойством BorderStyle (Стиль
рамки). Выберем в раскрывающемся списке значение
bsSingle (Тонкая). Это значение определяет тонкую рамку,
не допускающую изменения размеров окна.
Выравнивание элементов
Мы размещали фигуры на форме вручную, так что фор¬
ма выглядит не очень аккуратно. Расположить объекты
ровно и симметрично можно с помощью команд вырав¬
нивания.
Выделите все три фигуры. Щелкните на
каждой из них по очереди, удерживая
нажатой клавишу SHIFT. Когда выбрана
группа фигур, маркеры изменения раз¬
мера выглядят не так, как при выборе
одной фигуры.
После того как все фигуры выбраны, дай¬
те команду Edit ► Align (Правка >■ Выровнять).
Откроется диалоговое окно Alignment (Вы¬
равнивание). Установите слевапереключа-
тель Center in Window (Центрировать в окне), а
справа—переключатель Space Equally (С рав¬
ными промежутками). Послещелчканакнопке
OK фигуры будут стоять по центру по гори¬
зонтали и иметь равные промежутки по вертикали.
j{/ Свс Hlgi 13
9—1619
241
Все фигуры
выбраны для
настройки
Выравнивание расположения объектов на форме
Похожая команда Edit >• Size (Правка >- Размер) позволяет
выровнять размеры фигуры, но нам она не нужна, так
как мы уже придали фигурам одинаковые размеры.
Теперь придраться к размещению фигур трудно. Нашу
программу можно считать законченной. Если вы хотите
поэкспериментировать с ней, попробуйте запустить ка¬
кую-нибудь другую программу, окно которой не займет
весь экран, и посмотрите, загораются ли лампочки,
если окно светофора не активно.
Вид окна готовой программы
при разном положении указателя мыши
242
f?lQSQ 20
Кнопки с рисунками в простой
головоломке
Сейчас мы с вами познакомимся с еще
одной разновидностью кнопок. Это
кнопки с изображением, которые тоже
можно создать в системе С++ Builder.
С их помощью мы запрограммируем про¬
стую головоломку
Головоломка
Эту головоломку придумал французский
математик Эдуард Люка. Среди его науч¬
ных достижений — самое большое из про¬
стых чисел, обнаруженных без помощи
компьютера. А ведь первые компьютеры
появились только через пятьдесят лет
после его смерти!
Он же придумал немало головоломок. Мы будем про¬
граммировать одну из самых известных. Еще одна из-
вестнаяголоволомкаЛюка— « Ханойские башни ». Эти
две задачи, пожалуй, сегодня чаще всего встречаются
в компьютерной форме. Итак, в чем же состоит наша
головоломка?
Игровая доска содержит семь клеток, расположенных
в ряд. На трех левых клетках стоят белые фишки, на
трех правых — черные. Центральная клетка пуста.
Задача заключается в том, чтобы, делая допустимые
ходы, поменять фишки местами. Правила ходов такие.
1. Белые фишки могут ходить только вправо, а чер¬
ные — только влево.
2. Ходить разрешается только на свободное поле.
243
Эдуард Люка
3. Разрешены перемещение на соседнее поле и «пры¬
жок» через одну фишку.
В качестве фишек можно использовать шашки,
монетки или просто металлические кружки
Компьютерная версия
В нашей программе в роли фишек будут выступать
кнопки, причем не простые, а с изображением. Эти
кнопки будут передвигаться по игровой доске, создан¬
ной при помощи панели. Больше никаких элементов
управления не понадобится.
Какое изображение мы поместим на кнопки? Какое
захотим. Внешний вид кнопок может быть любым,
лишь бы «белые» и «черные» фишки отличались друг
от друга. Например, можно пометить кнопки цветными
треугольниками, направленными так, чтобы они ука¬
зывали допустимое направление ходов. В этом случае
окно программы может выглядеть, как показано на ри¬
сунке.
Вот что должно у нас получиться
Форма и компоненты
Запустим систему С++ Builder и создадим новый проект.
Сохраним его в папке Фишки. Как мы уже говорили, нам
нужны два типа компонентов: панель и кнопка с изо¬
244
бражением. Панель нужна одна, а вот кнопок понадо¬
бится шесть.
В ходе решения головоломки кнопки будут двигаться,
а вид окна — меняться. Поэтому надо точно задать гео¬
метрические размеры и положения всех объектов.
Надо, чтобы картинка всегда была аккуратной.
Лучше всего разметить все необходимые размеры зара¬
нее, на листе бумаге. Например, мы выбрали такую
схему.
Все размеры указаны в пикселах
Рамка панели и границы кнопок лежат внутри задан¬
ных областей. Настраивать положение объектов мы
будем, изменяя свойства Тор (Сверху), Left (Слева), Height
(Высота) и Width (Ширина).
Работу над программой начнем с того, что изменим заго¬
ловок формы — зададим свойству Caption (Заголовок)
значение Идет игра.
Добавляем панель
Начнем формирование окна программы с добавления
панели. Щелкните на значке Panel (Панель) на вкладке
Standard (Стандартные) палитры компонентов и на¬
рисуйте панель в окне формы. С помощью Инспек¬
тора объектов задайте для свойства Height (Высота)
значение 65, для свойства Width (Ширина) — значение
401. Методом перетаскивания поместите панель в верх¬
ний левый угол формы и измените размеры формы так,
245
чтобы на ней не оставалось «лишнего» свободного
места.
Нам нужны еще некоторые дополнительные настройки.
Во-первых, подпись на панели ни к чему — очистите
значение свойства Caption (Заголовок) для панели. Во-вто-
рых, мы можем изменить вид рамки панели. Напри¬
мер, если задать для свойства Bevellnner (Внутренняя фаска)
значение bwLowered, то панель будет иметь тоненькую
выпуклую рамку.
теш
■4
Форма с панелью после настройки
Если хотите, можете задать рамку по своему вкусу.
Только не делайте ее очень широкой или слегка уве¬
личьте размеры панели.
Добавляем кнопки
Теперь можно добавлять кнопки. Откройте в палитре
компонентов вкладку Additional (Дополнительные) и
выберите компонент BitBtn (Кнопка с изображением).
Название компонента происходит от слов bitmap
button (кнопка с растровым изображением). Нарисуй¬
те кнопку прямо на панели.
На первый взгляд, получившаяся кнопка ничем не
отличается от обычной, за исключением того, что стан¬
дартная надпись выглядит несколько иначе. Но на самом
деле это не так. У нее появились дополнительные свой¬
ства: Kind (Тип) и Glyph (Значок), которые позволяют вы¬
брать изображение, показываемое наряду с подписью.
Но до этого руки у нас дойдут не сразу.
246
Пока что зададим размеры кнопки — свойства Height
(Высота) и Width (Ширина) должны иметь значение 49.
Сразу же поместим кнопку на место: зададим свойствам
Left (Слева) и Тор (Сверху) значение 8.
[ffiИдет мгра НИР
На форму добавлена первая кнопка
Остальные кнопки добавим на форму при помощи буфе¬
ра обмена. Нажмите комбинацию клавиш CTRL + С, а
затем — CTRL + V. На панели появится новая кнопка.
Комбинацию клавиш CTRL + V надо будет нажать еще
четырежды. Всем новым кнопкам надо задать значение
свойства Тор (Сверху) равным 8, а значения свойства Left
(Слева) должны в соответствии с нашим планом быть
равны: 64,120,232,288 и 344. Окно формы примет вид,
показанный на рисунке.
Г-ТГЛ
Все кнопки помещены на форму.
Промежуток соответствует пустой клетке игрового поля
Создаем и добавляем рисунки
Сохраните текущее состояние проекта с помощью ко¬
манды File ► Save All (Файл ► Сохранить все), если это еще
не сделано. Теперь надо создать рисунки, которые раз¬
мещаются на кнопках. Так как нам нужны очень про¬
стые рисунки, их можно создать при помощи стандарт¬
ной программы Paint. Если вы пойдете по этому пути,
247
задайте размеры рисунков 45x45 пикселов и работайте
в режиме увеличения. Создайте две картинки с имена¬
ми Left.bmp и Right.bmp и поместите их в папку проекта.
Первый рисунок предназначен для трех левых кнопок,
второй — для трех правых.
Дальше надо назначить рисунки кнопкам. Для этого
придется по очереди выбрать каждую из кнопок и сде¬
лать следующие действия.
1. Очистите значение свойства Caption (Заголовок),
чтобы удалить подпись на кнопке.
2. Выберите в окне Инспектора объектов свойство Glyph
(Значок) и щелкните на кнопке построителя.
3. Откроется диалоговое окно Picture Editor (Редактор
рисунков) — щелкните на кнопке Load (Загрузить).
4. В диалоговом окне Load Picture (Загрузка рисунков)
выберите файл с изображением кнопки и щелк¬
ните на кнопке Открыть.
Картинка загружена в редактор рисунков
248
Left.bmp
Right.bmp
5. Щелкните на кнопке OK — и выбранная картинка
появится на кнопке.
После того как все эти действия выполнены, форма
готова. Она должна получиться такой, как показано на
рисунке.
Готовая форма для программы
Готовимся к программированию
Вы скоро увидите, что программа получится очень про¬
стой, но чтобы добиться этой простоты, надо сначала
как следует подумать. В чем заключается ход в игре?
В том, что одна из фишек-кнопок переносится на сво¬
бодное место. Это значит, что достаточно указать,
какой фишкой надо пойти. Если фишка может сделать
ход, то он только один.
Итак, пользователь может выбрать ход, щелкнув на
одной из кнопок. То есть мы будем обрабатывать собы¬
тие OnClick (При щелчке).
Но мы должны различать кнопки, а также знать, где
они находятся и где находится свободная клетка. По¬
этому мы поступим так: будем хранить дополнитель¬
ные данные прямо в свойствах объекта-кнопки. Для
этого нам нужно «свободное» свойство. Такое свойство
есть: все компоненты системы С++ Builder предоставля¬
ют свойство Tag (Тег), которое программист может
использовать по своему усмотрению. Им мы и восполь¬
зуемся.
Свойство Tag (Тег) может хранить целое число, а что нуж¬
но хранить нам? Нам нужно хранить два числа. Во-пер¬
249
вых, номер клетки, в которой находится кнопка, а во-
вторых, признак «цвета» фишки (от него зависит, в ка¬
кую сторону она может ходить). Эти данные легко «упа¬
ковать» в одно целое число: умножим номер клетки на
2 и, если фишка черная, прибавим к нему единицу.
Такое значение легко «разобрать» обратно на состав¬
ляющие. Разделим его на 2. Частное даст номер клетки,
а остаток будет признаком цвета фишки.
Теперь можно задать значения свойства Tag (Тег) для
всех кнопок. Поочередно выберем фишки слева напра¬
во и зададим для свойства Tag (Тег) значения, приведен¬
ные в таблице.
Значение свойства
Комментарий
~2
первая клетка, белая фишка
~4
вторая клетка, белая фишка
~6
третья клетка, белая фишка
П
пятая клетка, черная фишка
Тз
шестая клетка, черная фишка
15
седьмая клетка, черная фишка
Еще нам надо знать, какая из клеток у нас пустая. Ко¬
нечно, это значение всегда можно вычислить, но проще
создать дополнительную переменную и просто хранить
его. Так и поступим. Перейдите в окно кода, найдите в
нем область описания глобальных переменных и до¬
бавьте туда такой оператор:
int n = 4;
Здесь мы не только создаем нужную переменную, но и
сразу присваиваем ей начальное значение. Первона¬
чально у нас пуста четвертая клетка, отсюда и выбор
этого числа.
250
Как различать объекты
Щелчок на кнопке — единственное событие, которое
требует обработки. При щелчке на любой кнопке надо
делать примерно одно и то же. Поэтому для всех кно¬
пок нужна одна процедура обработки.
Но выполнение хода зависит от того, на какой именно
кнопке был сделан щелчок. Как процедура обработки
сможет различить кнопки? Давайте выберем первую
кнопку и на вкладке Events (События) окна Инспектора
объектов дважды щелкнем напротив события OnClick (При
щелчке). Система С++ Builder создаст заготовку процеду¬
ры BitBtn1Click:
void fastcall TForml::BitBtnlClick
(TObject *Sender)
{
}
Обратите внимание на то, что в процедуру передается
параметр — объект Sender. Он представляет именно тот
объект, который вызвал событие, и может дать доступ
к его свойствам. Но напрямую это не получится. Про¬
грамме надо специально указать, что объект Sender в
данном случае следует считать кнопкой. Чтобы преоб¬
разовать переменную к иному типу (если это возмож¬
но), в языке С++ новый тип указывается перед именем
переменной в скобках:
(TBitBtn *)Sender
Узнать, какая именно кнопка нажата, можно с помо¬
щью свойства Tag (Тег). Чтобы обратиться к нему, надо
написать:
((TBitBtn *)Sender)->Tag
251
Лишние скобки, указывающие, к чему именно следу¬
ет применить операцию преобразования типа, никог¬
да не повредят.
Программируем работу кнопок
Мы узнали, как выяснить, на какой кнопке был сде¬
лан щелчок. Теперь можно назначить процедуру BitBtn 1 Click
для обработки щелчка на всех
кнопках. Для этого надо выбрать
ее из раскрывающегося списка в окне Инспектора объек¬
тов.
Вернемся в окно кода. Начнем программирование с
того, что «разберем» свойство Tag (Тег) нашего объекта
на те составные части, из которых мы его собирали.
Переменная i теперь хранит номер клетки, в которой
находится кнопка, а переменная с определяет цвет
фишки.
Если ход возможен, то фишка должна переместиться
из клетки с номером i в клетку с номером n. Таким обра¬
зом, величину перемещения можно вычислить с помо¬
щью такого оператора:
k := n-i;
Мы ввели три новые переменных, которые необходимо
описать. Добавьте в начало процедуры после открыва¬
ющей фигурной скобки строку:
int i, с, k;
252
i = ((TBitBtn *)Sender)->Tag / 2;
с = ((TBitBtn *)Sender)->Tag % 2;
Возможен ли xog
Теперь нам надо выяснить, допустим ли ход выбран¬
ной фишкой. Если мы ходим белой фишкой, то длина
хода должна быть равна единице или двум. Если ход
делается черной фишкой, то ход происходит в противо¬
положном направлении и его длина должна быть отри¬
цательной. Проверяемое условие может быть записано
таким оператором:
if ((c==0 && (k==l || k==2))
II (c==l && (k==-l II k= = -2))) {
Здесь у нас ряд условий соединен логическими опера¬
циями. Знак && соединяет условия, которые должны
быть выполнены одновременно. Условие, части кото¬
рого соединены знаком 11, истинно, если истинно хотя
бы одно из отдельных условий. Для удобочитаемости
получающегося сложного выражения рекомендуется
щедро использовать скобки.
Фактически этот оператор говорит, что ход допустим
как в том случае, когда белая фишка движется вправо,
так и тогда, когда черная фишка идет влево.
Мы сразу поставили открывающую фигурную скобку,
понимая, что одним оператором нам здесь не обойтись.
Делоем xog
Итак, мы достигли того момента, когда нам действи¬
тельно надо делать ход. А сделать это очень просто.
Перемещение кнопки задано переменной k. Значение
свойства Tag (Тег) должно измениться на удвоенную
величину перемещения, а чтобы определить смещение
кнопки — изменение свойства Left (Слева), величину
перемещения надо умножить на расстояние между
клетками, то есть на 56. Запишем такой оператор:
253
((TBitBtn *)Sender)->Tag += 2*k;
Обратите внимание на то, что вместо оператора присва¬
ивания мы записали незнакомый знак +=. Это очень
удобная возможность сокращенной записи.Если нам
надо записать оператор типа
x = x + у;
где x — некоторая переменная, а у — любое выраже¬
ние, то его можно записать и в сокращенном виде:
X += у;
Это относится и к другим арифметическим операциям —
можно использовать знаки -=, *= и /=.B данном случае
это особенно удобно, так как изменяемая переменная
записывается очень сложно. Аналогичным образом
изменим и положение перемещаемой фишки:
((TBitBtn *)Sender)->Left += 56*k;
Теперь пустая клетка окажется там, где раньше была
фишка:
n = i ;
Осталось закрыть условный оператор:
>
Первая проверка
Наша программа уже практически готова! Запустим ее,
нажав клавишу F9. Легко проверить, что фишки можно
перемещать в строгом соответствии с правилами (а не
по правилам не получится). Можно даже попробовать
решить головоломку.
254
Головоломка в ходе решения
И если ее действительно удастся решить, то можно об¬
наружить важный недочет. Программа не реагирует на
то, что головоломка решена. Впрочем, об этом можно
догадаться и без проверки. Ведь ничего подобного мы
пока не программировали. Поэтому давайте закроем
программу щелчком на закрывающей кнопке и попы¬
таемся выяснить, решил ли игрок головоломку.
Условия победы
Кажется, что выяснить, решена ли головоломка, очень
сложно. Вроде бы надо проверять, где находится каж¬
дая кнопка-фишка. К тому же не исключено, что в конце
игры кнопки расположены в другой последовательно¬
сти, что еще более усложняет задачу.
Но на самом деле есть очень простой способ выполнить
такую проверку. Представьте себе, что черных фишек
на доске нет. Конечно, от головоломки тогда ничего не
останется, но ясно, что в ходе решения каждая белая
фишка сдвинется на четыре клетки вперед. Все вместе
они сдвинутся на 12 клеток. Легко убедиться, что это
число останется тем же, если в процессе движения по¬
рядок фишек изменится.
То же самое верно и в отношении черных фишек. Они
также должны сдвинуться на 12 клеток, а в сумме все
фишки должны переместиться на 24 клетки. Кроме
того, фишки движутся только в одном направлении и
ходы назад не разрешаются. Стало быть, если суммар¬
ная длина ходов меньше, чем 24 клетки, то головолом¬
ка еще не решена.
255
Поэтому будем действовать так. Создадим переменную
win. Опишем ее как глобальную, после переменной n:
int n = 4, win = 24;
Мы здесь сразу же присвоили этой переменной значе¬
ние 24.
При каждом ходе уменьшим значение этой переменной
на длину хода. Для этого достаточно добавить в проце-
ДУРУ BitBtn1Click после оператора n=i такой оператор:
win -= abs(k);
Здесь abs — стандартная функция, вычисляющая мо¬
дуль (абсолютную величину) параметра. Чтобы ее мож¬
но было использовать, необходимо добавить в начало
программного кода строку:
#include <stdlib.h>
Выполнять проверку завершения решения будем в конце
процедуры BitBtn1Click. Например, это можно сделать так:
if (win == 0) {
Caption = "Победа!";
Panell->Enabled = false;
Panell->Color = clFuchsia;
}
Здесь мы меняем заголовок окна (свойство Caption в дан¬
ном случае относится к форме), меняем цвет панели и
отключаем панель. Необычным может показаться
только последнее действие. Дело в том, что панель —
это контейнер. При отключении контейнера отключа¬
ются и все расположенные в нем объекты. Иными сло¬
вами, так мы отключили все наши кнопки одним опе¬
ратором.
256
Программа готова
Вот теперь наша программа действительно готова. По¬
смотрите на текст единственной написанной нами про¬
цедуры еще раз:
Теперь наша программа не просто работает, но и спо¬
собна показать, что головоломка решена. Запустите ее
снова, нажав клавишу F9. На этот раз попытайтесь на
самом деле решить головоломку. На это может пона¬
добиться насколько попыток. Когда задача будет реше¬
на, окно изменится так, как показано на рисунке.
257
void __fastcall TForml::BitBtnlClick
(TObject *Sender)
{
int i, с, k;
i = ((TBitBtn *)Sender)->Tag / 2;
с = ((TBitBtn *)Sender)->Tag % 2;
k = n-i;
if ( (c==0 && (k==l II k==2) )
|| (c==l && (k==-l II k==-2))) {
((TBitBtn *)Sender)->Tag += 2*'k;
((TBitBtn *)Sender)->Left += 56*k;
win -= abs(k);
n = i;
}
if (win == 0) {
Caption = "Победа I";
Panell->Enabled = false;
Panell->Color = clFuchsia;
}
}
Головоломка решена!
Заметим, что эта головоломка отличается тем, что не¬
правильный ход в ней «закрывает» возможность выиг¬
рыша. Такие «тупики», в отличие от правильного реше¬
ния, нашей программой не обнаруживаются. Пользо¬
ватель должен сам понять, что у него уже не осталось
или скоро не останется ни одного хода, и начать игру
сначала.
Неправильные ходы могут завести в тупик
Глово 21
Циклы И MQCCMBbl
Сильная сторона компьютеров проявля¬
ется не тогда, когда надо выполнить
расчет по очень слоурной формуле, а
тогда, когда надо выполнить огромное
количество относительно несложных,
но повторяющихся расчетов. Для таких
случаев в языках программирования hpe,-
дусмотрен специальный оператор, ко¬
торый называется оператором цикла
Циклические вычисления
Циклические вычисления — это повторяющиеся вы¬
числения. Человек сталкивается с ними впервые в ран¬
нем детстве, еще до того, как познакомится с таблицей
умножения. Что такое умножение? Это ряд последо¬
вательных, совершенно одинаковых операций сложе¬
ния. Например: 5 x 6 = 5 + 5 + 5 + 5 + 5 + 5. Именно так
и учат детей умножать числа.
Как работает процессор:
раздел для любознательных
Процессор компьютера — это электронная микросхема,
работающая не с числами, а с сигналами. Внутри про¬
цессор состоит из ячеек, похожих на ячейки памяти,
только их совсем немного и называются они регистрами.
От ячеек оперативной памяти регистры отличаются тем,
что, во-первых, доступ к ним происходит в десятки раз
быстрее, а во-вторых, данные в них можно изменять.
Возможно, вы до сих пор полагали, что данные в ячей¬
ках памяти тоже можно изменять? Вы думали, что
259
когда в программе изменяется значение переменной,
то в той ячейке памяти, в которой она хранится, значе¬
ние изменяется? Это и так, и не так. Оно действительно
изменяется, но только не в ячейке памяти. На самом
деле процессор забирает данные из ячейки памяти в
один из своих регистров, там их изменяет и снова от¬
правляет в ту же ячейку. Как видите, значение в ячейке
памяти действительно изменилось, но изменялось оно
не там, а в процессоре.
В общем, оперативная память — это не цех по обработке
чисел, а склад, где хранятся необработанные заготовки,
недоделанные полуфабрикаты и готовая продукция.
Настоящие цеха — это регистры процессора. В них-то
все и происходит.
По своему устройству процессор способен выполнять
всего лишь три операции с числами: сложение, сравне¬
ние и копирование. Чтобы выполнить сложение, ему
достаточно двух регистров. В один регистр закладыва¬
ется первое слагаемое, во второй — второе, а потом в
любом из них можно получить результат.
Если сумма будет вычисляться в
первом регистре, то к первому
слагаемому добавится второе.
При этом первое слагаемое будет
испорчено суммой.
Если же сумма будет вычислять¬
ся во втором регистре, то после
сложения второе слагаемое будет
испорчено суммой. В большин¬
стве случаев это не страшно, но
если нужно, чтобы слагаемые не
портились, то надо использовать
для суммы третий регистр.
До операции
После операции
Сумма портит одно
из слагаемых
260
В этом случае нагрузка на процессор выше, но зато оба
слагаемых можно использовать в повторных вычислениях
Для операций сравнения всегда используются три ре¬
гистра. В одном хранится первое число, в другом —
второе, а результат сравнения проявляется в специаль¬
ном флаговом регистре. Флаговый регистр — это такой
же регистр, как и все, только то, что в нем хранится,
рассматривают не как число, а как набор включенных
или выключенных битов (флажков). По тому, какой
флажок (бит) включился в результате сравнения, можно
сказать, какое из двух чисел больше или меньше.
Результат операции сравнения можно узнать по
состоянию флажков в специальном регистре
Как процессор выполняет умножение
Мы сказали, что процессор способен всего лишь к трем
операциям: сложению, сравнению и побитному копи¬
рованию, когда каждый бит одного регистра копиру¬
ется в какой-то бит другого регистра. На самом деле со¬
временные процессоры способны выполнять более
тысячи различных операций, но все они получаются
из этих трех простейших. Например, чтобы выполнить
вычитание, процессор выполняет сложение, но при
этом меняет знак у вычитаемого.
261
К умножению процессоры тоже не способны. Они заме¬
няют его последовательностью операций сложения,
как школьник, который не знает таблицы умножения.
Возведение в степень заменяется повторными операци¬
ями умножения, каждая из которых заменяется по¬
вторными операциями сложения и так далее.
Чтобы умножить одно число на другое, процессор дол¬
жен к нулю прибавить первое число столько раз, сколько
записано во втором числе. Для этого ему нужны уже
четыре обычных регистра и один флаговый. В одном
регистре хранится первый сомножитель, во втором —
второй, в третьем накапливается сумма, а в четвертом
организуется счетчик, который считает, сколько раз
произошло сложение. После каждого сложения число
в счетчике увеличивается на единицу и сравнивается
со вторым сомножителем.
Вот как это выглядит на схеме после первого шага (всего
их должно быть семь).
Состояние регистров процессора после первого шага
умножения чисел 5 и 7
Когда значение в счетчике сравняется со вторым сомно¬
жителем, включится флажок во флаговом регистре, гово¬
рящий о том, что операция завершена.
262
Состояние регистров процессора после седьмого шага
умножения чисел 5 и 7
Организация цикла e языке программирования
Конечно, мы сделали свой рассказ о том, что происхо¬
дит в процессоре, немножко упрощенным, но главное
вы должны понять: у процессора есть средства, чтобы
организовывать повторяющиеся вычисления. Задача
программиста — суметь ими воспользоваться.
О том, чтобы напрямую управлять процессором, мы
пока даже и не думаем. Для этого существуют так на¬
зываемые языки программирования ни.псого уровня.
Характерный представитель такого языка — Ассемб¬
лер. В нем имеется столько команд, сколько понимает
процессор, плюс еще чуть-чуть, в общем, больше тысячи,
поэтому его трудно и изучать, и использовать, хотя для
системных программистов это необходимо.
Понятие «низкий уровень» не говорит о том, что язык
плохой или несовершенный, — оно говорит только о
том, что он близок процессору. Большинство програм¬
мистов работают с языками программирования высо¬
кого уровня, далекими от процессора, но зато прибли¬
женными к человеку. К высокому уровню относится и
язык С++, который мы сейчас изучаем.
263
В языках программирования высокого уровня нет ника¬
ких средств для того, чтобы управлять регистрами про¬
цессора, чтобы посмотреть, что там хранится в данный
момент времени и в каком состоянии находятся те или
иные флажки флагового регистра. Но во всех языках
программирования есть операторы для организации
повторяющихся вычислений (циклов).
Цикл со счетчиком
Язык С++ имеет несколько видов оператора цикла. Мы
рассмотрим некоторые виды, а начнем с самого просто¬
го цикла — цикла со счетчиком. Он основан на специ¬
альном операторе, который называется оператором for.
Этот оператор представляет собой заголовок цикла. Он
организует цикл. А выполняется в ходе цикла (чаще
всего, многократно) оператор, который идет после него.
Знакомиться с оператором цикла лучше всего на при¬
мере. Допустим, мы хотим найти сумму первых десяти
чисел. Назовем сумму переменной S. Будем считать,
что она и все другие переменные, которые мы будем ис¬
пользовать, описаны в начале нашей программы.
S = 0;
for (i = 1; i < 11; i++)
S += i;
Давайте посмотрим, что здесь происходит. Переменная
S выполняет роль накопителя. При каждом такте сложе¬
ния в ней накапливается сумма, но сначала мы должны
обнулить это значение, иначе в конце получим неиз¬
вестно что.
Переменная i выполняет роль счетчика цикла. Еще ее
называют параметром цикла. Поскольку мы хотим
найти сумму первых десяти натуральных чисел, то наш
счетчик должен изменяться от 1 до 10. Настройку цик¬
264
ла определяют три отдельных оператора, записанных
в скобках после ключевого слова for. Первая часть, i=l
определяет начальную установку цикла. В данном
случае переменной i присваивается значение 1.
Вторая часть — это условие продолжения цикла. Оно
проверяется каждый раз, перед тем как выполнять
операторы внутри цикла. У нас это условие имеет вид
i<l 1. Наконец, последняя часть определения цикла это
приращение — оператор, изменяющий переменную
цикла после выполнения операторов цикла. Оператор
i++, использованный в данном случае, увеличивает зна¬
чение переменной i на единицу. Это снова краткая за¬
пись, присущая языку С++, которая может трактовать¬
ся как оператор присваивания.
Что происходит в данном цикле? Сначала переменной
i присваивается значение 1. Затем немедленно прове¬
ряется условие. В данном случае оно истинно, так что
выполняется оператор, находящийся внутри цикла.
После этого выполняется оператор приращения, а за¬
тем программа снова переходит к проверке условия.
Эти действия продолжаются, пока условие не станет
ложным (в нашем примере это произойдет, когда зна¬
чение переменной i станет равно 11). После этого вы¬
полнение цикла заканчивается, и программа перехо¬
дит к оператору, расположенному после цикла.
Не ограничивает ли нас то, что тело цикла состоит из
одного-единственного оператора? Вы уже должны сами
догадаться, что нет. Если нам надо поместить внутрь
цикла несколько операторов, мы должны заключить
их в фигурные скобки.
Как видите, все очень просто, и вы уже должны быть
готовы пройти небольшой контрольный тест. Каким
примерам циклов какие описания соответствуют?
(Предупреждение! В примерах есть ловушки для невни-
265
мательных. Не доверяйте именам переменных. Автор
мог назвать их, как хотел. Доверяйте только реальному
коду, то есть операторам.)
Описание
примера
Номер примера
(проставьте сами)
А.
Сумма первых пяти нечетных чисел
fT
Произведение первых пяти нечетных
чисел
Г
Квадрат числа 7
~
Сумма квадратов чисел 1, 4, 7
Важное замечание
Применяя циклы со счетчиком, надо с особым уваже¬
нием относиться к переменной цикла. Она автомати¬
чески наращивается внутри цикла, и, возможно, вам
придет в голову попытаться изменять ее своими рука¬
ми. Делать этого не надо ни в коем случае. Находясь
внутри цикла, не изменяйте значение переменной
266
Sum = 0;
for (i = 1; i<10; i+=3
{
proizv = i*i;
Sum += proizv;
}
for (i = 1; i<10; i+=3)
Proizv = i * i;
' Sum = 1;
for (i = 1; i<10; i+=2)
Sum *= i;
^Proizv = 0;
for (i = 1; i<10; i+=2)
Proizv += i;
цикла, так как после этого поведение программы может
стать непредсказуемым. После выхода из цикла — дело
другое. Можете делать с этой переменной все, что хотите.
Кстати, после выхода из цикла она сохраняет свое
последнее значение.
Как из барана сделать бурун
Оператор цикла можно применять не только для рабо¬
ты с числами, но и для работы с текстовыми данными.
Сейчас мы продемонстрируем, что можно сделать со
словом, например таким, как «баран». Мы будем заме¬
нять в нем буквы а на у, пока из «барана» не получится
«бурун».
В слове «баран» буква а встречается два раза. В нашем
примере мы об этом знаем, но представьте себе, что
преобразуемое слово прочитано из файла. Это значит,
что мы не знаем, сколько в нем букв а, и не знаем,
сколько раз надо повторять цикл. Цикл со счетчиком,
с которым мы уже познакомились, здесь применять
нельзя. Нам придется использовать другой тип цикла,
так называемый цикл while.
Цикл ujhile
Этот цикл отличается от цикла со счетчиком тем, что
часть работы, которую раньше брал на себя компиля¬
тор, теперь должен сделать сам программист. Например,
наш первый пример цикла может быть переписан так:
S = 0;
i = 1;
while (i <= 10) {
S += i;
i++;
}
267
После ключевого слова while стоит условие продолже¬
ния цикла. Если оно истинно, выполняется оператор,
составляющий тело цикла, в противном случае работа
цикла заканчивается.
После выполнения тела цикла проверка условия про¬
изводится снова. Переменные, входящие в условие,
должны иметь нужные значения перед входом в цикл
и изменяться внутри цикла. Это приводит к тому, что
первоначально истинное условие после нескольких
повторений цикла становится ложным и цикл завер¬
шается.
Оператор, образующий тело цикла, может быть состав¬
ным, как в данном случае, или одиночным.
Вернемся к нашим баранам
Вернемся к примеру работы со словом «баран». Сначала
надо создать переменную, в которой оно хранится. Та¬
кую переменную можно назвать как угодно, но в начале
программы ее надо описать, указав тип AnsiString, напри¬
мер:
AnsiString s;
Тогда далее мы можем написать:
s = "баран";
Здесь обязательны кавычки — они говорят программе
о том, что мы присваиваем строковое значение.
Как найти, в каком месте строки находится буква а?
Для этого можно использовать метод Pos. Это функция,
которая возвращает номер позиции, в которой находит¬
ся указанный символ в нашей строке. Если символ от¬
268
сутствует, возвращается значение 0. Теперь ясно, как
действовать.
1. Проверяем значение функции Pos.
2. Если оно равно нулю, преобразование закончено.
3. Иначе заменяем букву а в этой позиции буквой у.
Это можно сделать с помощью методов lnsert и
Delete.
4. Возвращаемся к пункту 1.
Чтобы понять, что наша программа описывает цикл,
представьте себе, что в строке очень много букв а. Тогда
мы будем все время повторять пункты: 1, 3, 4, 1, 3, 4,
1, 3, 4 — одни и те же действия. Записать этот цикл на
языке С++ можно так:
s = "баран";
while (s.Pos("a") != 0) {
n = s.Pos("a");
s.Delete(n, 1);
s.Insert("у", n);
; }
Здесь условие, которое стоит после ключевого слова
while, зависит от содержимого строки. Цикл заверша¬
ется, когда в строке не остается ни одной буквы а.
Строка s изменяется внутри цикла методами Delete и
lnsert. Метод Delete в данном случае удаляет из строки s
один символ (второй параметр) в позиции n (первый па¬
раметр). Метод lnsert вставляет строку "у" (первый пара¬
метр) в строку s, начиная с позиции n (второй параметр).
В ходе работы такой программы со словом «баран» бу¬
дут происходить следующие изменения.
269
s — "баран"
Pos{s, "а")
-2
Цикл продолжается
s - "буран"
Pos(s, "а")
-4
Цикл продолжается
s — "бурун"
Pos(s, "а")
-0
Цикл завершается
Обратите внимание на то, что наш цикл будет работать
правильно независимо от того, сколько букв а в исход¬
ной строке. Это верно, даже если в ней нет ни одной
нужной буквы! В этом случае условие ложно при пер¬
вой же проверке, и тело цикла вообще не выполняется.
Такая особенность этого типа цикла используется до¬
статочно часто.
О стиле программирования
Записывая последний цикл, мы ввели дополнительную
переменную n. В ней хранится номер позиции, которую
буква «а» занимает в изменяемом слове. Обязательно
ли было это делать? Нет, не обязательно. Можно было
записать наш цикл покороче:
s = "баран";
while (s.Pos("a") 1= 0) {
s.Delete(s.Pos("a"), 1);
s.Insert("y", s.Pos("a"));
}
Чего мы добились? В программе стало на одну строчку
меньше, зато сами строчки стали длиннее и сложнее.
Как правильно? Как лучше?
Сначала о правильности. Как ни близки два варианта
записи цикла, во втором случае функция Pos вызыва¬
ется на один раз чаще. Если бы функция делала что-то
еще, кроме вычисления значения (например, выводила
270
что-то на экран), то результаты работы программы
были бы другими. Такое действие называют побочным
эффектом и обычно считают дурной практикой про¬
граммирования. Однако иногда без этого не обойтись,
и в таком случае за вызовами подпрограмм, обладаю¬
щих побочным эффектом, надо следить особенно строго.
Теперь о том, как лучше. Сегодня в программировании
самое важное — это время. Время, которое может быть
потрачено на то, чтобы разобраться в программе или
процедуре или хотя бы вспомнить, что и как она делает.
Лучше написать лишних пять строк кода и еще десять
строк комментариев, чем оставить один эффектный
длинный оператор, взглянув на который невозможно
понять, что же он делает. Так что первый, более понят¬
ный вариант этой программы предпочтительнее.
Что токо© массив
Циклы в программах используются очень широко. С их
помощью, например, выполняют операции поиска
нужных слов в текстах или значений в списках. Когда
наш компьютер загружается, он попадает в бесконеч¬
ный цикл, в ходе которого опрашиваются клавиатура
и мышь в ожидании действий пользователя. Если
загрузить компьютер и ничего не делать, на нем будет
изображаться Рабочий стол Windows до тех пор, пока
в электросети по каким-то причинам не пропадет
напряжение.
Событийный механизм всех программ тоже основан на
каких-то циклах, работающих у них внутри, в ходе
которых программы проверяют, не произошло ли дол¬
гожданное событие. Но кроме этого у циклов есть еще
одно очень важное предназначение: они используются
для работы с наборами данных, которые называются
массивами.
271
Массив — это набор переменных или объектов, имею¬
щих общее имя. Выше мы говорили, что имя перемен¬
ной — это на самом деле ее адрес в памяти компьютера
и оно должно быть уникальным (неповторимым). Мас¬
сивы — это исключение из общего правила. Их исполь¬
зуют, когда множество переменных имеют много общего
и надо обработать их все сразу.
Давайте рассмотрим такой пример. В стратегической
игре рота танков в составе 14 машин заехала на минное
поле. Допустим, все машины подорвались на минах и
их свойство Скорость получило значение, равное нулю.
Если бы в программе скоростй каждого танка соответ¬
ствовала своя переменная, то нам пришлось бы напи¬
сать четырнадцать разных операторов присваивания.
А если скорость всех танков — это единый массив, то
он состоит из 14 элементов, каждый из которых имеет
номер, равный номеру танка. В этом случае можно со¬
здать один цикл, который обработает все 14 элементов
и каждому присвоит нулевое значение.
Используемый в программе номер элемента массива на¬
зывают индексом. В языке С++ нумерация элементов
массивов начинается с нуля. Записывается индекс в
квадратных скобках.
Например, чтобы указать пятый элемент массива а,
надо написать: a[4]. Элемент массива — это обычная
переменная, которую можно использовать по своему
усмотрению.
Например, оператор:
а[2] = a[0];
присвоит третьему элементу массива значение, равное
значению первого элемента.
272
Как массивы хранятся в памяти компьютера
Как хранятся переменные, мы уже знаем. В зависимо¬
сти от типа данных переменная получает больше или
меньше ячеек памяти. Например, для действительных
чисел надо выделить больше ячеек, чем для целых, а
для символьных строк надо выделить столько ячеек,
сколько в строке символов.
Напомним, что в большинстве языков программирова¬
ния (С++ не исключение) в начале каждой процедуры
программист должен указать, какие переменные в ней
используются и какие данные в них хранятся, чтобы
программа-компилятор отвела им необходимое место
в оперативной памяти.
Это относится и к массивам. Более того, в случае мас¬
сива мы должны указать и число элементов, ведь их
может быть и пять, и пять миллионов. Массив объяв¬
ляется как обычная переменная, но после идентифи¬
катора необходимо указать в квадратных скобках чис¬
ло элементов массива. Например, если в массиве
предполагается иметь 14 элементов, то надо написать:
int a[14];
Поскольку нумерация элементов массива начинается
с нуля, в программе можно использовать элементы
массива с индексами от 0 до 13.
Элементами этого массива будут целые числа. Компи¬
лятору эти сведения нужны, чтобы он мог рассчитать,
сколько ячеек памяти отдать массиву. Если, например,
каждому целому числу он отводит 4 байта, то всему
массиву достанется 56 байтов. В следующих ячейках
будут располагаться другие переменные или массивы.
10—1619
273
Индекс массива
Основное удобство массивов состоит в том, что индекс
любого элемента массива может быть задан не только
числом, но и выражением, например a[i + 1]. Это озна¬
чает, что для работы с элементами массива можно
использовать циклы.
Представьте себе, что наша танковая рота не вся заст¬
ряла на минном поле. Допустим, половина танков про¬
рвалась. В этом случае «обездвижить» те танки, кото¬
рым не повезло, можно с помощью такого цикла:
for (i = 0; i<7; i++)
S[2*i] = 0;
В этом случае «не повезет» танкам с четными индекса¬
ми (0, 2, 4 ... 12).
Мы можем ввести в игру элемент случайности. Допус¬
тим, судьба танка проверяется бросанием монетки.
Если «орел» — танк подорван, а если «решка» — ему
удалосьпроехать. Допустим, «орлу» соответствуетеди-
ница, а «решке» — ноль. Создавать случайные числа с
помощью функции random мы уже умеем: для этого в
качестве параметра надо указать число возможных со¬
стояний:
randomize();
for (i = 0; i<14; i++) {
k = random(2);
if (k == 1)
S[i] = 0;
>
274
Эта программа переберет все 14 танков, входящих в
роту, для каждого из них «подбросит монетку», и если
выпадет «орел» (то есть единица), свойство скорости
танка получит значение, равное нулю.
Многомерные массивы
Массив с одним индексом назывется одномерным. В нем
хранится как бы строчка со значениями, например с
числами. Но иногда нужно, чтобы одна переменная
содержала целую таблицу данных. В этом случае исполь¬
зуют многомерные массивы.
В языке С++ каждый индекс многомерного массива за¬
писывается в отдельных квадратных скобках. Напри¬
мер, вот так может выглядеть описание двумерного
массива, содержащего 100 элементов (10 строк и 10
столбцов):
int а [10] [10] ;
Чтобы обратиться к первому элементу такого массива,
надо написать a[0][0], последний элемент обозначается
как a[9][9].
(оловоломка для сообразительных
Допустим, у нас был массив a, состоящий из десяти
чисел. Программа создала из него массив b.
int a[10], b[10];
for (i = 0; i<10; i++) {
a[i] = i + 1;
k = 9 - i;
b[k] = a[i];
}
275
Разберитесь с тем, что делает программа, и ответьте на
следующие вопросы.
1. Чем отличается массив b от массива а?
2. Какое значение имеет переменная цикла i после
того, как работа цикла завершена?
3. Какое значение по окончании цикла имеет пере¬
менная k?
f?lQSQ 22
£щ© один электронный ольбом
Мы уже создали несколько программ и
познакомились с разными элементами
управления. В этой главе мы научимся
программировать работу переключате¬
лей
Что токое переключатели
С помощью переключателей можно выбрать одну из
нескольких настроек. Переключатели, в отличие от
флажков, никогда не встречаются по одному — только
группами. В группе всегда выбран ровно один переклю¬
чатель. При включении нового переключателя старый
сбрасывается.
По-английски переключатели называются «радиокнопками» —
Radio8utton. Такое название им дали за механизм работы, похо¬
жий на механизм кнопок радиоприемников и магнитол: при нажа¬
тии одной из кнопок отщелкивается то кнопко, котороя была вклю¬
чена ранее. 8 России термин «радиокнопки» не используют.
Ч
Как видите, в одном окне
может быть несколько
групп переключателей
Это диалоговое окно из программы Paint содержит две группы
переключателей: Единицы и Палитра
277
Описание программы
Тот, кто внимательно читал эту книгу, знает, что мы
уже создали один электронный альбом, который может
показывать любые рисунки.
На этот раз мы создадим интерактивный электронный
альбом, в котором пользователь сможет сам выбрать,
что он хочет посмотреть. Окно программы будет состо¬
ять из двух частей. Слева мы поместим набор пере¬
ключателей, содержащих набор «разделов» нашего
альбома. Альбом посвящен городам мира, поэтому в
качестве естественных разделов удобно использовать
части света.
Справа мы поместим раскрывающийся список, в кото¬
ром можно будет выбрать конкретный город. Содержи¬
мое списка будет меняться — оно зависит от того, какой
переключатель установлен. Под раскрывающимся
списком будет показана фотография выбранного
города.
Где взять виды городов? Где хотите, например в Интер¬
нете. Если вас не интересуют города, можете исполь¬
зовать собственные фотографии, кадры из любимых
игр и вообще все что угодно. Суть программы от этого
совершенно не изменится.
Вот что должно у нас получиться
278
Группа
переключателей
Создаем переключатели
Запустим систему С++ Builder и создадим новый проект.
Сохраним его в папке Города. Начнем с создания груп¬
пы переключателей. Система С++ Builder автоматически
относит переключатели, находящиеся внутри одного
контейнера, к одной группе. Такой подход опирается
на традицию заключения группы переключателей в
рамку. Давайте и мы поступим так же.
Выберите в палитре компонентов вкладку Standard
(Стандартные) и щелкните на значке GroupBox
(Рамка). Нарисуйте рамку в левой части формы. С помо¬
щью Инспектора объектов измените ее свойство Caption
(Заголовок) — введите название Части света.
Теперь можно создавать переключатели. Выберите в
палитре компонентов значок RadioButton (Переклю¬
чатель). Нарисуйте объект методом протягивания
в верхней части рамки.
На форму помещен первый переключатель
Нам нужны еще четыре переключателя, и для их со¬
здания мы используем метод копирования объектов
через буфер обмена. Пока объект RadioButton1 остается
выделенным, нажмите комбинацию клавиш CTRL + С.
Это стандартный прием операционной системы Windows
для помещения выделенного объекта в буфер обмена.
279
Произойдет его копирование в специальную область
оперативной памяти компьютера.
Теперь нажмите комбинацию клавиш CTRL + V — это ко¬
манда вставки объекта из буфера обмена. Система С++
Builder поместит на форму копию объекта RadioButton1.
Разумеется, в программе не может быть двух одинако¬
вых объектов, и система С++ Builder за этим следит. Она
автоматически изменит имя объекта — новый объект
получит имя RadioButton2. Перетащите этот объект так,
чтобы он располагался под первым.
Опять нажмите клавиши CTRL + V. Поместите новый
переключатель еще ниже в тот же столбец. Таким же
способом вставьте еще два переключателя. Важно,
чтобы все переключатели располагались в столбце в том
же порядке, в каком они были созданы: не перестав¬
ляйте их местами.
Группа переключателей на форме
Учимся аккуратности
Чтобы окно программы выглядело аккуратно, элемен¬
ты управления в нем необходимо выравнивать и распо¬
лагать через равные интервалы. Это особенно важно в
тех случаях, когда элементы управления однотипные,
как в нашем случае. Малейшая неровность в располо¬
жении одинаковых элементов управления сразу бро¬
сается в глаза.
280
В принципе, выровнять элементы управления в окне
можно ручным перетаскиванием, для этого на форме
изображена сетка, — но на самом деле так не поступают.
Ручное выравнивание — это слишком грубое выравни¬
вание, лучше научиться пользоваться специальными
командами выравнивания. Они действуют на те объек¬
ты, которые в данный момент выделены.
Выделите все пять переключателей. Проще всего это
сделать, поочередно щелкнув на каждом из переклю¬
чателей, удерживая нажатой клавишу SHIFT. Когдаубе-
дитесь, что все нужные элементы управления выделе¬
ны, дайте команду Edit >• Align (Правка ► Выравнивание).
Откроется диалоговое окно Alignment (Выравнивание).
Установите слева переключатель Left Sides (Левые края),
a справа переключатель Space Equally (Равные промежутки).
Щелкните на кнопке OK — и система автоматически
разместит элементы управления ровно и регулярно.
После этого переключатели в окне формы будут выгля¬
деть действительно аккуратно.
Следующая задача — подобрать для них подписи. По
очереди выберите каждый переключатель и измените его
свойство Caption (Заголовок). Каждая подпись — название
одной части света. В этом примере мы «обидели» Австра¬
лию; если хотите, добавьте еще один переключатель.
Задаем выравнивание переключателей
по горизонтали и по вертикали
281
Осталось последнее — включить первый переключа¬
тель при запуске программы. Для этого выберите его и
задайте для свойства Checked (Включен) значение true (Да).
После настройки переключателей форма должна выгля¬
деть так, как показано на рисунке.
Группа переключателей с подписями
Добавляем демонстрационные объекты
Остальные элементы управления также разместим в
рамке. В палитре компонентов выберите значок GroupBox
(Рамка), нарисуйте рамку справа от переключате¬
лей и измените свойство Caption (Заголовок) — вве¬
дите слово Города.
Раскрывающийся список и изображение поместим
внутрь рамки. Раскрывающийся список расположим
сверху. Выберите в палитре компонентов значок
ComboBox (Раскрывающийся список). Нарисуйте
объект в верхней части второй рамки. Сразу же с помо¬
щью Инспектора объектов измените свойство Style (Стиль) —
выберите для него значение csDropDownList (Раскрываю¬
щийся список).
Под раскрывающимся списком разместим изображе¬
ние. Откройте на палитре компонентов вкладку
Additional (Дополнительные) и выберите на ней ком¬
понент lmage (Изображение). Поместите этот
282
объект в рамку. На этом первичное формирование окна
нашей программы завершено.
Все объекты, которые увидит пользователь,
уже помещены на форму
Организация хранения данных
— Как завершено?! Нашей программе еще не известно
ни одно название города, и у нее нет информации ни об
одном рисунке. Нам надо хранить двадцать пять назва¬
ний городов и сведения о таком же количестве файлов
с изображениями. И почему мы, собственно говоря, не
занесли названия городов в раскрывающийся список?
— Потому что в раскрывающемся списке должны быть
не все названия, а только те, которые соответствуют
выбранному переключателю. То есть всего названий
двадцать пять, а в раскрывающемся списке их должно
быть только пять.
Поэтому хранить эти данные мы будем в виде обычного
списка (точнее говоря, в двух). Чтобы пользователь их
не видел, сделаем списки невидимыми. При установке
одного из переключателей программа скопирует в рас¬
крывающийся список только нужные пункты.
Щелкните на значке ListBox (Список) на вкладке
Standard (Стандартные) палитры компонентов и со¬
здайте список в любом месте формы. Повторите эту опе¬
рацию, чтобы создать еще один список. Снова выберите
первый список.
283
Найдите в окне Инспектора объектов свойство Visible (Ви¬
димый) и задайте для него значение false (Нет). В окне
формы ничего не изменится, но во время работы про¬
граммы этот элемент управления отображаться не будет.
Щелкните на свойстве ltems (Пункты), а затем на кнопке
построителя. Введите в список двадцать пять названий
городов — по пять для каждой части света.
Названия городов и имена файлов с изображениями
представлены в невидимых списках
Настройте аналогичным образом и второй список —
сделайте его невидимым и введите имена файлов, содер¬
жащих иллюстрации. Их тоже должно быть двадцать
пять. Вот теперь можно говорить о том, что создание
формы завершено, хотя кое-какие дополнительные на¬
стройки нам еще понадобятся.
В нашей программе мы воспользуемся тем фактом, что для каж¬
дой части света в списке задано одинаковое число городов. О том,
как изменить программу, чтобы избежать этого ограничения, мо¬
жете подумать самостоятельно.
284
Невидимые
списки
Так выглядит готовая форма нашей программы
Готовимся к программированию
Самая трудная часть программы связана с выбором
переключателя. Основное событие для переключателей —
это включение (Click). Оно возникает при включении лю¬
бого переключателя в группе, причем неважно, как это
сделано: щелчком мыши или нажатием клавиш ENTER
или ПРОБЕЛ. Процедура для обработки этого события
вызывается также при нажатии курсорных клавиш.
Чтобы создать эту процедуру, дважды щелкните на
любом из переключателей, хотя лучше все-таки на пер¬
вом. Мы используем эту процедуру для обработки
любого из переключателей. Но нам надо знать, какой
переключатель установлен, адля этого придется иссле¬
довать объект Sender, который передается в процедуру
обработки.
Объект Sender передается в процедуру
обработки события как параметр
285
void fastcall TFoml::RadioButtonlClick
(TObject *Sender)
{
}
— Что это за объект? — Тот самый, что вызвал собы¬
тие, то есть в нашем случае только что установленный
переключатель.
— Что нам надо сделать? — Несколько дел: во-первых,
заполнить раскрывающийся список нужными назва¬
ниями городов, во-вторых, выбрать в списке первый
пункт, в третьих, показать нужную картинку.
Самое трудное здесь — первая операция, потому что
для этого надо знать, какой переключатель был уста¬
новлен. Мы получаем его как объект Sender, но уточ¬
нить, о каком именно переключателе идет речь, мы
должны сами. Например, можно было бы сверить поло¬
жение элемента управления или его подпись, но все это
трудоемкие и не очень удобные операции.
Если бы мы могли как-нибудь маркировать наши пере¬
ключатели, чтобы сразу выяснить, какой из них вы¬
бран... Если бы у них было «лишнее» свойство...
Такое свойство есть у всех компонентов системы С++
Builder, и мы им уже пользовались в главе 20. Это спе¬
циальное свойство Tag (Тег). Оно никогда не использу¬
ется внутри системы С++ Builder и создано специально
для того, чтобы программист мог использовать его для
передачи дополнительных данных.
Слово Tog переводится с английского как «ярлык» или «этикетка».
8 программировании, когда речь идет о пометке или разметке,
его обычно не переводят, а просто записывают русскими буква¬
ми: «тег».
Значение свойства Tag (Тег) — целое число. Конечно,
можно было бы просто пронумеровать наши переклю¬
чатели, но мы сделаем еще лучше.
Первому переключателю соответствуют названия горо¬
дов с 0 по 4, второму — с 5 по 9 и так далее. Пятому
переключателю соответствуют значения с 20 по 24.
В качестве значения свойства Tag (Тег) мы используем
286
номер первого из нужных элементов. Это позволит нам
быстро выяснить, какие именно пункты надо занести
в раскрывающийся список.
Итак, с помощью Инспектора объектов дадим свойству Tag
(Тег) для первого переключателя значение 0, для вто¬
рого — 5, для третьего — 10, для чет¬
вертого — 15, для пятого — 20. Вот
теперь можно приступать к программированию.
Программируем переключатели
Мы уже создали заготовку процедуры RadioButtoniClick.
Она должна вызываться при установке любого переклю¬
чателя. Поэтому прежде чем добавлять в нее какие-
либо операторы, мы воспользуемся вкладкой Events (Собы¬
тия) Инспектора объектов, чтобы назначить ее для обработки
события OnClick (При щелчке) всех переключателей.
Вернемся в окно кода и приступим к написанию про¬
граммы. Что должно произойти, когда один из переклю¬
чателей установлен? Содержимое раскрывающегося
списка должно измениться так, чтобы соответствовать
выбранной части света. Старое содержимое должно
удалиться, а на его место должны скопироваться нуж¬
ные пункты из невидимого списка. После этого про¬
грамма должна выбрать первый пункт раскрываю¬
щегося списка и показать соответствующую ему
картинку.
Давайте приступим к реализации этого плана. Очис¬
тить раскрывающийся список можно с помощью метода
Clear:
ComboBoxl->Clear();
Теперь надо сформировать новое содержимое списка.
Мы знаем, что в него должны войти пять пунктов из
287
невидимого списка ListBox1. Номер первого добавляемо¬
го пункта определяется свойством Tag (Тег) объекта
Sender.
Это значение понадобится нам и тогда, когда пользо¬
ватель выберет название города из раскрывающегося
списка. Поэтому стоит запомнить его в свойстве Tag (Тег)
самого раскрывающегося списка. Но если мы просто
напишем:
ComboBoxl->Tag = Sender->Tag;
система С++ Builder выдаст нам сообщение об ошибке.
Почему так произойдет? Потому что мы-то знаем, что
объект Sender — это обязательно переключатель, а для
системы С++ Builder это сейчас просто какой-то объект,
у которого может и не быть свойства Tag (Тег).
Нам надо явно указать, что система должна считать
этот объект переключателем. Для этого нужно доба¬
вить команду преобразования типа. Оператор присва¬
ивания запишется так:
ComboBoxl->Tag = ((TRadioButton *)Sender)->Tag;
Теперь нужно скопировать пять элементов, начиная с
нужного, из списка ListBox1 в раскрывающийся список
ComboBox1. Это удобнее всего сделать при помощи цикла:
for (i=0; i<5; i++)
ComboBoxl->Items->Add
(ListBoxl->Items->Strings[ComboBoxl->Tag+i]);
Нам пришлось использовать переменную цикла i, кото¬
рую надо описать. Добавим после открывающей фигур¬
ной скобки строку:
288
int i;
Теперь осталось программно выбрать первый элемент
раскрывающегося списка и показать картинку, соот¬
ветствующую этому элементу. Это можно сделать при
помощи таких операторов:
ComboBoxl->ItemIndex = 0;
Imagel~>Picture~>LoadFromFile
(ListBox2->Items->Strings[ComboBoxl->Tag]);
Программируем раскрывающийся список
Самую сложную часть программы мы уже написали.
Остальные процедуры будут намного проще. Напри¬
мер, при выборе пункта из раскрывающегося списка
достаточно заменить рисунок. Выберите раскрываю¬
щийся список на форме, перейдите в окно Инспектора
объектов и откройте вкладку Events (События).
Дважды щелкните напротив события OnChange (При
изменении), которое возникает, когда в списке выбира¬
ется другой пункт. Система С++ Builder создаст процеду¬
ру обработки этого события.
В нее достаточно добавить всего один оператор:
Imagel->Picture->LoadFromFile
(ListBox2->Items->Strings
[ComboBoxl~>Tag+ ComboBoxl->ItemIndex]);
Начальная установка
Может показаться, что программа уже готова. Давайте
попробуем запустить ее. Нажмите клавишу F9.
289
Пока что в момент запуска программы
окно выглядит пустым
Легко убедиться, что сразу после запуска программы
наш раскрывающийся список пуст и никакой картинки
под ним нет. Но стоит щелкнуть на любом переключа¬
теле, как программа начинает работать так, как мы и
задумали. Это должно подсказать нам, что мы упустили
из виду начальную установку программы. Давайте
щелкнем на форме вне элементов управления, откроем
вкладку Events (События) Инспектора объектов и создадим
процедурудля обработки события OnCreate (При создании).
void fastcall TForml::FormCreate
(TObject *Sender)
{
}
Мы можем долго размышлять, что должно происхо¬
дить при начальной установке программы, если нас не
осенит нужная мысль: при начальной установке дол¬
жно произойти то же самое, что при установке первого
переключателя. А это значит, что нам достаточно вы¬
звать процедуру обработки переключателей, передав в
нее в качестве параметра объект — первый переклю¬
чатель. Итак, нам нужен всего один оператор:
RadioButtonlClick(RadioButtonl);
290
Программа готова
Вот теперь наша программа действительно готова. Она
выполняет довольно сложные действия, но сама по себе
получилась простой. Давайте посмотрим на написан¬
ные нами операторы:
void fastcall TForml::RadioButtonlClick
(TObject *Sender)
{
int i;
ComboBoxl->Clear();
ComboBoxl->Tag =
( (TRa.dioButton * ) Sender) ->Tag;
for (i=0; i<5; i++)
ComboBoxl->Items->Add
(ListBoxl->Items->Strings[ComboBoxl->Tag+i]);
ComboBoxl->ItemIndex = 0;
Imagel->Picture->LoadFromFile
(ListBox2->Items->Strings[ComboBoxl->Tag]);
>
void fastcall TForml::FormCreate(TObject
*Sender)
{
RadioButtonlClick(RadioButtonl);
>
void fastcall TForml::ComboBoxlChange(TObject
*Sender)
{
Imagel->Picture~>LoadFromFile
(L i s tBox2 - > 11 ems -- > S t r ings
[ComboBoxl- >Tag f CoiPX>oBoxl->ItemIndex] ) ;
}
291
Запустите программу, нажав клавишу F9. Теперь на¬
чальный вид окна сразу будет правильным.
Наконец-то программа работает правильно
При установке другого переключателя состав раскры¬
вающегося списка и рисунок сразу же изменяются.
Более того, если вы раскроете список и будете выби¬
рать его пункты в таком виде, изображение будет ме¬
няться сразу в момент выбора нового пункта, пусть
даже список его частично закрывает. Программа при
этом не испытывает никаких проблем.
Картинки обновляются, даже когда список раскрыт
292
Глово 23
Изучаем список флажков
В этой главе мы создадим компьютер¬
ную версию одной из простых головоло¬
мок Самуэля Лойда. Лойд — известный
американский автор головоломок и шах¬
матных задач — жил и работал во вто¬
рой половине XIX и в начале XX века. Мы
используем в этой головоломке список
флажков и еще один дополнительный
список. Заодномыузнаему какдобавлять
и удалять элементы списка
Головоломко, которую мы решаем
Большинство головоломок Лойда оформлено в виде
небольших рассказов. Нам, к сожалению, придется
отбросить все «украшения» и сохранить только суть
задачи. Она вроде бы проста: из заданного набора чисел
надо выбрать те, сумма которых соста¬
вит 50. Числа, которые Лойд избрал для
своей головоломки, такие: 25, 27, 3, 12,
6, 15, 9, 30, 21, 19. Порядок их записи
здесь тот же, что и у автора, мы его про¬
сто сохранили. Несмотря на кажущуюся
простоту, головоломка может доставить
немало хлопот.
Какие элементы управления нам понадобятся? Во-пер-
вых, мы используем список флажков. Он похож на
обычный список, но возле каждого пункта имеется
флажок. Его можно установить или сбросить. Пример
использования списка флажков можно найти в опе¬
рационной системе Windows. Он применяется в диало¬
Самуэль Лойд
293
Пример использования списка флажков в
операционной системе Windows
Еще нам понадобится обычный список, без флажков.
В него мы поместим выбранные числа. Сумму этих чисел
покажем в виде надписи. Окно нашей программы дол¬
жно выглядеть примерно так, как показано на рисунке.
Вот что должно у нас получиться
Свойства: Установка и удаление программ
294
говом окне установки и удаления компонентов (Пуск >
Настройка > Панель управления >• Установка и удаление про¬
грамм > Установка Windows).
Эта иллюстрация к головоломке принадлежит самому Лойду
Форма и компоненты
Запустите систему С++ Builder и создайте новый проект.
Сохраните его под именем Головоломка. Мы уже решили,
что нам нужны три элемента управления: список флаж¬
ков, простой список и надпись. При работе программы
активен только список флажков, остальные элементы
управления служат только в качестве подсказки.
Создаем и настраиваем список флажков
Начнем оформление программы со списка флажков.
Выберите в палитре компонентов вкладку Additional (До¬
полнительные) и щелкните на значке CheckListBox (Список
флажков). Нарисуйте список в левой части формы
методом протягивания. Область списка должна
295
получиться высокой и узкой. Мы уточним размеры и
вид этого объекта впоследствии.
Сравните обычный список и список флажков
Теперь надо задать состав списка. В окне Инспектора
объектов щелкните на свойстве ltems (Пункты), а затем на
кнопке построителя. Откроется диалоговое окно String
List editor (Редактор списка строк). Введите число 25 и на¬
жмите клавишу ENTER. Затем введите очередной эле¬
мент списка и снова нажмите клавишу ENTER. Нужные
числа приведены в начале главы. После того как все
десять чисел введены, нажмите кнопку ОК.
Теперь вернемся к оформлению списка флажков. С по¬
мощью маркеров изменения размеров подберите его
размер по вертикали так, чтобы все числа поместились
в список (без полос прокрутки) и при этом в нем не оста¬
валось бы свободного места. Точно так же задайте раз¬
мер списка по горизонтали. Форма должна выглядеть
так, как показано на следующем рисунке.
296
Форма со списком флажков
Создаем дополнительные объекты
Создадим второй список. В палитре компонентов
выберите вкладку Standard (Стандартные) и щелк¬
ните на значке ListBox (Список). Нарисуйте второй
список справа от первого. Пусть его ширина будет при¬
мерно такой же, а высота — поменьше. Никакой на¬
стройки для этого объекта не требуется — первоначально
второй список пуст. Заполняться он будет при работе
программы.
Щелкните на значке Label (Надпись) на панели ком¬
понентов. Нарисуйте надпись ниже второго спис¬
ка. На панели свойств разыщите свойство Caption (Заго¬
ловок). Введите в него текст: «Сумма: 0». Это начальное
значение суммы выбранных чисел (пока ничего не вы¬
брано). Содержание надписи мы также будем изменять
в ходе работы программы.
В пределах надписи должно оставаться свободное место.
В зависимости от того, какие числа выбраны, сумма
может быть двузначной и даже трехзначной. Нужно,
чтобы результат всегда помещался в отведенное ему
место. Поэтому надо задать для свойства AutoSize (Авто¬
подбор) значение false (Нет), после чего несколько увели¬
чить область надписи. Давайте также зададим для свой-
CTBaAlignment (Выравнивание) значение: taCenter (По центру).
297
После того как в форму включены все нужные объекты,
можно уточнить размеры окна. Чтобы их нельзя было
менять во время работы программы, можно задать для
свойства BorderStyle (Тип границы) формы значение bsSingle
(Тонкая). Окно программы готово, и можно приступать
к программированию.
Пример готовой формы
Установка и сброс флажков
В окне программы только один элемент управления
способен к диалогу — это список флажков. Программа
должна что-то делать только в том случае, если один
из флажков устанавливается или сбрасывается. В этом
случае возникает событие ClickCheck (Переключение флаж¬
ка). Нам надо описать реакцию на него.
Выберите список флажков на форме, откройте вклад¬
ку Events (События) в окне Инспектора объектов, дважды
щелкните напротив события OnClickCheck (При переклю¬
чении флажка). Система С++ Builder создаст заготовку про¬
цедуры CheckListBox 1ClickCheck для обработки этого собы¬
тия.
Для какого пункта изменилось состояние флажка?
Неизвестно. Эта информация в нашу процедуру не по¬
ступает. Поэтому будем действовать так. При изменении
состояния какого либо флажка полностью очистим
вспомогательный список, а затем сформируем его за¬
298
ново. Так как наши списки довольно короткие, эта опе¬
рация будет выполняться очень быстро.
Очистка списка выполняется при помощи метода Clear,
поэтому первый оператор процедуры будет выглядеть так:
ListBoxl->Clear();
Дальше нам надо пробежаться по всем пунктам списка
флажков и проверить, был ли флажок установлен или
сброшен. Для этого нам понадобится оператор цикла:
for (i = 0; i < CheckListBoxl->Items~>Count; i++)
Мы ввели переменную цикла i, которую необходимо
описать.
Теперь надо проверить, какие флажки установлены.
Для этого у списка флажков имеется свойство Checked.
Это логический массив, в котором столько же элемен¬
тов, сколько пунктов в списке. Оператор проверки со¬
стояния флажка должен выглядеть так:
if (CheckListBoxl->Checked[i])
Если флажок установлен, следует добавить во второй
список новый пункт. Это делается при помощи метода
Add свойства ltems (Пункты). Содержимое добавляемого
пункта передается в качестве параметра. Нам нужен
такой оператор:
ListBoxl->Items->Add
(CheckListBoxl~>Items~>Strings[i]);
Свойство Strings представляет собой массив строк, содер¬
жащих текст пунктов списка. На этом формирование
вспомогательного списка заканчивается.
299
Обновление суммы
Итак, списки обновлены. Теперь надо заново подсчи¬
тать сумму выбранных чисел. Здесь нас ждет небольшой
подводный камень. Хотя элементы списка выглядят
как числа, они на самом деле являются текстовыми
строками. Поэтому при суммировании нам придется
воспользоваться функцией StrTolnt.
Чтобы этой функцией можно было пользоваться, в на¬
чало программы надо добавить строку:
#include <stdlib.h>
Суммирование удобно выполнить в цикле. Пусть сум¬
ма накапливается в переменной s, которую также необ¬
ходимо описать в начале процедуры. Ее расчет можно
выполнить так:
s = 0;
for (i = 0; i < ListBoxl->Items->Count; i++)
s += StrToInt(ListBoxl->Items->Strings[i]);
Чтобы представить сумму в поле надписи, она должна
быть текстовой строкой. Присоединим ее к тексту под¬
сказки с помощью операции слияния строк:
Labell.Caption = "Сумма: " + IntToStr(s);
Проверка решения
Осталось сделать последний шаг: проверить, решенали
головоломка. Так как значение суммы выбранных
чисел у нас уже есть, надо сравнить его с заданным чис¬
лом 50:
if (s == 50) {
300
Если головоломка еще не решена, делать ничего не надо
и решение можно продолжать. Если же решение най¬
дено, отключим все элементы управления (изменив свой¬
ство Enabled). Кроме того, выведем в поле надписи сооб¬
щение «Победа». Одним оператором это записать нельзя,
поэтому мы и используем фигурные скобки:
Labell->Caption = "Победа: " + IntToStr(s);
CheckListBoxl~>Enabled = false;
ListBoxl->Enabled = false;
}
Осталось только описать использованные переменные.
После открывающей фигурной скобки в начале проце¬
дуры добавьте следующий оператор:
int i, s;
Программа готова
Ha этом наша программа готова, и ее можно испытать.
Ее текст, как он виден в окне кода, показан ниже.
void __fastcall TForml::CheckListBoxlClickCheck
(TObject *Sender)
{
int i, s;
ListBoxl->Clear();
for (i = 0;
i < CheckListBoxl->Items~>Count; i + +)
if (CheckListBoxl->Checked[i])
ListBoxl->Items->Add
(CheckListBoxl->Items->Strings[i]);
s = 0;
for (i = 0; i < ListBoxl->Items->Count; i + +)
s += StrToInt(ListBoxl->Items->Strings[i]);
301
Labell->Caption = "Сумма: " + IntToStr(s);
if (s == 50) {
Labell->Caption = "Победа: " + IntToStr(s);
CheckListBoxl->Enabled = false;
ListBoxl->Enabled = false;
}
>
Нажмите клавишу F9, чтобы запустить программу. При
установке флажков выбранные числа попадают во вто¬
рой список, а при их сбросе — удаляются из него.
Если добавить во второй список слишком много чисел,
они перестают помещаться в область элемента управ¬
ления, и тогда в списке появляется полоса прокрутки.
Она позволяет увидеть любой из пунктов списка. Но
если область списка имеет недостаточную шири¬
ну, то полоса прокрутки может перекрывать пун¬
кты списка. В этом случае надо закрыть програм¬
му и увеличить ширину списка на форме.
Когда программа заработает, попробуйте решить голо¬
воломку. Это далеко не самая сложная из головоломок
Лойда. На всякий случай примите подсказку: чтобы
получить сумму 50, надо выбрать три числа.
Головоломка в ходе решения
302
Глово 24
Разыгрываем сражение
Среди компьютерных игр популярны ро¬
левые, в которых отряд бойцов побеж¬
дает полчища монстров, обычно в ито¬
ге спасая мир от гибели. Сейчас мы
напишем несложную программу, кото¬
рая моделирует слш ход сражения меж¬
ду героями и враг(ши.
Кок устроен бой
Итак, предположим, что в компьютерном мире столк¬
нулись герой-рыцарь и монстр, какой-нибудь тролль.
Рыцарь вооружен верным двуручным мечом, а тролль —
грубой (но очень большой
и тяжелой) дубинкой.
И вот они вступают в бит¬
ву. Обычно вероятность
того, что удар достиг це¬
ли, определяется исходя
из прочности доспехов.
При удачном ударе вы¬
числяется нанесенный
урон. Вот этим-то мы сей¬
час и займемся.
Величина нанесенного повреждения определяет¬
ся свойствами оружия. Но один удар может ока¬
заться сильнее, другой — слабее. Все это в свой¬
ствах оружия учитывается. Например, числовая
характеристика двуручного меча рыцаря может
записываться так:
2d6+2
303
Что это значит? Все очень просто. Обозначение d6 озна¬
чает игральную кость. Буква d — это первая буква анг¬
лийского слова die (игральная кость). Число 6 означает
число граней игральной кости. Двойка перед этой за¬
писью означает, что кость надо кинуть дважды (или
кинуть две одинаковые кости).
Результаты бросков надо сложить и потом прибавить
к ним число, которое стоит в конце. Например, если
при броске выпали грани с цифрами 2 и 4, то мы скла¬
дываем (2+4)+2 и получаем, что рыцарь нанес троллю
повреждение в 8 единиц. Если тролль сильный, то это¬
го будет недостаточно, чтобы его убить, и он может на¬
нести ответный удар.
Пусть тяжелая дубинка тролля имеет характе¬
ристики 1d4+5.
Обозначение d4 указывает, что необходимо бро¬
сить четырехгранную кость. Вы можете спро¬
сить: а разве такие кости бывают? Да, бывают.
Мы в основном знакомы с шестигранными иг¬
ральными кубиками, но в мире используются кости
самых разных размеров. Например, в настольной сис¬
теме ролевых игр Dungeons & Dragons используются
4-гранная, 6-гранная, 8-гранная, 10-гранная, 12-гран-
ная и 20-гранная кости.
Кроме того, в этой системе еще
иногда нужна стогранная кость.
Такой кости в природе не суще¬
ствует, и результат такого броска
получают, бросив две десяти¬
гранные кости. Одна из них про¬
нумерована от 1 до 10, а другая —
от 00 до 90. Результат броска по¬
лучают, сложив выпавшие зна¬
чения.
Игральные кости для
игровой системы
Dungeons & Dragons
304
Итак, если на четырехгранной кости выпала единица,
урон, который понес рыцарь, составляет 1+5=6 еди¬
ниц. Право удара снова переходит к рыцарю, и он бьет
опять. Битва продолжается, пока один из противников
не погибнет (иногда кто-то из участников сражения
может обратиться в бегство).
Как вычисляет исход боя компьютер? Естественно,
никаких игральных костей внутри процессора нет. Но
они ему и не нужны. Генератор случайных чисел позво¬
ляет получить случайное число из заданного диапазо¬
на, то есть фактически бросить игральную кость с про¬
извольным числом граней. Число бросков также не
ограничено.
Давайте попробуем создать такую программу, которая
позволит нам моделировать результат применения ору¬
жия, заданного в такой системе. Например, если дву¬
ручный меч имеет характеристики 2d6+2, то мы должны
иметь возможность указать, что мы бросаем две шести¬
гранные кости и хотим прибавить к результату два.
Программа должна выдать нам одно число, в предыду¬
щем примере это было число восемь.
Описание формы
Мы постараемся предоставить пользователю нашей
программы все удобства, но, в то же время, несколько
его ограничим. Конечно, можно представить себе, что
кто-то захочет бросить 2382 кости с 3154 гранями каж¬
дая, но на практике это вряд ли кому-то понадобится.
Поэтому ограничим число костей десятью, а число гра¬
ней у каждой кости — числом 100.
Постоянная добавка к мощности оружия может быть
и отрицательной. Например, какой-нибудь ржавый
кухонный нож вполне может иметь характеристику
1d2-1. Вы не можете представить себе двугранную
11—1619
305
кость? Тогда представьте себе монету, которая может
упасть «орлом» или «решкой». Такой нож в половине
всех случаев вообще не наносит никакого поврежде¬
ния, а во второй половине уменьшает жизнь врага лишь
на единицу. С учетом ограничений на число и «мощ¬
ность» костей будем считать, что «бонус» может изме¬
няться в диапазоне от -100 до 100.
Итак, характеристики оружия задаются тремя числа¬
ми: числом бросаемых костей, количеством граней
каждой кости и постоянным бонусом. Нам надо обес¬
печить возможность ввода трех чисел. Для этой цели
понадобятся три числовых поля. Но мы пойдем не¬
множко дальше. К первому полю мы присоединим
счетчик. Так как число костей невелико, его удобно
уменьшать или увеличивать при помощи счетчика.
Для задания числа граней кости мы применим поле со
списком. Прииспользовании «стандартной кости» число
граней можно выбрать из раскрывающегося списка.
Если же захочется применить, скажем, 37-гранную
кость, это тоже возможно, но число граней придется
ввести с помощью клавиатуры.
Наконец, для ввода значения
бонуса будем использовать
обычное поле ввода.
Еще нам нужна командная
кнопка для вычисления ре¬
зультата. Бросок костей и под¬
счет очков будет выполнен при
ее нажатии. Кроме того, нам
необходима область для отобра¬
жения результата. Обычно для
этой цели используют надпись.
Но в системе С++ Builder надпись
306
имеет тенденцию сливаться с окном формы. Конечно,
надпись можно поместить на панель. Но мы вместо
этого применим для выдачи результата текстовое поле.
Не пропустили ли мы чего-нибудь? Конечно, пропус¬
тили. Нам понадобятся дополнительные надписи,
чтобы разъяснить назначение элементов управления.
В целом, окно может выглядеть так, как показано на
рисунке.
Форма и компонента
Начнем создавать форму для нашей программы с са¬
мого простого. Сначала заменим заголовок формы, за¬
дав для свойства Caption (Заголовок) значение Бросаем
кости! Потом найдем в палитре компонентов зна¬
чок Label (Надпись) и создадим вдоль левого края
формы три надписи друг под другом. Для всех
трех надписей изменим свойство Caption (Заголовок),
записав, соответственно: Число граней:, Число костей: и
Бонус:. Форма будет выглядеть так, как показано на ри¬
сунке.
Заготовка окна будущей программы
307
Создаем поле со счетчиком
Далее приступим к созданию элементов управления
для ввода параметров оружия. Число бросаемых костей
мы хотим задавать при помощи поля со счетчиком. Но
надо знать, что операционная система Windows (и вслед
за ней система С++ Builder) рассматривает их как два эле¬
мента управления, хотя и тесно связанных друг с дру¬
гом. Поэтому и создавать их придется по отдельности.
Сначала создадим текстовое поле. Выберите ком¬
понент Edit (Текстовое поле) на палитре компонен¬
тов и разместите его справа от первой надписи. Сразу
измените свойство Text (Текст) — введите туда число 1.
Это будет означать, что сразу после запуска программа
настроена на бросок одной-единственной кости.
Теперь надо создать счетчик. Выберите на палит¬
ре компонентов вкладку Win32, а на ней — ком¬
понент UpDown (Счетчик). Поместите его рядом с тексто¬
вым полем справа. Размещать его точно необязательно,
и сейчас вы поймете почему.
Приступим к настройке счетчика. Нам нужно задать
значения для свойств Min (Минимальное значение), Мах
(Максимальное значение) и Increment (Приращение). Первые
два свойства указывают, в каком диапазоне изменяется
значение, выбираемое с помощью счетчика. Свойство
Increment (Приращение) показывает, на сколько единиц
меняется число в поле при щелчке на кнопках счетчи¬
ка. Зададим для этих свойств значения 1, 10 и 1.
Теперь нам осталось присоединить счетчик к полю. Для
этого найдите свойство Associate (Присоединение). Оно со¬
держит раскрывающийся список объектов, к которым
308
счетчик может быть присоединен. Вы¬
берите в этом списке объект Edit1. Обра¬
тите внимание, что сразу после этого счетчик автома¬
тически располагается вплотную к текстовому полю и
приобретает нужную высоту. Более того, нам теперь не
понадобится его программировать — при использова¬
нии счетчика текст в поле сразу претерпит нужные из¬
менения, а при изменении значения в поле вручную
счетчику также станет сразу известно об этом.
Создаем поле со списком
Для создания поля со списком служит компонент
ComboBox (Поле со списком) вкладки Standard (Стан¬
дартные) палитры компонентов. Выберите его и размес¬
тите ниже поля со счетчиком. Подберите его размеры
таким образом, чтобы ширина полей была одинаковой.
Тип поля со списком задается свойством Style (Стиль). В дан¬
ном случае нас устраивает заданный по умолчанию вари¬
ант csDropDown. Он как раз и описывает раскрывающий¬
ся список, допускающий ввод дополнительных значений.
Теперь зададим стандартное содержимое списка. Щелк¬
ните на свойстве ltems (Пункты), а затем на кнопке постро¬
ителя. Откроется диалоговое окно String List editor (Редак¬
тор списка строк).
309
Заполните это окно стандартными значениями для иг¬
ральных костей, как показано на рисунке, и щелкните
на кнопке ОК.
Осталось выбрать число, которое отображается в списке
после запуска программы. Оно определяется свойством
Text (Текст). Занесите сюда число 6 (оно соответствует
обыкновенному игральному кубику).
Добавляем поле и кнопку
Ввод значения бонуса несложен — для этого нужно
обычное текстовое поле. Выберите нужный ком¬
понент и создайте поле ниже поля со списком.
Сразу измените для объекта Edit2 свойство Text (Текст).
Пусть начальное значение бонуса будет 0. В этот мо¬
мент наша форма должна выглядеть так, как показа¬
но на рисунке.
Основные элементы управления уже помещены на форму
Следующий наш шаг — добавление кнопки. Выберите
в палитре компонентов компонент Button (Кнопка)
и нарисуйте на форме широкую кнопку. В каче¬
стве свойства Caption (Заголовок) задайте строку
Бросить кости!
310
Добавляем поле вывода
Мы с вами решили, что для отображения результата
броска воспользуемся текстовым полем. Но вводить в
него данные вручную мы запретим, так что оно будет
не полем ввода, а полем вывода. Итак, в третий раз до¬
бавим на форму компонент Edit (Текстовое поле). На
этот раз нам придется отнестись к его настроике
серьезнее, чем раньше. Сначала выберем в Инспек¬
торе объектов свойство Font (Шрифт) и щелкнем на кнопке
построителя. В открывшемся диалоговом окне Выбор
шрифта зададим для этого поля шрифт — Arial, начерта¬
ние — полужирное, размер — 72. Затем щелкните на
кнопке ОК.
Высота текстового поля автоматически увеличится,
чтобы крупные символы помещались в него. Давайте
заменим также текст в поле. Изменим свойство Text
(Текст), занеся в него строку (шесть знаков «-»). Это
будет как бы «прочерк», показывающий, что резуль¬
тата броска костей пока нет. Чтобы подчеркнуть это,
сделаем цвет фона поля красным — зададим для свой¬
ства Color (Цвет) значение clRed.
И наконец, осталось самое важное: запретить пользо¬
вателю изменять содержимое
текстового поля вручную. Для
этого надо присвоить свойству
ReadOnly (Только чтение) значение
true (Да). Если текст доступен
только для чтения, то во время
работы программы его нельзя
изменить.
На этом формирование окна
программы завершено. Со все¬
ми необходимыми объектами
форма выглядит так.
311
Приступаем к программированию
Нам придется использовать генератор случайных чи¬
сел и другие стандартные функции, поэтому начнем с
добавления в начало программы строки:
#include <stdlib.h>
Главное действие, которое должна выполнять програм¬
ма, — расчет результатов броска костей при щелчке на
кнопке. В этом случае мы должны подсчитать резуль¬
тат броска и выдать его в поле Edit3.
Все это выполняется очень просто. Начнем с того, что
выберем в окне формы кнопку, в Инспекторе объектов
откроем вкладку Events (События) и дважды щелкнем
напротив события OnClick (При щелчке). Система С++
Builder создаст заготовку для процедуры обработки щел¬
чка:
void fastcall TForml::ButtonlClick
(TObject *Sender)
{
}
Нам понадобятся дополнительные переменные. Вычис¬
лять результат броска мы будем, накапливая сумму
выпавших очков в переменной s. Кроме того, нам при¬
дется использовать цикл по числу используемых кос¬
тей. Обозначим переменную цикла через i. Итак, доба¬
вим после открывающей фигурной скобки следующую
строку:
int i,s;
312
Выполнить расчет нетрудно. Единственная особен¬
ность состоит в том, что все параметры оружия хранятся
у нас в свойствах, которые представлены текстовыми
строками. Поэтому перед расчетом необходимо преоб¬
разовать строку в число с помощью стандартной функ¬
ции StrTolnt.
В качестве начального значения переменной s возьмем
величину бонуса:
s=StrToInt(Edit2->Text);
Добавлять результаты броска костей будем в цикле. Это
делают следующие операторы:
for (i=sl; i<=StrToInt(Editl->Text); i + + )
s += l+random(StrToInt(CoraboBoxl->Text));
Теперь нам надо изменить содержимое надписи Edit3 и
ее цвет (чтобы пометить успешный бросок). Это можно
сделать так:
Edit3->Text = IntToStr(s);
Edit3~>Color = clLime;
Нам пришлось преобразовать число обратно в строку с
помощью функции lntToStr.
Обработка числоеых полей
Теперь вернемся к вопросу настройки параметров ору¬
жия. Фактически, нам надо задать три числа, и мы де¬
лаем это при помощи трех числовых полей. Тот факт,
что одно из этих полей соединено со счетчиком, а дру¬
гое — со списком, сути дела не меняет. Когда одно и то
же действие надо выполнить несколько раз с мини¬
мальными отличиями, лучше всего написать для этого
специальную процедуру.
313
Итак, мы решили написать специальную функцию для
обработки всех трех полей. Что она должна сделать?
Она должна проверить, что пользователь действительно
ввел число, а не какую-нибудь ерунду, вроде своего
имени, она должна убедиться, что число лежит в нуж¬
ном диапазоне, и еще она должна привести получен¬
ное значение к стандартному виду.
А теперь предположим, что такая функция у нас уже
есть. Пусть она называется SetTo. В нее необходимо пере¬
дать следующие параметры: содержимое данного поля
(свойство Text), минимально допустимое числовое зна¬
чение и максимально допустимое значение.
Результатом вызова функции должно быть «обработан¬
ное» значение поля, такое, которое удовлетворяет всем
заданным ограничениям.
Так вот, если такая функция у нас уже есть, мы можем
легко написать процедуры обработки событий, связан¬
ных с изменением содержимого наших полей. Каждая
из них будет содержать только один дополнительный
оператор. Начнем с первого поля, которое определяет
число бросаемых костей. Выберите его на форме, от¬
кройте в Инспекторе объектов вкладку Events (События) и
создайте процедуру обработки события OnChange (При
изменении). В нее надо добавить всего один оператор:
Editl->Text = SetTo(Editl->Text, 1, 10);
Создадим точно такие же процедуры для обработки
события OnChange (При изменении) для объектов Edit2 и
ComboBox1. В первую из них надо добавить оператор:
ComboBoxl->Text = SetTo(ComboBoxl->Text, 2, 100);
а во вторую — оператор:
Edit2->Text = SetTo(Edit2->Text, -100, 100);
314
Beog чисел
Итак, нам осталось написать функцию SetTo, которая
получает неизвестно какую строку, а выдает нам, пусть
и снова в виде строки, число из диапазона, допустимо¬
го для данного элемента управления.
Первый вопрос, который при этом возникает: где опи¬
сывать эту функцию? Ответ: до того как она использо¬
вана, то есть лучше всего — до всех процедур обработки
событий. Перейдите в это место в окне кода. Начать
описание функции необходимо с заголовка. В данном
случае он может выглядеть так:
AnsiString fastcall SetTo
(AnsiString Txt, int min, int max)
Указание типа возвращаемого значения показывает,
что перед нами функция. После имени функции в скоб¬
ках указываются формальные параметры. Первый из
них — текст из поля. Как и в других случаях, следует
использовать специальный строковый тип AnsiString. Ос¬
тальные два параметра задают минимальное и макси¬
мальное допустимые значения поля. Их можно офор¬
мить как целые числа — тип int.
Затем сразу наберем пару фигурных скобок:
{
}
Подготовка к созданию функции завершена, и теперь
можно писать ее текст. Сначала нам нужно преобразо¬
вать поступившую строку в число. Здесь нас ждет не¬
большая тонкость. Мы совершенно не представляем,
что мог ввести пользователь. Это могло быть и не число
315
вовсе, а какие-нибудь буквы. Чтобы у нас не возникло
проблем, нам надо использовать функцию StrTolntDef.
Если строку невозможно преобразовать в число, эта
функция вернет число, заданное ее вторым парамет¬
ром. Полученное значение мы сохраним в виде пере¬
менной. Поэтому сначала добавим после открывающей
фигурной скобки строку
int i ;
а далее напишем:
i = StrToIntDef(Txt,0);
Теперь нам надо изменить значение переменной i так,
чтобы оно попало в нужный диапазон. Это можно сде¬
лать с помощью таких условных операторов:
if (i<min) i=min;
i f (i >max) i =max;
Теперь переменная i содержит нужное число и нам надо
определить значение функции. Преобразуем число в
строку и укажем ее в операторе return:
return (IntToStr(i));
В принципе, на этом можно было бы и закончить, но
есть еще одна тонкость. Если пользователь бросил кос¬
ти, а затем изменил параметры, то у него в окне оста¬
нется результат прошлого броска. В некоторых случаях
это может сбивать с толку. Поэтому мы в рамках этой
же функции очистим значение поля результата и снова
дадим ему красный фон. Слудующие операторы надо
вставить перед оператором return:
Forml - >Edit3 ->Text = " " ;
Forml->Edit3->Color = clRed;
316
Программа готова
Вот теперь наша программа готова. Вот как выглядит
написанный нами код.
AnsiString _fastcall SetTo
(AnsiString Txt, int min, int шах)
{
int i;
i = StrToIntDef(Txt,0);
if (i<min) i=min;
if (i>max) i=max;
Forml->Edit3 - >Text=" " ;
Forml->Edit3->Color = clRed;
return (IntToStr(i));
}
void fastcall TForml::ButtonlClick(TObject *Sender)
{
int i,s;
s=StrToInt(Edit2->Text);
for (i=l; i<=StrToInt(Editl->Text); i++)
s += l+random(StrToInt(ComboBoxl->Text));
Edit3->Text = IntToStr(s);
Edit3~>Color = clLime;
}
void fastcall TForml::EditlChange(TObject *Sender)
{
Editl->Text = SetTo(Editl->Text, 1, 10);
}
317
void fastcall TForml::ComboBoxlChange
(TObject *Sender)
{
ComboBoxl->Text = SetTo(ComboBoxl->Text, 2, 100);
}
void fastcall TForml::Edit2Change(TObject *Sender)
{
Edit2->Text = SetTo(Edit2->Text, -100, 100);
}
Запустите программу, нажав клавишу F9, и посмотрите,
как она работает. Возможно, у вас возникнут некото¬
рые трудности с вводом отрицательных значений в поле
Бонус, но они легко преодолимы. Поэкспериментируйте
с разным числом костей и граней при разных значениях
бонуса.
Наша программа в работе
Для интереса мы провели бой между рыцарем и трол¬
лем до конца.
318
Параметры рыцаря: двуручный меч 2d6+2, 50 баллов
здоровья.
Параметры тролля: тяжелая дубинка 1 d4+5, 60 баллов
здоровья.
Рыцарь бьет первым, все удары попадают в цель.
Удар
рыцарь
тролль
Здоровье
рыцарь
тролль
9 баллов
51 балл
7 баллов
43 балла
10 баллов
41 балл
8 баллов
35 баллов
8 баллов
33 балла
9 баллов
26 баллов
12 баллов
21 балл
8 баллов
1 8 баллов
4 балла
1 7 баллов
7 баллов
1 1 баллов
9 баллов
8 баллов
6 баллов
5 баллов
8 баллов
0 баллов,
тролль умирает
319
Конечно, если вы сами напишете такую программу,
ваш бой может развиваться иначе. Мы постарались
подобрать такие параметры здоровья бойцов, чтобы
предсказать результат было трудно, хотя рыцарь имеет
небольшое преимущество.
Головоломка «на флажках»
В этой главе мы создадим головоломку,
достаточно простую для программиро¬
вания, но достаточно интересную для
игры. Порадуйте ею друзей и близких —
пусть они видяту что вы научились пи¬
сать программы не мелом на доске, а
можете предъявить результаты своих
опытов
Как устроена головоломка
В окне программы изображаются пять сброшенных
флажков. Задача — установить их все. Это было бы со¬
всем не сложно сделать, если бы не одна хитрость.
Когда мы устанавливаем какой-то флажок, меняется
состояние двух следующих флажков. При этом сбро¬
шенные флажки устанавливаются, а установленные —
сбрасываются.
Щелкать разрешено только на сброшенных флажках.
Щелчок на установленном флажке не действует, так
что решить головоломку отнюдь не просто. Примерный
вид окна готовой программы показан на рисунке.
Вот что должно у нас получиться
Набор
флажков
Окно
_ программы
Мы не придумали эту головоломку. Она достаточно
известна и даже неоднократно встречалась в мире ком¬
пьютерных игр. Например, в игре Tales of Phantasia вместо
флажков были использованы нажимные плиты.
Конструируем окно программы
Запустите систему С++ Builder и создайте новый проект.
Сохраните его в папке Флажки.
Итак, нам не надо ничего, кроме флажков. Выбе¬
рите компонент CheckBox (Флажок) на панели ком¬
понентов и нарисуйте флажок в верхней части формы.
Задайте для свойства Caption (Заголовок) значение «Победа!».
На самом деле этот текст будет выводиться только в конце
игры, когда головоломка решена, а сейчас мы его вводим
лишь для того, чтобы подобрать размеры области флаж¬
ка. Используя маркеры, настройте размеры объекта так,
чтобы надпись целиком помещалась в одной строке. Сво¬
бодного места при этом должно быть как можно меньше.
Теперь надо «размножить» наш флажок с помощью
буфера обмена. Убедитесь, что он все еще выбран. На¬
жмите комбинацию клавиш CTRL + С, а затем — CTRL + V.
В окне появится новый флажок. Поместите его под пер¬
вым. Повторите операции вставки и перемещения еще
три раза, чтобы получить столбец из пяти флажков.
Добавление флажков с помощью буфера обмена
322
Теперь можно задать свойство Caption (Заголовок) для
каждого из флажков. Пусть «названиями» флажков
будут числа: 1, 2, 3, 4, 5. Это позволит записать для себя
решение головоломки. Попробуйте сами выровнять
флажки и подобрать размер формы.
He sce просто
Событие, на которое реагирует флажок, — Click (щелчок
мыши). Так флажок переключают (устанавливают или
сбрасывают) чаще всего, но не всегда. Состоянием
флажка можно еще управлять с клавиатуры и из про¬
граммы, то есть программно. Из-за того что мы хотим,
чтобы щелчок влиял и на другие флажки, нужна особая
аккуратность.
Представьте себе, что мы щелкнули на флажке. При
этом вызывается процедура обработки события. Она пе¬
реключит еще два флажка. При этом процедура обра¬
ботки будет вызвана еще дважды. Это снова приведет
к переключению флажков и так далее. Так процесс
может продолжаться до бесконечности или до полного
исчерпания ресурсов компьютера.
Что же делать? Как разорвать порочный круг? Пока
процедура обработки события не завершила свою ра¬
боту, остальные обращения к ней нужно игнорировать.
Для этого мы создадим глобальную логическую пере¬
менную s. В «нормальной» ситуации она будет иметь
значение false (Нет), а в то время, когда идет обработка
события флажка, — true (Да). Если переменная s уже
имеет значение true (Да), то вызов процедуры обработ¬
ки «лишний» и делать ничего не нужно.
Итак, приступим. Начнем с того, что в окне кода доба¬
вим описание глобальной переменной:
323
bool s';
Мы создали логическую переменную s. Щелкните на
форме вне флажков и с помощью Инспектора объектов со¬
здайте процедуру обработки события OnCreate (При со¬
здании). Добавьте в нее один оператор:
s = false;
Теперь можно приступать к обработке щелчка на флаж¬
ке. Щелкните на первом флажке, а затем с помощью
Инспектора объектов создайте процедуру CheckBox1Click.
Сразу после этого назначьте эту процедуру для обра¬
ботки события OnClick (При щелчке) для всех остальных
флажков. Для этого надо выбрать имя
процедуры в раскрывающемся списке.
Теперь вернемся в окно кода и добавим в процедуру
CheckBox1Click такие операторы:
if (s) return;
s = true;
s = false;
Точками обозначено место, куда мы потом запишем
«настоящие» операторы обработки флажков. Во время
их выполнения значение переменной s равно true (Да).
Если программа снова вызовет процедуру обработки,
будет выполнен оператор return — немедленный выход
из процедуры.
Установка и сброс флажков
Итак, после оператора s = true должны идти команды,
которые переключат флажки. Но нас ждут серьезные
сложности. Во-первых, нам надо узнать, какой флажок
изменил свое состояние. Во-вторых, было бы удобно
324
работать с флажками, как с массивом. Для обеих этих
проблем существует единое решение. Где расположе¬
ны наши флажки? На форме. Форма является для них
контейнером. А у объектов-контейнеров имеется свой¬
ство Controls (Элементы управления) — массив элементов
управления, находящихся в данном контейнере.
Так как никаких других элементов управления у нас
на форме нет, мы можем быть уверены, что этот мас¬
сив содержит пять элементов (с индексами от 0 до 4) —
пять наших флажков.
К сожалению, это означает, что нам придется исполь¬
зовать по всей программе очень неудобную конструк¬
цию вида:
((TCheckBox *)Forml->Controls[i])
Здесь i — некоторая индексная переменная (она может
быть любой), а вся конструкция в целом указывает си¬
стеме С++ Builder, что объект, найденный в массиве
Controls, следует считать флажком.
В главе 20 мы уже сталкивались с тем, что когда одна
процедура-обработчик используется для нескольких
элементов управления, то программа должна выяс¬
нить, на каком же именно элементе управления про¬
изошел щелчок. Для этого мы использовали объект
Sender, который обработчик получает при возникнове¬
нии события.
Здесь мы имеем дело не с одним объектом Sender; а с
массивом объектов — Controls [ ]. Чтобы получить доступ
к их свойствам, надо явно указать тип этих объектов.
Сначала надо выяснить, какой флажок был переключен.
Это удобно сделать в цикле. Используем в качестве
параметра цикла переменную Index. Не забудем перед
запуском программы ее описать. Нам нужны такие опе¬
раторы:
325
for (Index = 0; Index<5; Index++)
if (Sender == Forml->Controls[Index]) break;
Оператор break прерывает выполнение цикла. Поэтому,
когда выполнение цикла завершается, значение пере¬
менной lndex соответствует переключенному флажку.
Теперь надо проверить, был ли флажок установлен или
сброшен. Процедура-обработчик вызывается уже после
того, как состояние флажка изменилось. Если значе¬
ние свойства Checked (Установлен) paBHofalse (Нет), флажок
сброшен, а если true (Да) — установлен. Номер флажка в
массиве определяется переменной lndex. Запишем услов¬
ный оператор:
if (!((TCheckBox *)
Forml->Controls[Index])~>Checked)
Знак операции отрицания I делает истинное выраже¬
ние ложным, а ложное — истинным.
Условие выполнено, если флажок сейчас сброшен, то
есть до щелчка он был установлен. В этом случае нам
надо отменить сброс флажка, то есть написать:
((TCheckBox *)Forml->Controls[Index])->Checked =
true;
Далее напишем оператор:
else {
после которого и надо запрограммировать изменение
состояния «дополнительных» флажков. Текущийфла-
жок уже переключен. Для него больше ничего делать
не надо.
Нам надо переключить два следующих флажка. Как
узнать их номера? Начнем с того, что прибавим едини¬
цу к номеру текущего флажка:
326
num = Index + 1;
Мы ввели новую переменную num, которую также надо
будет описать. Она теперь, скорее всего, имеет нужное
значение. Но для последнего флажка (с номером 4,
наши флажки пронумерованы от 0 до 4), мы выйдем за
пределы массива, так что нам нужно в этом случае из¬
менить состояние нулевого флажка:
if (Index == 4) num = 0;
Теперь состояние флажка можно поменять на проти¬
воположное:
((TCheckBox *)Forml->Controls[num])->Checked =
l((TCheckBox *)Forml->Controls[num])->Checked;
Точно так же можно поменять значение еще одного
флажка. Разница будет только в том, как выполняет¬
ся проверка на выход за пределы массива:
num++;
if (Index == 3) num = 0;
((TCheckBox *)Forml->Controls[num])->Checked =
!((TCheckBox *)Forml~>Controls[num])->Checked;
На этом изменение состояния флажков закончено.
Завершим составной оператор закрывающей фигурной
скобкой:
}
Проверка решения
Осталось выяснить, решена головоломка или еще нет.
Головоломка решена, если установлены все пять флаж-
327
ков. Так как выяснить, установлен ли флажок, позво¬
ляет логическое значение, нам придется использовать
логические операции. Нужные операторы могут выгля¬
деть так:
e = true;
for (num = 0; num<5; num++)
© = 6 & &
((TCheckBox *)Forml->Controls[num])->Checked;
Здесь мы ввели логическую переменную e. Логическая
операция && дает результат true (Да), только если оба
операнда равны true (Да). Поэтому после цикла значение
переменной e останется равным true (Да), только если
все флажки установлены.
Теперь мы должны решить, как быть, если голово¬
ломка решена:
if (e)
Давайте в этом случае зададим для всех флажков под¬
пись «Победа!», a заодно и отключим их, чтобы никто
не щелкал по ним зря. Это можно сделать так:
for (num = 0; num<5; num++) {
((TCheckBox *)Forml->Controls[num])~>Caption =
"Победа!";
((TCheckBox *)Forml~>Controls[num])->Enabled =
false;
>
Свойство Enabled (Включен) определяет, активен флажок
или нет.
Проверка работы программы
Наша программа почти готова! Осталось добавить в на¬
чало процедуры описания использованных переменных:
328
int Index, num;
bool e;
Теперь можно посмотреть на получившийся код.
void fastcall TForml::FormCreate
(TObject *Sender)
{
s = false;
>
void fastcall TForml::CheckBoxlClick
(TObject *Sender)
{
int Index, num;
bool e;
if (s) return;
s = true;
for (Index = 0; Index<5; Index++)
if (Sender == Forml->Controls[Index]) break;
if (1((TCheckBox *)
Forml->Controls[Index])->Checked)
((TCheckBox *)Forml->Controls[Index])->Checked
= true;
else {
num = Index + 1;
if (Index == 4) num = 0;
((TCheckBox *)Forml->Controls[num])->Checked
= i ((TCheckBox *)
Forml~>Controls[num])->Checked;
num++;
if (Index == 3) num = 0;
329
((TCheckBox *)Forml->Controls[num])->Checked
= !((TCheckBox *)
Forml->Controls[num])- >Checked;
>
e = true;
for' (num = 0; num<5; num++)
e = e &&
((TCheckBox *)Forml->Controls[num])->Checked;
if (e)
for (num = 0; num<5; num++) {
((TCheckBox *)Forml->Controls[num])->Caption
= "Победа!";
((TCheckBox *)Forml->Controls[num])->Enabled
= false;
}
s = false;
}
Нажмите клавишу F9, чтобы запустить программу.
Попытайтесь самостоятельно решить получившуюся
головоломку. Убедитесь, что сбросить установленный
флажок нельзя, а установка любого флажка действи¬
тельно вызывает изменение состояния еще двух.
Головоломка в ходе решения
Решение головоломки может вызвать некоторые труд¬
ности. Подскажем, что в кратчайшем решении надо
сделать пять щелчков, причем на пяти разных флажках.
fL8ce пять»
330
Глава 26
Компьютер рисует узоры
До сих пор мы полностью определяли вид
окна нашей программы, размещая ком¬
поненты на форме. Но средствами сис¬
темы С++ ВшШегможно и просто нари¬
совать в окне программы все, что мы
захотим. Это могут быть прямые
линии, круги, прямоугольники и многое
другое
Описание программы
Рисовать в окне программы имеет смысл тогда, когда
использовать заранее заготовленный рисунок нельзя.
Давайте напишем программу, в которой изображение
на экране «живет собственной жизнью», меняется по
тем законам, которые ему предписаны
Представьте себе область, разбитую на
клеточки. Каждая клеточка покрашена
в свой цвет. Например, на рисунке пока¬
зан квадрат размером 3x3 с клетками
трех цветов: красного, желтого и зеле¬
ного.
Чтобы было проще, можно обозначить
каждый цвет числом. Например, крас¬
ный цвет обозначим числом 1, желтый —
2, зеленый — 3. В этом случае тот же
самый рисунок можно представить в
виде схемы.
А теперь пусть рисунок (точнее, узор) «оживет». Зада¬
дим правила, по которым он может «развиваться». Ра¬
зумеется, управлять развитием, изменением и обнов¬
лением рисунка будет программа.
331
Эти правила довольно просты. Цвета упорядочены по
номерам. За красным цветом идет желтый, за желтым —
зеленый. Будем считать, что за самым последним цве¬
том опять идет первый (то есть за зеленым — красный).
Кроме того, программа будет считать, что верхний и
нижний, а также правый и левый края нашей области
соединены друг с другом. Не всем легко представить
это себе наглядно, но в таком случае у каждой из кле¬
ток ровно четыре «соседа» — клетки с общей границей.
Проверим для каждой клеточки цвета соседей. Если
среди соседей есть клетки со «следующим» цветом, то
цвет нашей клетки становится таким же. Если таких
соседей нет, цвет клетки остается без изменений.
Проверка и обновление всех клеток образуют шаг из¬
менения узора. Таких шагов может быть сколько угод¬
но. Вот как может меняться картинка по этим прави¬
лам.
В программе и цветов, и клеток может быть не три, а
намного больше. Попытайтесь самостоятельно предста¬
вить себе, что может из этого получиться.
Ясно, что одноцветная область, граничащая с клеткой
«следующего» цвета, в конце концов полностью изме¬
нит цвет. Может показаться, что, каким бы ни был на¬
чальный узор, в конце концов получится одноцветная
область. Но это слишком поверхностный взгляд, и он
ошибочен. Что получится на самом деле, будет видно,
когда программа заработает.
332
Содержание окна
Эта программа немного необычная. Она работает без
участия пользователя, который играет роль зрителя.
Мы будем только смотреть, что получается на экране.
Программа нарисует изображение сама. Для этого ну¬
жен компонент PaintBox (Область рисования). Он содержит
набор свойств и методов, которые позволяют рисовать
все что угодно. Но в этой программе требуется рисовать
только клеточки — простые одноцветные квадратики.
Нужно, чтобы картинка обновлялась «сама собой».
Поэтому для перерисовки следует использовать тай¬
мер. Интервал таймера может быть любым. Он зависит
от компьютера, на котором работает программа. Совре¬
менные компьютеры способны обновить наше изобра¬
жение несколько раз в секунду.
Таймер на экране не изображается, поэтому в работе
программа будет выглядеть примерно так, как пока¬
зано на рисунке.
Здесь еще непонятно, какой узор у нас получится
333
Добавляем объекты
Итак, начнем создавать программу. Запустите систему
С++ Builder и создайте новый проект. Сохраните его в пап¬
ке Узор.
На форму надо поместить
два объекта. Первый из
них — область рисования PaintBox. Нужный компонент
располагается на вкладке System (Системные) палитры
компонентов.
Выберите эту вкладку и щелкните на кнопке
PaintBox (Область рисования). Методом протягивания
нарисуйте область рисования на форме. С помощью Ин¬
спектора объектов задайте для области рисования разме¬
ры 320x320. Для этого надо изменить CBoftcTBaWidth (Ши¬
рина) и Height (Высота). Затем методом перетаскивания
расположите область рисования по центру формы. Под¬
берите размеры формы. Щелкните на форме вне области
рисования и измените свойство Caption (Заголовок). Вве¬
дите в него значение Узор.
Еще на форму надо добавить таймер. Вкладка System
(Системные) уже открыта. Щелкните на кнопке
Timer (Таймер), а затем на форме вне области рисо¬
вания. На ней появится значок таймера. С помощью
Инспектора объектов задайте значение свойства Interval (Ин¬
тервал). Значение лучше выбрать поменьше — напри¬
мер, можно задать число 100 (то есть одну десятую се¬
кунды).
Приступаем к программированию
Программа, которую мы собираемся написать, будет
довольно сложной. Многое необходимо продумать зара¬
нее. Хочется не только увидеть, какой узор получится,
но и посмотреть, как он изменится при ином размере
области или другом количестве цветов. Чтобы изме-
334
Значок таймера на поле готовой формы
нить программу было легко, надо писать ее особым об¬
разом.
В сложной программе надо сначала определить, какие
данные она должна использовать. У нас два основных
набора данных. Во-первых, это совокупность цветов.
Разумно представить их в виде одномерного массива.
В этом случае номер цвета — это его индекс в массиве,
в котором представлены все допустимые цвета. Кроме
того, надо предусмотреть в программе способ «замы¬
кания» массива — чтобы за последним цветом следо¬
вал первый.
Число цветов выбирается программными средствами.
Можно использовать только часть всего массива цве¬
тов — те элементы, которые нужны. Объем рабочей час¬
ти массива определит специальная константа.
Второй элемент данных — это набор цветов отдельных
клеточек. Их можно кодировать числами — номерами
в массиве цвета. Понадобится большой двумерный мас¬
сив, размер которого также можно описать константой.
335
Замена любой из этих констант сразу же меняет пара¬
метры узора.
Описываем константы и переменные
Теперь опишем предусмотренные нами константы и
переменные. Перейдите в окно кода и пролистайте его
немного вверх, пока не найдете место, где описаны гло¬
бальные переменные. Описания констант надо вста¬
вить непосредственно перед ним.
Описания констант вставляем сюда
Описания констант записываются после ключевого
слова const. Надо добавить такие операторы:
const size=40;
const csize=12;
Здесь ключевое слово const указывает, что далее идет
описание константы. Константа size задает число кле¬
точек по каждому направлению, константа csize — чис¬
ло используемых цветов. Нужные нам цвета определим
при помощи массива Colors:
336
TColor Colors[16] =
{ clRed, clGreen, clYellow, clBlue,
clWhite, clGray, clFuchsia, clTeal,
clNavy, clMaroon, clLime, clOlive,
clPurple, clSilver, clAqua, clBlack };
Их может быть больше, чем мы будем использовать.
Набор элементов массива задается в фигурных скоб¬
ках. Здесь использованы стандартные константы, кото¬
рыми система С++ Builder описывает обычные цвета
Windows.
Теперь нам надо описать двумерный массив клеточек.
В языке С++ двумерный массив — это как бы «массив
массивов». Следующий оператор надо вставить сюда
же:
int Points[size][size];
Обратите внимание на то, что длина массива задана при
помощи константы. Система С++ Builder допускает та¬
кую запись, так как нужная константа уже определе¬
на выше.
Способ хранения данных задан. Можно приступать к
описанию логики работы программы.
Инициализация донных
Вы не забыли, что в начальный момент нужно задать
цвета квадратиков случайным образом? Кроме этого,
надо обеспечить генерацию неповторяющихся случай¬
ных чисел.
Щелкните на форме вне объектов, в Инспекторе объектов
выберите вкладку Events (События) и дважды щелкните
напротив события OnCreate (При создании). Система С++
Builder создаст заготовку процедуры FormCreate:
12—Ю19
337
void fastcall TForml::FormCreate
(TObject *Sender)
{
}
Для заполнения массивов понадобятся циклы. Язык
С++ требует обязательного описания переменных, по¬
этому надо описать переменные цикла. Добавьте после
открывающей фигурной скобки такой оператор:
int i, j ;
Операторы тела процедуры добавляются после этой
строки. Сначала надо обеспечить «неповторимость»
случайных чисел:
randomize();
Чтобы система нашла эту процедуру, надо добавить в
начало программы строку:
#include <stdlib.h>
Теперь можно приступать к инициализации массива
квадратиков. Для этого удобно использовать два вло¬
женных цикла:
for (i=0; i<size; i++)
for (j=0; j<size; j++)
Points[i][j] = random(csize);
Минимально возможное значение элемента массива —
0, максимально возможное — на единицу меньше кон¬
станты csize. Рисовать начальный узор не нужно — уже
через одну десятую секунды в первый раз сработает
таймер, который и сделает все, что требуется.
Обработка узора
Процедура пересчета (и рисования) узора должна вы¬
зываться таймером. Выберите объект Timer1 на форме,
перейдите в Инспекторе объектов на вкладку Events (Собы¬
тия) и дважды щелкните напротив события OnTimer (При
338
срабатывании таймера). Система С++ Builder создаст заго¬
товку процедуры Timer1Timer.
void fastcall TForml::TimerlTimer
(TObject *Sender)
{
5
}
Сначала опишем нужные переменные. Для этого раз¬
местим необходимые описания после открывающей
квадратной скобки. Во-первых, понадобится цикл по
элементам массива Points. Массив двумерный — нужны
две переменные цикла: i и j.
Дополнительные переменные удобно использовать для
определения «соседства». Нужны пять переменных:
одна позволит вычислить следующий цвет, а осталь¬
ные — определить соседей сверху, снизу, справа и сле¬
ва. Обозначим эти пять переменных буквами с (от
color), и (up), d (down), I (left) и r (right).
Наконец, цвета всех клеточек должны определяться со¬
стоянием узора на прошлом шаге. Поэтому изменять
цвета «на месте» нельзя. Запишем новые значения цве¬
тов в отдельный массив. Потом мы целиком скопиру¬
ем его обратно. Итак, нужна еще копия двумерного
массива. Соответствующие описания можно записать
так:
int i,j;
int c,d,u,l,r;
int newPoints[size][size];
Теперь надо вычислить, какого цвета будет каждая
клеточка на следующем шаге. Для этого понадобится
цикл, охватывающий все квадратики:
339
for (i=0; i<size; i++)
for (j=0; j<size; j++) {
Сначала надо вычислить «следующий цвет» и запом¬
нить его в переменной с:
c=Points[i]tj]+l;
Важно, что после последнего цвета идет первый:
if (c==csize) c=0;
Аналогичным образом можно вычислить индексы для
квадратиков, примыкающих к данному сверху, снизу,
слева и справа. Напомним, что края нашего узора как
бы «склеены» друг с другом. Понадобятся такие опе¬
раторы:
d=i-l;
if (d<0) d=size-l;
u=i+l;
if (u==size) u=0;
i=j-i;
if (l<0) ls=size-l;
r=j+l;
if (r==size) r=0;
Если среди соседей «следующий» цвет отсутствует, то
клеточка остается без изменений:
newPoints[i] [j]=Points[i 3 [j 3;
Если же хотя бы один из «соседей» имеет такой цвет,
клеточка перекрашивается:
340
if (Points[d][j]==c || Points[u][j]==c 11
Points[i][l]==c || Points[i][r]==c)
newPoints[i][j]=c;
В условном операторе указано четыре условия (для
каждого из соседей), соединенных знаками операции
| |. В таком случае все условие истинно, если истинно
хотя бы одно из отдельных условий.
На этом цикл обновления цветов завершен, и его можно
закрыть:
>
Как действовать дальше? Для рисования отведена об¬
ласть фиксированного размера. Но число клеточек мо¬
жет быть разным. Выберем размер клеточки так, чтобы
узор занимал, по возможности, всю область рисования.
Для этого надо разделить размер области (320) на число
клеток. Результат надо присвоить какой-нибудь пере¬
менной, например переменной с, которая сейчас «сво¬
бодна»:
с = 320 / size;
Напомним, что при целочисленном делении дробная
часть частного или остаток отбрасываются.
Теперь можно обновить узор на экране. По сути, надо
просто нарисовать его. Нужен еще один цикл по всем
квадратикам:
for (i=0; i<size; i++)
for (j=0; j<size; j++) {
Сначала копируем новые значения цветов клеточек в
основной массив:
341
Points[i][j]=newPoints[i][j];
Теперь можно приступать к рисованию. Объект PaintBox1
имеет свойство Canvas (Холст), свойства и методы кото¬
рого и обеспечивают рисование. Воспользуемся для
рисования методом Rectangle, который рисует прямо¬
угольник с заданными вершинами. Цвет контура прямо¬
угольника задается свойством Pen->Color, а цвет закрас¬
ки — свойством Brush->Color. Поэтому сначала нужно
настроить эти цвета:
PaintBoxl->Canvas->Pen->Color =
Colors[Points[i][j]];
PaintBoxl->Canvas->Brush->Color =
Colors[Points[i][j]];
Теперь можно выполнить рисование. Параметры
метода Rectangle задают левую, верхнюю, правую и ниж¬
нюю границы прямоугольника. Оператор может выг¬
лядеть так:
PaintBoxl->Canvas->Rectangle
(с*(i-l),c*(j-l),c*i-l,c*j-l);
Эти параметры выбраны так, что между клетками оста¬
ется небольшой зазор. Если вы хотите, чтобы они рас¬
полагались вплотную друг к другу, сливаясь, напиши¬
те так:
PaintBoxl->Canvas->Rectangle
(c*{i-l),c*{j-l),c*i,c*j);
Все сделано. Завершите цикл закрывающей фигурной
скобкой:
}
342
Программа готова
Программа готова, и вот как выглядят составляющие
ее процедуры:
void _fastcall TForml::FormCreate
(TObject *Sender)
int i, j;
randomize();
for (i=0; i<size; i++)
for (j=0; j<size; j++)
Points[i][j] = random(csize);
void fastcall TForml::TimerlTimer
(TObject *Sender)
int i,j;
int c,d,u,l,r;
int newPoints[size][size];
for (i=0; i<size; i++)
for (j = 0; j<size; j++) {
c=Points[i][j]+l;
if (c==csize) c=0;
d=i-l;
if (d<0) d=size-l;
u=i+l;
if (u==size) u=0;
i=j-i;
if (l<0) l=size~l;
r=j+l;
if (r==size) r=0;
343
newPoints[i][j]=Points[iJ[j];
if (Points[d][j]==c || Points[u][j]==c ||
Points[i][l]==c || Points[i][r]==c)
newPoints[i][j]=c;
>
с = 320 / size;
for (i=0; i<size; i + +)
for (j=0; j<size; j++) {
Points[i][j]=newPoints[i][j];
PaintBoxl->Canvas->Pen->Color =
Colors[Points[i][j]];
PaintBoxl->Canvas->Brush- >Color =
Colors[Points[i][j]];
PaintBoxl->Canvas->Rectangle
(c*(i-1)/c*(j-1),c*i-l,c*j-l) ;
}
}
Запустите программу клавишей F9. В первый момент
не будет происходить ничего интересного. Значитель¬
ная часть узора будет неподвижной, и только кое-где
возникнут постепенно расширяющиеся области, в ко¬
торых происходит перекрашивание узора. И только
когда в дело будет вовлечена почти вся область, карти¬
на начнет проясняться.
Наиболее интересные результаты получаются, когда
используется примерно 10-12 цветов. В этом случае в
области образуется несколько «раскручивающихся»
спиралей, причудливо взаимодействующих друг с дру¬
гом. Количество спиралей зависит от размера области,
так как их возникновение определяется случайными
факторами. Чем больше клеточек, тем больше спиралей.
344
При разных параметрах получившиеся узоры могут очень
сильно отличаться друг от друга
Заранее предсказать эволюцию узора практически не¬
возможно. Если число цветов уменьшить, то число спи¬
ралей настолько возрастет, что человеческий глаз будет
не в силах уловить закономерность в мешанине цветов.
Если число цветов увеличить, то количество спиралей
уменьшится и нередко узор будет «вырождаться» в
одноцветный рисунок. Очень редко (многие из читате¬
лей этой книги ни разу не встретятся с такой ситуаци¬
ей) узор может превратиться в «ползущую полоску»,
движущуюся справа налево или сверху вниз.
Редкий случай ползущей полосы
345
Эксперименты для любознательных
Изменяя константы size и csize, можно заставить про¬
грамму выглядеть и работать по-другому. Попробуйте
поэкспериментировать с разными значениями и посмо¬
трите, как меняется узор. Попробуйте изменить про¬
грамму так, чтобы у клетки было не четыре, а восемь
соседей. Посмотрите, как выглядят спирали и узор в
этом случае. Поищите и другие варианты выбора сосе¬
дей клетки и посмотрите, как при этом меняется вид
узора.
Изменив способ поиска соседей клетки, можно заставить узор
выглядеть совершенно иначе
346
В заключение хочется обратить внимание на еще один
интересный факт. Вначале на нашем рисунке царила
полная случайность. Однако в соответствии с «законами
природы» (а для рисунка наши правила обновления —
это законы природы) в этом хаосе появились законо¬
мерности. Итоговый узор, содержащий множество пря¬
мых линий каждого цвета, никто не назовет случай¬
ным.
Приложение
Указатель компонентов, свойств, событий,
методов, стандартных функций и процедур
BitBtn (кнопка с изображением)
Предназначен для создания командных кнопок, на которых по¬
мимо подписи имеется рисунок.
Свойства
компонента
Caption
Содержит подпись на кнопке
248*
Glyph
Содержит изображение для кнопки
246
Kind
Позволяет использовать для типовых действий
кнопки co стандартным изображением
246
События
OnClick
Происходит в случае щелчка на кнопке
249
Button (Кнопка)
Предназначен для создания командных кнопок, с помощью кото¬
рых пользователь способен выполнять различные действия.
Свойства
компонента
Caption
Содержит подпись на кнопке
90, 112,
147, 182
Default
Позволяет нажимать кнопку при помощи
клавиши ENTER
113, 186
События
OnClick Происходит в случае щелчка на кнопке
91, 152
CheckBox (Флажок)
Предназначен для создания флажка.
Свойства компонента
Caption Содержит подпись возле флажка
208, 322
Checked Определяет, установлен ли флажок
212, 326
348
Alignment
Определяет относительное расположение
208
флажка и подписи
События
OnClick
Происходит при переключении флажка
211, 323
ChecklistBox (Список флажкое)
Предназначен для создания списка, каждый из пунктов которого
снабжен флажком.
Свойства компонента
Checked
Массив логических значений,показывающих,
какие из флажков, соответствующих отдельным
пунктам списка, установлены
299
ltems
Содержит набор пунктов списка
296
ltems->Count
Количество пунктов в списке
299
События
OnClickCheck Происходит при переключении одного
из флажков списка любым способом
298
ComboBox (Поле со списком)
Предназначен для создания разных видов списков, обычно рас¬
крывающихся .
Свойства компонента
ltemlndex
Содержит номер выбранного пункта списка
149, 289
ltems
Содержит набор пунктов списка
145, 309
ltems->Count Содержит число пунктов списка
]52
Style
Определяет конкретный видхписка,
представленного объектом
144, 282,
309
Text
Содержит текст в поле списка. Это свойство
доступно только в том случае, если в данный
элемент управления можно вводить значения,
не представленные в списке
зТо
349
События
OnChange Происходит, когда выбранное значение
изменяется. Способ изменения не важен
289, 314
Методы
Clear Очищает список, уничтожая все элементы
287
ltems->Add Добавляет элемент в список
288
6dit (Текстовое поле)
Предназначен для создания текстовых полей. Текстовое поле исполь¬
зуют для ввода в программу текстовых или числовых значений.
Свойства компонента
Color Определяет цвет фона поля
311
Font Составное свойство, управляющее начертанием
текста в поле
зТТ
ReadOnly Разрешает или запрещает изменение текста в поле
зТТ
Text Хранит содержимое текстового поля
111, 308
События
OnChange Происходит, когда текст в поле изменяется
314
GroupBox(Рамка)
Предназначен для создания рамки, окружающей группу элемен¬
тов управления.
Свойства компонента
Caption Содержит заголовок, отображаемый
в верхнем левом углу рамки
206, 279
lmoge (Рисунок)
Позволяет отобразить на форме растровое изображение.
Свойства компонента
Picture Содержит рисунок, отображаемый в данном объекте
У2Л
Stretch Указывает, что рисунок надо отмасштабировать
по размерам объекта
121, 135
350
События
OnClick Происходит в случае щелчка на изображении 125
Методы
Picture->LoadFromFile Загружаетизображениеизфайла 127,138
Label (Надпись)
Предназначен для создания в пределах формы надписей. Пользо¬
ватель программы может читать их, но не изменять текст само¬
стоятельно.
Свойства
компонента
Alignment
Определяет способ выравнивания текста
в пределах объекта
208, 297
AutoSize
Устанавливает размер объекта по объему текста
88, 207
Caption
Содержит текст надписи
86, 109
Font
Составное свойство, управляющее
начертанием текста надписи
88
ListBox (Список)
Предназначен для создания списков, содержимое которых посто¬
янно отображается на экране.
Свойства
компонента
ltems
Содержит набор пунктов списка
284
Методы
Clear
Очищает список, уничтожая все элементы
299
ltems->Add
Добавляет элемент в список
299
MainMenu (Строка меню)
Представляет строку меню, отображаемую под строкой заголов¬
ка программы.
События
OnClick Происходит в случае щелчка на пункте меню. Относится 1 36
не к меню в целом,а именно к указанному пункту
351
OpenDialog (Окно открытия)
Позволяет открыть диалоговое окно для выбора файла.
Свойства компонента
DefaultExt
Определяет расширение имени файла,
если пользователь не указал его
125
FileName
Содержит имя выбранного файла
Ш
Filter
Определяет допустимые типы выбираемых файлов
124
Options
Содержит дополнительные параметры
внешнего вида и работы диалогового окна
V29
Ш
Определяет содержимое строки заголовка окна
VU
Методы
Execute
Показывает диалоговое окно на экране
126
PaintBox (Область рисования)
Предназначен для создания области, в которой можно программ¬
но рисовать произвольные изображения.
Свойства компонента
Canvas
Предоставляет набор свойств и методов
для выполнения рисунков
342
Canvas->Pen->Color
Цвет контура рисуемых фигур
342
Canvas->Brush->Color
Цвет заливки рисуемых фигур
342
Методы
Canvas.Rectangle
Рисует прямоугольник
342
Panel (Панель)
Создает панель — контейнер для размещения других элементов
управления. Панель может быть представлена на экране рамкой.
Свойства
компонента
Bevellnner
BevelOuter
Задают вид рамки панели
119, 246
BevelWidth
Задает толщину рамки панели
П9
Caption
Содержит подпись, отображаемую на панели
119,217
Color
Определяет цвет панели
256
352
PopupMenu (Контекстное меню)
Представляет контекстное меню, которое может открываться при
работе программы.
События
OnClick
Происходит в случае щелчка на пункте меню. Относится
не к меню в целом,а только к указанному пункту
139
Методы
Popup
Показывает контекстное меню на экране
139
RodioButton (Переключатель)
Предназначен для создания отдельного переключателя. Переклю¬
чатели, расположенные в одном контейнере, образуют группу.
Свойства
компонента
Caption
Содержит подпись, отображаемую возле переключателя
281
Checked
Указывает, включен ли данный переключатель
282
События
OnClick
Происходит при включении данного переключателя
любым способом
285
Scrollbar (Полоса прокрутки)
Предназначен для создания на форме полос прокрутки, которые
могут использоваться как для прокрутки содержимого рабочей
области, так и для других целей.
Свойства компонента
Kind
Задает вид полосы прокрутки (горизонтальная
или вертикальная)
218
Мах
Максимальное значение для полосы прокрутки
iT9
Min
Минимальное значение для полосы прокрутки
2?8
Position
Текущее положение ползунка
2T9
SmallChange
Определяет, насколько изменяется положение
ползунка при «построчном» листании
2T9
LargeChange
Определяет, насколько изменяется положение
ползунка при «постраничном» листании
2T9
353
События
OnChange Происходит, когда положение ползунка изменяется
222
Shape (Фигура)
Позволяет изображать на форме простые геометрические фигуры.
Свойства компонента
Brush Определяет способ закраски фигуры
220, 229
Brush->Color Определяет цвет внутренней части фигуры
220, 230
Brush->Style Определяет, как производится закраска
внутренней части фигуры
230
Pen Определяет способ рисования контура фигуры
229
Pen->Color Определяет цвет контура фигуры
229
Shape Определяет форму фигуры
220, 229
События
OnMouseMove Происходит, когда указатель мыши
наведен на данный объект
223
Timer (Таймер)
Позволяет выполнять действия программы в режиме реального
времени, ориентируясь по системным часам компьютера.
Свойства компонента
Interval Содержит интервал срабатывания таймера
186, 334
События
OnTimer Происходит, когда истекает интервал
времени, заданный таймером
187, 338
TrackBar (Движок)
Предназначен для создания на форме движков, элементов управ¬
ления, позволяющих задавать числовые значения при помощи
шкалы и перемещающегося по ней бегунка.
Свойства компонента
Frequency Указывает, как плотно располагаются засечки
на полосе движка
206
354
LineSize
Определяет, насколько изменяется значение движка
при использовании курсорных клавиш
205
Мах
Максимальное значение движка
205
Min
Минимальное значение движка
205
PageSize
Определяет, насколько изменяется значение
205
движка при использовании клавиш PAGE UP и PAGE DOWN
Position
Текущее положение бегунка
205
События
OnChange
Происходит, когда положение бегунка изменяется
209
UpDouun (Счетчик)
Предназначен для создания счетчика, обычно присоединяемого
к текстовому полю.
Свойства
компонента
Мах
Максимальное значение счетчика
308
Min
Минимальное значение счетчика
308
Increment
Изменение значения при щелчке на кнопках счетчика
308
Associate
Содержит объект, к которому счетчик присоединен.
Счетчик и этот объект работают совместно
308
Характеристики формы
Свойства
Borderlcons
Управляет отображением служебных
кнопок в строке заголовка окна
240
BorderStyle
Тип границы окна программы. Определяет, можно
ли менять размеры окна
184, 241,
298
Caption
Содержит текст строки заголовка окна
109, 118
ClientWidth
Ширина рабочей области в окне программы
Ш
ClientHeight
Высота рабочей области в окне программы
184
Color
Задает цвет внутренней области окна
24]
Controls
Массив элементов управления, находящихся
непосредственно на форме
325
355
События
OnCreate
Происходит, когда форма уже готова,но еще не
выведена на экран. Это удачный момент для
настройки начальных характеристик программы
149, 222,
290, 324,
337
OnMouseDown Происходит при нажатии любой кнопки
мыши, если указатель наведен на форму
K39
OnMouseMove Происходит, когда указатель мыши наведен
на форму
232
Методы
Close
Закрывает форму и завершает работу программы
92, 137
ClientToScreen Переводит локальные координаты на форме в
координаты на экране
Г39
Специальные и универсальные свойства компонентов
Enabled
Указывает, включен ли данный объект. Отключенный
объект не реагирует ни на какие события
190, 231,
301, 328
Height
Задает вертикальный размер объекта
в пикселах
120, 183,
217,229
Left
Горизонтальная координата объекта
в контейнере
120, 184,
220, 245
Name
Позволяет менять имя, под которым объект
представлен в программе
95
TabStop
Указывает, что элемент управления можно
активизировать, нажав несколько раз клавишу TAB
Ш
Tag
Дополнительное свойство, которое программист
может использовать по своему усмотрению
249, 286
Top
Вертикальная координата объекта в контейнере
120, 184
Visible
Указывает, изображается ли объект на форме
183, 284
Width
Задает горизонтальный размер объекта в пикселах
120, 183
Стандартные процедуры и функции
abs
Возвращает абсолютную величину (модуль) аргумента 256
Delete
Удаляет символ из строки
269
Insert
Вставляет символ в строку
269
356
lntToStr
Преобразует число в текстовый вид
180, 209
Pos
Определяет позицию заданного символа в строке
268
random
Возвращает число, случайно выбранное
в заданном диапазоне
150, 189,
338
randomize
При наличии вызова этой процедуры
последовательность сгенерированных случайных
чисел не повторяется в разных запусках программы
155, 190,
338
StrTolnt
Преобразует текстовую запись числа в целое число
180,300
StrTolntDef
Преобразуеттекстовую запись числа в целое число.
Если текст не является записью числа, возвращает
заданное значение
3?6
357
Ответы на вопросы
Страница
Ответ
10
А — Подрыв на мине
В — Гибель под огнем противника
С — Промах
D — Попадание в цель
16
В
_
30
62
Кузнечик
164
Разрешенные идентификаторы: YesNo, Y00000Y,
L0123456789012345678901234567890, helllo,
Неразрешенные идентификаторы:
О! — неразрешенный символ «!»
3points — начинается с цифры «3»
Agent 007 — содержит пробел
МпггпЫ — содержит смесь похожих по начертанию
русских и английских букв
Ничего нельзя сказать об идентификаторе СОВЕТ
(если он набран английскими буквами, то разрешен)
166
Labe11->Parent->Le ft
213
1. Свойству Max для движков надо задать значение 199
2. При расчете свойства Label3->Caption надо
вместо знака умножения («*») поставить знак
сложения («+»). Сделать это следует в двух процедурах
3. В свойстве Caption объекта Frame следует
записать «Сумма» вместо «Произведение»
266
A4; БЗ; B2; Г1 ~
275
Массив b содержит те же элементы, что массив a , но
в обратном порядке.
Переменная i имеет значение 10. При этом значении
условие i<10 становится ложным.
358
Содержание
От авторов 3
Глава 1. Программирование без компьютера 7
Программировать может каждый 7
Изобретаем команды 8
Изобретаем язык программирования 11
Инструкции, операторы и параметры 11
Что такое компилятор? 13
Делаем язык международным 15
Глава 2. Процедуры и функции 17
Как выбрать язык программирования 17
От языка программирования к системе
программирования 20
Что такое подпрограмма 21
Процедуры и функции 22
Объявление процедур и функций 23
Формальные и фактические параметры 25
Библиотеки подпрограмм 26
Как пишут программы 27
Заключительное упражнение 29
Глава 3. Как люди учились программировать
компьютер 31
Программирование в машинных кодах 31
Программирование на Ассемблере 34
Алгоритмическое программирование 36
Процедурное программирование 39
Объектно-ориентированное программирование 40
Визуальное программирование 42
359
Глава 4. Компьютер без программирования 45
О пользе стандартизации 45
Командные кнопки 48
Как пользоваться мышью 48
Меню 49
Диалоговые окна 50
Контекстное меню 54
Другие элементы управления 55
А теперь то же самое, но по-английски 58
Глава 5. Объекты и их свойства 59
Что такое объекты 59
Свойства объектов 61
Объектно-ориентированный подход 62
Свойства и методы объектов 64
Программирование без труда 67
События и их обработка 69
Событийный механизм управления программой 70
Какие бывают события 71
Повторим главное 7 2
Глава 6. С++ Builder: первое знакомство 73
Запуск системы С++ Builder 73
Что мы видим на экране 74
Глава 7. Простейшая программа 81
Начнем с конца 81
Как хранится разрабатываемая программа 81
Познакомьтесь с формой 81
Познакомьтесь с компонентами 82
Создаем объект Надпись 84
Настроим свойства объекта 85
Создаем командную кнопку 90
Реакция на кнопку 91
Запуск программы 93
Об именах объектов и процедур 94
360
Глава 8. Сохранение — мать учения! 97
Как хранится информация в компьютере 97
Файлы 98
Папки 99
Диалоговое общение с компьютером 99
Создание папки 100
Переименование папки 101
Сохраняемсвойпроект 102
Сохраняем файлы 104
Полезные советы 105
Глава 9. Учим программу читать 107
Шаг назад 107
Описание новой программы 107
Форма и компоненты 108
Настраиваем форму 108
Создаем надпись 109
Добавляем текстовоеполе 110
Настраиваем текстовое поле 110
Добавляем кнопку 112
Нажатиекнопкисклавиатуры 113
Текстовое поле уже работает 113
Программируем работу кнопки 115
Глава 10. Создаем электронный альбом 117
Начнем с конца 117
Начало работы 118
Размещение исходного рисунка 121
Компонент-невидимка 122
Настраиваем стандартное диалоговое окно 124
Начинаем программирование 125
Загрузка изображения 126
Проверка программы 127
Жесткое тестирование 129
Глава 11. Создаем строку меню 131
Описание программы 131
Где взять меню 132
361
Создаем меню в редакторе 133
Создаем контекстное меню 134
Добавляем изображение 135
Приступаем к программированию 136
Программируем показ картинок 138
Проверка 138
Контекстное меню 138
Что у нас получилось 141
Глава 12. Программа учится сочинять 143
Проект «Писатель» 143
Форма и компоненты 144
Создаем и настраиваем списки 144
Добавление кнопки 147
Первая проверка 147
Присвоение начальных значений:
ищем событие 148
Как запрограммировать неожиданность 150
Программируем кнопку 151
Вторая проверка 153
Неповторяющиеся случайные числа 154
Глава 13. Учим программу запоминать 157
Как хранятся объекты 157
Что такое адресация 158
Двойная адресация 159
Адресация в компьютере .160
Переменные 161
Переменные и идентификаторы 162
Идентификаторы объектов и свойств 164
«Точечная» запись 165
Глава 14. Типы данных 167
Данные в памяти 167
Хранение переменных 173
Как компилятор узнает тип переменной 174
362
О больших и мнленысих буквах 177
Объявление переменных 178
Основные типы переменных 179
Преобразование типов 180
Глава 15. Управление при помощи
таймера 181
Описание игры «Поймай кнопку!» 181
Создаем прыгающую кнопку 182
Создаем кнопку для закрытия окна 185
Добавляем и настраиваем таймер 186
Приступаем к программированию 187
Программируем прыгающую кнопку 189
Последние штрихи 190
Подведем итоги 190
Домашнее задание 192
Глава 16. Учим программу думать 193
Искусственный интеллект
вкомпьютерныхиграх 193
Как компьютер «размышляет» 195
Условие 197
Простейшийусловный оператор 198
Многострочная запись 198
И да, и нет 199
Цепочка условных операторов 200
Оператор выбора 201
Глава 17. Таблица умножения 203
Описание программы 203
Размещаем движки 204
Настраиваем движки 205
Добавляем рамку 206
Добавляем надписи 207
Настраиваем надписи 207
Добавляем и настраиваем флажок 208
363
Программируем работу движков 208
Проверяем программу 210
Программируем влияние флажка 211
Программа готова 212
Самостоятельная работа 213
Глава 18. Движение по координатам 215
Описание программы : 215
Определяем область движения 217
Добавляем полосы прокрутки 217
Настраиваем полосы прокрутки 218
Добавляем подвижные фигуры 220
Дополнительные переменные 221
Управляем полосами прокрутки 222
Выбираем фигуру 223
Программа готова 224
Глава 19. Осваиваем компьютерную графику 227
Начнем с конца 227
Форма и компоненты 228
Настройка фигур 228
Первая проверка 230
Программируем лампочки 231
Что произойдет при наведении указателя 232
Нисходящее и восходящее программирование:
раздел для любознательных 234
Попадание в фигуру 235
Программа готова 238
Приведем окно в порядок 239
Выравнивание элементов 241
Глава 20. Кнопки с рисунками в простой
головоломке 243
Головоломка 243
Компьютерная версия 244
Форма и компоненты 244
Добавляем панель 245
364
Добавляем кнопки 246
Создаем и добавляем рисунки 247
Готовимся к программированию 249
Как различать объекты 251
Программируем работу кнопок 252
Возможен ли ход 253
Делаем ход 253
Первая проверка 254
Условия победы 255
Программа готова 257
Глава 21. Циклы и массивы 259
Циклические вычисления 259
Как работает процессор:
раздел для любознательных 259
Как процессор выполняет умножение 261
Организация цикла в языке программирования 263
Цикл со счетчиком 264
Важное замечание 266
Как из барана сделать бурун 267
Цикл while 267
Вернемся к нашим баранам 268
О стиле программирования 270
Что такое массив 271
Как массивы хранятся в памяти компьютера 273
Индекс массива 274
Многомерные массивы 275
Головоломка для сообразительных 275
Глава 22. Еще один электронный альбом 277
Что такое переключатели 277
Описание программы 278
Создаем переключатели 279
Учимся аккуратности 280
Добавляем демонстрационные объекты 282
Организация хранения данных 283
365
Готовимся к программированию 285
Программируем переключатели 287
Программируем раскрывающийся список 289
Начальная установка 289
Программа готова 291
Глава 23. Изучаем список флажков 293
Головоломка, которую мы решаем 293
Форма и компоненты 295
Создаем и настраиваем список флажков 295
Создаем дополнительные объекты 297
Установка и сброс флажков 298
Обновление суммы 300
Проверка решения 300
Программа готова 301
Глава 24. Разыгрываем сражение 303
Как устроен бой 303
Описание формы 305
Форма и компоненты 307
Создаем поле со счетчиком 308
Создаем поле со списком 309
Добавляем поле и кнопку 310
Добавляем поле вывода 311
Приступаем к программированию 312
Обработка числовых полей 313
Ввод чисел 315
Программа готова 317
Глава25. Головоломка«нафлажках» 321
Как устроена головоломка 321
Конструируем окно программы 322
Не все просто 323
Установка и сброс флажков 324
Проверка решения 327
Проверка работы программы 328
366
Глава 26. Компмотор рисует узоры 331
Описание программы 331
Содержание окпн 333
Добавляем объекты 334
Приступаем к программированию 334
Описываем конотянты и переменные 336
Инициализация данных 337
Обработка узорн 338
Программа готова 343
Эксперименты для любознательных 346
Приложение
Указатель компонентов, свойств, событий,
методов, стандартных функций и процедур 348
Ответы на вопросы 358
ЗАО «КОМПАНИЯ «АСТ-ПРЕСС»:
Россия, 107078, Москва, Рязанский пер., д. 3
(ст. м. «Комсомольская», «Красные ворота»)
Тел./факс 261-31-60, тел.: 265-86-30, 265-83-59
E-mail: ast_press @ col.ru
По вопросам покупки книг «АСТ-ПРЕСС» обращайтесь
в Москве: «АСТ-ПРЕСС.
Образование»
Склад:
в Москве: «Клуб Зб’б»
в Киеве: «АСТ-ПРЕСС-Дикси»
Офис: Москва, Рязанский пер., д. 3
Тел./факс: (095) 265-84-97,
265-83-29
E-mail: ast-pr-e@postman.ru
г Балашиха, ш. Энтузиастов, д. 4
Тел.: (095) 521-78-37, 521-03-72
Офис: Москва, Рязанский пер., д. 3
Тел./факс: (095) 261-24-90,
267-28-33, 523-92-63
Тел.: (044) 228-01-88,
464-08-74
Симонович Сергей Витальевич
Евсеев Георгий Александрович
ЗАНИМАТЕЛЬНОЕ ПРОГРАММИРОВАНИЕ:
С++
Книга для детей, родителей и учителей
Главный редактор С. Симонович
Научный редактор Г. Евсеев
Дизайнер обложки А. Поляков
Корректор О. Голотвина
Компьютерная верстка И. Симонович
ИД № 04467 от 09.04.2001.
Подписано в печать 24.09.01. Формат 60 x 90/16.
Печать офсетная. Бумага типографская.
Печ. л. 23,0. Тираж 20000 экз. Заказ № 1619. С-226.
Налоговая льгота — общероссийский классификатор продукции ОК-005-93,
том 2 — 953 000.
Гигиенический сертификат№ 77.99.02.953.Д.003869.07.01 от 11.07.2001 г.
ООО «АСТ-ПРЕСС КНИГА».
109202, Москва, ул. 2-я Фрезерная, д. 3, стр. 1.
При участии OOO «Издательский Дом «АСТ-ПРЕСС».
Отпечатано с готовых диапозитивов в Государственном
Московском предприятии «Первая Образцовая типография»
Министерства Российской Федерации поделам печати, телерадиовещания
и средств массовых коммуникаций.
113054, Москва, Валовая, 28.
С.Симонович, ГЕвсеев
Что такое компьютерная программа
Как создают программы
Знакомство с языком С++
и системой программирования Borland С++ Builder
Конструирование программ
из готовых компонентов
Отладка программ
В серии;
ПРЕСС
Занимательное ПРОГРАММИРОВАНИЕ: С++
Занимательное ПРОГРАММИРОВАНИЕ: Delphi
Занимательное ПРОГРАММИРОВАНИЕ: HTML
Занимательное ПРОГРАММИРОВАНИЕ: Visual Basic
АСТзанимательное
ПРОГРАММИРОВАНИЕ