Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

[ Миронченко ] Императивное и объектно-ориентированное програмирование на Turbo Pascal и Delphi

.pdf
Скачиваний:
68
Добавлен:
25.04.2014
Размер:
3.16 Mб
Скачать

251

Результат, который получается в результате действий Tn над входным словом m (т.е. слово, которое остается на ленте после окончания работы МТ), обозначим Tn (m) .

Теперь, после того, как мы научились кодировать программы для МТ, можно записать выражение для универсальной МТ:

U (n, m) = Tn (m)

Если на входном слове m МТ Tn остановится, то число, которое останется на ленте, - это ответ, который выдаст МТ: Tn (m) = p . Если же МТ не остановится, то мы запишем это так: Tn (m) = .

Теперь рассмотрим «проблему останова»: существует ли МТ, которая может для любой машины Тьюринга и входного слова определить, остановится ли эта машина Тьюринга на данном входном слове.

Если вдруг окажется, что такой МТ не существует то тогда, если мы принимаем тезис Чёрча-Тьюринга, то выходит, что вполне четко сформулированную алгоритмическую проблему – проблему остановки – алгоритмически решить нельзя.

Давайте предположим, что решение задачи остановки существует. Тогда можно построить следующую МТ:

0,Tn (m) =

S (n, m) =

1,Tn (m)

Если существует Машина Тьюринга S (n, m) , то мы можем построить и другую

МТ:

T (m), S (n, m) = 1

Q(n, m) = Tn (m) S (n, m) = n

0, S (n, m) = 0

(Мы ввели операцию , которая обладает такими свойствами: a 1 = a для любого натурального a , † 0 = 0 ).

При m = n , т.е. когда на вход машины Тьюринга Tn будет подаваться ее

собственный номер, получим: Q(n, n) = Tn (n) S (n, n) .

Теперь давайте рассмотрим машину Тьюринга 1+ Q(n, n) . Такая машина существует, т.к. существует МТ Q(n, m) . Пусть ее номер будет равен k :

Tk (n) = 1+ Tn (n) S (n, n) .

Давайте теперь на вход машины Tk подадим ее собственный номер. Тогда получим:

Tk (k ) = 1+ Tk (k ) S (k, k )

Но если S (k , k ) = 0 (т.е. Tk не останавливается на входном слове k ), то получим, что = 1, что неверно.

Если же S (k, k ) = 1, то Tk (k ) = 1+ Tk (k ) , что также неверно. Третьего случая быть не

может, поэтому мы пришли к противоречию.

Следовательно, не существует машины Тьюринга, которая могла бы определить для любого входного слова и любой МТ, останавливается ли эта машина на данном входном слове.

Таким образом, Тьюринг показал, что не существует процедуры, которая позволяет решать любую алгоритмическую задачу.

252

14.4.Простейший модульный язык программирования

Впервом разделе этой главы мы установили отличительные черты модульных языков. Во втором разделе мы, следуя Алану Тьюрингу, построили простейший язык программирования. В этом разделе мы рассмотрим, каким должен быть простейший язык программирования, удовлетворяющий условиям модульности.

Основу простейшего модульного языка должны составить:

1.Подпрограммы (с рекурсией) и модули.

2.Оператор If

3.Типы данных: любой числовой, любой символьный, pointer, record

4.Другие операторы: оператор присваивания, операторы сравнения.

5.Приведение типов (для бестиповых указателей)

Подпрограммы и модули нужны, т.к. иначе это – вообще не модульный язык. Т.к.

через рекурсию и if можно выразить циклы, то циклы можно выбросить.

Из типов данных нам нужен числовой тип и символьный (надо как-то задать соответствие между символами и их кодами) – эти два типа и будут базовыми.

Pointer нужен для того, чтобы можно было задавать последовательные структуры данных (с помощью указателей можно ввести индексацию). Record позволит строить пользовательские типы, что дает возможность структурировать типы данных. На основе перечисленных 4-х типов можно создать любой тип данных, причем он будет полностью соответствовать условиям модульности языка.

Вописании, которое было дано выше, я не учитывал эффективность реализации, т.е. скорость работы программ. На уровне процессора эффективно реализованы операции с вещественными числами, поэтому хотя мы можем свести операции с плавающей точкой к целочисленной арифметике, однако вычисления станут значительно медленнее.

То же справедливо и для строк. В ассемблере есть цепочечные команды, на основе которых можно выполнять строковые операции довольно быстро.

Вописанном выше простейшем модульном языке есть один неприятный момент

использование бестиповых указателей. Вроде бы бестиповый указатель – это тоже тип данных, поэтому условие типизированности языка не нарушается. Но все равно хотелось бы от них избавиться но при этом не потерять той общности, которую они нам дают.

Во второй части книги мы рассмотрим то, каким путем эту проблему пытается решить объектно-ориентированная методология программирования.

14.5. Взаимодействие с операционной системой

Вы знаете, что программа – это последовательность элементарных команд, написанных на машинном языке. Набор команд процессора позволяет ему работать с аппаратурой ПК: изменять значение любой ячейки памяти, посылать команды принтеру и монитору. Логично предположить, что программа, которую мы можем написать, может заставить процессор все это делать. Однако на каком бы языке вы не писали программу, она изначально ограничена в возможностях – и ограничивает ее операционная система (ОС).

253

Всеми программами, которые выполняются на ПК, руководит ОС. ОС определяют иногда как «расширенную машину», имея в виду то, что ОС предоставляет набор подпрограмм, которые позволяют программам работать с аппаратурой. Иногда говорят, что ОС - управляющий ресурсами (или «менеджер ресурсов», потому что сейчас даже уборщица – «менеджер по клинингу»), т.к. она распределяет ресурсы ПК между приложениями. Мы определим ОС так:

ОС – программа, являющаяся прослойкой между аппаратным обеспечением и прикладными программами и предоставляющая функции, позволяющие прикладным программам работать с аппаратным обеспечением и взаимодействовать друг с другом.

Прикладные программы

Вызовы функций ОС

Операционная система

Работа с аппаратурой

Аппаратное обеспечение

Рис 14.2. Упрощенная схема взаимодействия программ с ОС

Основные функции ОС:

1.Скрывать от прикладных программ аппаратуру и предоставлять удобный набор функций для работы с ней.

2.управление ресурсами

3.защита приложений друг от друга

4.Различные дополнительные возможности, например: поддержка многопоточности, возможность работы с несколькими процессорами и т.д.

Прежде, чем расшифровать все 4 пункта, введем важное определение:

Процесс – выполняемая программа, включая текущие значения счетчика команд, регистров переменных, используемые ресурсы (например, открытые файлы).

Программа (последовательность команд), находящаяся на жестком диске и не запущенная на выполнение, не является процессом. Когда программа запускается, ОС выделяет ей участок динамической памяти, а также предоставляет на некоторое время доступ к процессору. При этом ОС содержит список процессов, которые находятся в памяти и информацию о них. Когда процесс отработает выделенное ему время, то он должен сохранить свои регистры, чтобы, когда ему снова дадут доступ к процессору, он мог записать во все регистры нужные значения и продолжить свою работу.

254

Одной программе может соответствовать несколько процессов (например, если вы открываете несколько документов с помощью Блокнота).

Можно сказать, что процесс объединяет в себе всю информацию, которая необходима программе, чтобы выполняться под управлением ОС.

Сокрытие аппаратуры

Непосредственная работа с устройствами ввода-вывода (жесткий диск (HDD – Hard Disk Drive), Flash-память и др.) довольно громоздка. Если бы программист должен был знать все тонкости работы с каждым устройством, то программирование превратилось бы в ад. Поэтому ОС содержит набор функций, которые позволяют более удобно работать с устройствами ПК. Некоторые ОС (такие, как Windows) полностью скрывают аппаратуру, поэтому работать с ней можно лишь с помощью функций ОС, а некоторые в принципе дают возможность прямого обращения к устройствам

(например, MS-DOS).

Управление ресурсами

Ресурсами ПК является его аппаратура. Управление ресурсами означает, что ОС должна как можно более эффективно разделять ресурсы между процессами.

Основными ресурсами являются:

1.Процессор

2.Оперативная память (ОП)

3.Энергонезависимая память (например, жесткий диск (ЖД), флэш-ОЗУ).

Если в памяти одновременно находятся несколько процессов, то ОС должна разделить время процессора между ними.

Часть ОС, которая распределяет время процессора между процессами, называется планировщиком процессов.

Планировщик процессов выделяет процессам квант времени. Когда он истекает,

планировщик дает квант времени следующему процессу и т.д.

Часть ОС, которая управляет дисковым пространством, называется файловой системой.

Файл – именованная область энергонезависимой памяти.

Каталог – системный файл, поддерживающий структуру ФС.

С точки зрения пользователя, каталог содержит набор подкаталогов и файлов.

ФС содержит информацию о каждом файле и каталоге, который находится на диске (размер, время создания, полное имя и т.д.). Сама же ОС содержит функции, которые позволяют создать новый файл на диске, открыть его для чтения и/или записи и т.д. Способ хранения файлов в разных системах может быть различен. Например, в файловых системах для CD, CD-R дисков удобно хранить файл просто как набор идущих подряд байтов. В ФС FAT-32 ЖД разбивается на сегменты одинакового размера. Каждый файл может занимать один или несколько сегментов. ФС запоминает для каждого файла начальный сегмент и в специальной FAT-таблице для каждого сегмента ЖД ФС запоминает следующий за ним сегмент.

Аналогично, в ОС есть подпрограммы для работы с другими устройствами, например, оперативной памятью (выделение динамической памяти, освобождение ее). Вы знаете, что есть менеджер кучи, который заботится о состоянии динамической памяти. Эта утилита также является частью ОС. Для того, чтобы эффективно управлять

255

динамической памятью, каждому процессу ОС передает некоторое количество ДП. Затем, если процессу надо больше памяти, ОС может добавить ее.

Защита приложений друг от друга

Вы знаете, что одновременно в памяти могут находиться несколько процессов, следовательно, они должны быть так разграничены, чтобы они не могли нарушать работу других приложений. Для этого ОС предоставляет каждому процессу собственное адресное пространство, в котором процесс может работать как ему вздумается. Но затем это пространство отображается на настоящую ОП.

Многопоточность

Часто надо, чтобы процесс был разбит на несколько «подпроцессов», которые действовали бы «одновременно» (т.е. псевдопараллельно). Например, если мы программируем игру в шашки, то пока мы думаем, процессор будет простаивать в ожидании хода. Гораздо эффективнее было бы часть этого времени предоставить электронному игроку, чтобы он мог думать одновременно с нами. В таком случае разбить процесс на 2 «подпроцесса» было бы разумно.

Поток – это выполняемая программа (набор команд), включая регистры и переменные стека.

Т.е. поток – это аналог процесса, только все потоки внутри одного процесса выполняются в одном адресном пространстве (т.е. ОС память выделяет процессу в целом, а не потокам по отдельности), и совместно используют ресурсы, принадлежащие процессу.

Если ОС может поддерживать возможность разделения процессов на несколько потоков, то такая ОС называется многопоточной.

14.6. Загрузка ОС

При включении ПК происходит процесс, который называется самозагрузкой. Центральный процессор (ЦП) построен так, что он считывает первую команду всегда из определенной области памяти. Как правило, эту область памяти конструируют так, чтобы информация, записанная на ней, не изменялась. Поэтому такую память называют ПЗУ (постоянное запоминающее устройство). В этой памяти находится специальная программа, называющаяся программой первоначальной загрузки, которая и предписывает процессору загрузить в энергозависимую память операционную систему. После того, как начинают выполняться команды ОС, она может полностью контролировать работу ПК.

Набор функций, который ОС предоставляет пользовательским программам, в Windows называется Win32 API (Application Programming Interface). Так как большую часть работы с аппаратурой ОС берет на себя, то как следствие прикладные программы становятся гораздо более аппаратно-независимыми.

Однако зачастую неудобно использовать функции ОС напрямую, поэтому во всех системах разработки приложений есть набор модулей, которые позволяют упростить выполнение тех или иных действий для программиста. Кроме того, проектировщики систем разработки хотят сделать приложения, написанные на этих программных продуктах, независимыми или практически независимыми от ОС. Изучая Delphi, мы посмотрим, насколько это им удалось.

256

Часть II Объектно-ориентированное программирование

Предисловие ко 2-й части

Delphi – объектно-ориентированный язык программирования, являющийся расширением языка Pascal. Раньше этот язык программирования называли Object Pascal, a именем Delphi называли всю среду разработки приложений. В принципе, базовые возможности ООП поддерживает и Turbo-Pascal, однако набор средств, который он предоставляет, довольно скромен, поэтому ТР обычно называют объектным языком.

Помимо объектно-ориентированности, важным отличием Delphi от ТР является то, что среда разработки Delphi ориентирована на разработку визуальных приложений под Windows (в отличие от ТР, который работал, основываясь на функциях DOS), поэтому стандартная библиотека классов и подпрограмм разработана полностью на основе функций Win32 API. Кроме того, в Delphi 2005 есть возможность программировать на платформе .NET. Следовательно, стандартных модулей, которые мы использовали в ТР, в Delphi нет. Но вы не должны сильно огорчаться по этому поводу, т.к. мы не вдавались в изучение всех функций этих модулей, а использовали лишь несколько подпрограмм.

Есть аналог Delphi и для ОС Linux, который называется Kylix. Структура следующих глав такова:

В15-й главе мы рассмотрим основные отличия Delphi от ТР, которые не касаются объектно-ориентированного программирования (ООП).

В16-й главе мы разберемся, что такое ООП, и какие ОО-возможности есть у

Delphi.

В17-й главе вы научитесь писать оконные приложения под Windows. В частности, мы рассмотрим, что такое исключения и как с ними работать, как писать многопоточные приложения, а также изучим основные видимые компоненты. Количество встроенных компонентов огромно, и рассматривать всех их не имеет смысла, поэтому мы изучим лишь несколько самых основных, но для того, чтобы вы могли легко и быстро разбираться с другими компонентами, мы изучим свойства, которые присущи всем компонентам в целом.

В18-й главе мы рассмотрим базовые графические возможности Delphi и научимся рисовать фракталы.

И, наконец, в последней главе мы подведем итог под всеми нашими знаниями программирования, критически разберем ООП вообще и некоторые ОО-языки в частности, а также попытаемся решить проблемы ООП, разработав новую концепцию –

везенспрограммирование (Wesensprogrammierung).

257

Глава 15: Основы Delphi

15.1. Среда Delphi

Для работы вам понадобится среда разработки Delphi (например, Delphi 2005, хотя вполне подойдет и Delphi версий 6,7). Основное отличие Delphi 2005 от Delphi предыдущих версий - это поддержка программирования как под Win32, так и на платформе .NET. Наша цель – изучить объектно-ориентированное программирование, а не заниматься различными «технологиями», поэтому мы не будем рассматривать программирование на платформе .NET (ей посвящено достаточно много литературы). Основные отличия в языке между Delphi 2005 и Delphi 7 мы опишем в разделе 16.20. В остальной части учебника все примеры будут работать как под Delphi 2005, так и под

Delphi версий 6,7.

Запустите Delphi 2005. После этого зайдите в пункт меню File и выберите раздел

New -> VCL Forms Application – Delphi for Win32. После этого вы увидите следующее окно:

Справочная служба Структура Проекта

Запуск

программы

Форма

Инспектор

Объектов

Переход к программному коду

Переход к окну редактирования формы

Набор компонентов

 

Рис 15.1. Среда разработки Delphi 2005

Краткие пояснения

Любые оконные приложения содержат хотя бы 1 форму (так называется в Delphi окно).

Каждая программа содержит основные объекты. Они отображаются в «Дереве объектов».

258

Инспектор объектов позволяет задавать свойства объектов, которыми они будут обладать в момент загрузки.

Кнопка Code позволяет перейти непосредственно к программному коду вашей программы. Если вы уже сделали это, то увидели, что Delphi по умолчанию генерирует стандартный код.

Набор компонентов – это готовые примитивы, которые вы можете использовать внутри формы, например: кнопки, поля для ввода текста, различные диалоги, области рисования и т.д.

Справочная служба - непременный помощник в вашей работе. Она довольно содержательна и при этом относительно компактна.

Кнопка запуска позволяет запустить приложение. Также это можно сделать, нажав F9. Если вы хотите откомпилировать программу, не запуская ее, нажмите Ctrl+F9.

Для того чтобы понять, как функционирует оконное приложение, надо знать ООП. Поэтому в первых двух главах, посвященных Delphi, мы будем продолжать писать консольные приложения (т.е. приложения, которые используют окно с командной строкой).

В дальнейшем изложение будет ориентировано на студию 2005 как более новую, однако я думаю, что у читателя не возникнут большие затруднения при работе со студиями более ранних версий. Перевод приложений из Delphi 6, 7 в Delphi 2005 не сложен, однако могут быть проблемы с русскими символами: несмотря на то, что в Delphi 2005 можно использовать кириллицу даже в идентификаторах (равно как и немецкие буквы ö, ä, ü, ß), однако по непонятным причинам Delphi 2005 не разрешает писать в словах строчную букву «я». Поэтому в примерах, реализованных в Delphi 2005 я строчные «я» заменил на заглавные.

15.2. Консольное приложение в Delphi

Для того чтобы создать консольное приложение, зайдите в пункт меню File -

>New->Other.

В окошке, которое вы после этого увидите, выберите Console Application в разделе

Delphi Projects.

После этого вы увидите окно с простым кодом:

Пример 1: Консольное приложение. program Project1;

{$APPTYPE CONSOLE} uses

SysUtils; begin

{ TODO -oUser -cConsole Main : Insert code here } end.

Директива компилятору {$APPTYPE CONSOLE} предназначена для того, чтобы компилятор знал, что приложение будет консольным. Тогда компилятор создаст консоль, в которой можно будет работать в командной строке. В противном случае консоль не будет создана и процедуры writeln, readln будут работать лишь для работы с файлами. Но не забывайте, что несмотря на внешний вид приложения, в ТР вы

259

работали под управлением DOS, а консоль в Delphi – это приложение, написанное под 32-разрядные версии Windows13.

Модуль SysUtils по умолчанию указывается в директиве uses для всех приложений автоматически, но для простейших программ он не обязателен. Никаких форм с консольным приложением не связано.

15.3. Изменения в Delphi (по сравнению с ТР)

Числовые типы

В Delphi некоторые из числовых типов данных, которые мы использовали в ТР, изменили свои значения, появилось много новых стандартных типов. Некоторые типы, как, например, extended, определяются в Win32 и .NET по-разному (за подробностями обращайтесь к справочной системе). Мы сейчас быстро обсудим основные отличия.

Целые типы

Основными типами являются Integer и Cardinal.

Тип

Диапазон значений

Объем занимаемой памяти

Integer

–2147483648..2147483647

4 байта

Cardinal

0..4294967295

4 байта

Все паскалевские целые типы, кроме Integer, перешли в Delphi без изменений. Появились и новые типы. Подробнее смотрите справку Delphi (ключевые слова – simple

types, integer types).

Вещественные типы

Все паскалевские типы, кроме real, не изменились. Real стал эквивалентен старому типу Double. Появилось 2 новых типа: Real48 и Currency.

Тип

Диапазон значений

Значащие

Объем

 

 

цифры

занимаемой

 

 

 

памяти

Real

5.0*10^–324 .. 1.7 *10^308

15-16

8

Real48

2.9*10^–39 .. 1.7*10^38

11-12

6

Currency

–922337203685477.5808..

19-20

8

 

922337203685477.5807

 

 

В справочной системе можете набрать simple types, или real types, чтобы узнать более подробную информацию о вещественных типах.

Комментарий

Помимо комментария {}, который был в ТР, и (* *), который остался в наследство от обычного Рascal, в Delphi перекочевал из С++ однострочный комментарий:

// Это – комментарий.

13 Windows 2000 и более поздние версии Windows могут эмулировать работу DOS, поэтому многие приложения, написанные под DOS, могут работать и под этими ОС.

260

Использование подпрограмм

В ТР результат функции мог быть только простого типа (число, символ, указатель или строка). В Delphi это ограничение снято. Результат функции может быть любого типа. В ТР результат функции присваивался ее имени, а в Delphi введена специальная переменная Result, в которую надо присваивать значение функции, хотя старый способ оставлен для совместимости с более ранними версиями Паскаля.

Перегрузка подпрограмм

Перегрузкой подпрограмм называется объявление 2 подпрограмм с одинаковыми названиями, но разными сигнатурами (т.е. набором параметров и результатом). Например, вы можете объявить 2 такие функции:

function MeinMax(x,y:integer):integer;overload begin

if x>y then result:=x

else result:=y;

end;

function MeinMax(x,y:real):real;overload begin

if x>y then result:=x

else result:=y;

end;

Для того, чтобы компилятор понял, что подпрограммы перегружены, после их объявления надо ставить директивы overload (т.е. перегруженная). В ТР перегрузки не было, хотя она очень удобна.

Out-параметры

Появился новый тип параметров – out-параметры. Пример процедуры с outпараметром:

procedure Proc(out X:real;a:integer); begin

X:=a/2;

end;

Out-параметры – это ссылки, как и const- и var-параметры. Out-параметры – противоположность const-параметров: если вторые нельзя изменять, но можно использовать, то первые нельзя использовать, но можно изменять.

Умалчиваемые параметры

В Delphi можно писать функции, в которых некоторые параметры можно пропускать.

Например:

procedure Schweiger(a:real;b:integer=0);