Основы алгоритмизации и программирования - Учебное пособие (Струков В.М.)

1. основные отличительные особенности языка object pascal

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

1.1. Новая концепция типов данных

Основные отличия в этой части касаются следующих моментов:

Введены новые типы данных.

Введено понятие абсолютных и относительных типов данных.

Существенно развиты понятие и свойства типа  «объект».

Рассмотрим эти особенности более подробно.

Абсолютные и относительные типы данных

Все типы данных в языке Object Pascal разбиты на две группы:

Абсолютные типы.

Относительные типы.

Такое деление типов введено в связи с бурным развитием компьютерной техники и, в частности, программного обеспечения, а именно, операционных систем, и попыткой адаптироваться к ним. Так, операционные системы, начиная с МSDOS и заканчивая последними версиями Windows, претерпели существенные изменения. Одним из таких принципиально важных параметров операционных систем, претерпевших изменения, есть разрядность операционной системы, которая вначале равнялась 16 разрядам, затем 32 и уже последние версии операционных систем ряда Windows имеют 64-разрядное машинное слово. Аналогичные изменения произошли и с разрядностью системной шины материнской платы и разрядностью центрального процессора. В связи с этими изменениями введены типы данных, которые соответствующим образом адаптируются к этому. Такие типы будем называть относительными, поскольку размер оперативной памяти, который выделяется для их хранения, зависит от разрядности операционной системы и процессора компьютера. Так, например, наиболее широко применяемый тип integer в 16-разрядной операционной системе занимает два байта оперативной памяти, а в 32-разрядной – 4 байта. Соответствующим образом изменяется и диапазон представления значений величин этих типов.

Абсолютными типами будем назвать такие типы данных, характеристики которых не зависят от свойств операционной системы и технических характеристик компьютера.

Более подробно на характеристике конкретных абсолютных и относительных типов остановимся далее. Отметим здесь только еще один момент – приведенное деление типов относится в основном к простым типам, поскольку структурированные типы являются производными по отношению к простым типам.

Теперь подробнее остановимся на изменениях, которые произошли в конкретных типах данных.

Целые типы

Язык Object Pascal предоставляет программисту возможность оперировать с 9 целыми типами данных, различающимися затратами оперативной памяти и диапазоном представления чисел. В таблице 1.1. приведено описание абсолютных целых типов. Отметим, что максимальную производительность обеспечивают типы, разрядность которых совпадает с разрядностью машинного слова.

Таблица 1.1. Абсолютные целые типы

Имя типа

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

Формат представления

Shortint

-128 ÷ 127

Знаковый, 1 байт

Smallint

-32 768 ÷ 32 767

Знаковый, 2 байта

Longint

-2 147 483 648 ÷ 2 147 483 647

Знаковый, 4 байта

Int64

-263 ÷ 263 -1

Знаковый, 8 байт

Byte

0 ÷ 255

Беззнаковый, 1 байт

Word

0 ÷ 65 535

Беззнаковый, 2 байта

Longword

0 ÷ 4 294 967 295

Беззнаковый, 4 байта

Таким образом, из таблицы 1.1. видно, что по сравнению с языком Turbo Pascal в языке Object Pascal добавлены три новых целых типа – Smallint, Int64 и Longword, которые позволили значительно расширить диапазон представления целых чисел.

Описание относительных типов приведено в таблице 1.2.

Таблица 1.2. Относительные целые типы

Имя типа

Разрядность операционной системы

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

Формат представления

Integer

16

-32 768 ÷ 32 767

Знаковый, 2 байта

Cardinal

16

0 ÷ 65 535

Беззнаковый, 2 байта

Integer

32

-2 147 483 648 ÷ 2 147 483 647

Знаковый, 4 байта

Cardinal

32

0 ÷ 4 294 967 295

Беззнаковый, 4 байта

Из таблицы 1.2. видно, что в языке Object Pascal появился новый целый тип – Cardinal, а свойства типа Integer существенно изменились.

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

Группа вещественных типов также претерпела изменения.

К абсолютным вещественным типам отнесены типы, приведенные в таблице 1.3.

Таблица 1.3. Абсолютные вещественные типы

Имя типа

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

Количество значащих цифр

Требуемая память

Single

±1.5*10-45 ÷ ±3.4*1038

7 ÷ 8

4 байта

Real48

±2.9*10-39 ÷ ±1.7*1038

11 ÷ 12

6 байт

Double

±5.0*10-324 ÷ ±1.7*10308

15 ÷ 16

8 байт

Extended

±3.6*10-4932 ÷ ±1.1*104392

19 ÷ 20

10 байт

Comp

-263+1 ÷ 262-1

19 ÷ 20

8 байт

Currency

-922 337 203 685 477.5808 ÷ 922 337 203 685 477.5807

19 ÷ 20

8 байт

Таким образом, из таблицы 1.3. видно, что по сравнению с языком Turbo Pascal в языке Object Pascal добавлены два новых вещественных типа – Real48 и Currency.

Относительным вещественным типом является тип Real, который в настоящий момент соответствует типу Double.

Символьные типы

В Object Pascal к ранее известному типу Char добавлены два новых типа AnsiChar и WideChar, причем оба этих типа отнесены к категории абсолютных типов и имеют характеристики, описанные в таблице 1.4.

Таблица 1.4. Символьные типы

Имя типа

Хранимое значение

Требуемая память

AnsiChar

Один символ ANSI

1 байт

WideChar

Один символ Unicode

2 байта

Введение этих дополнительных типов связано с использованием систем кодирования информации, отличных от стандарта ASCII, который использовался в MSDOS, - ANSI и Unicode. Тип AnsiChar предназначен для представления символов в стандарте кодирования ANSI, а тип WideChar, соответственно, - для кодирования символов в стандарте Unicode, в котором каждый символ кодируется числами от 0 до 65 535. Первые 256 символов Unicode совпадают с символами ANSI.

Тип Char отнесен к категории относительных типов и на настоящий момент предназначен для представления символов в кодировке ANSI.

Замечание. Отметим, что при объявлении одномерного массива из элементов типа Char, в отличие от языка Turbo Pascal, в Object Pascal нумерация элементов массива обязательно должна начинаться с 0. В противном случае транслятор выдаст сообщение об ошибке.

Логические типы

В Object Pascal для совместимости с другими системами программирования введены еще несколько логических типов. Свойства логических типов представлены в таблице 1.5.

Таблица 1.5. Логические типы

Имя типа

Хранимое значение

Требуемая память

Boolean

Логическое значение True или False

1 байт

Bytebool

Логическое значение True или False

1 байт

Wordbool

Логическое значение True или False

2 байта

Longbool

Логическое значение True или False

4 байта

Основным логическим типом, кроме специально оговоренных случаев, является тип Boolean.

Строковые типы

Строковые типы в Object Pascal представлены тремя абсолютными типами и одним относительным. Характеристики абсолютных типов приведены в таблице 1.6.

Таблица 1.6. Строковые типы

Имя типа

Максимальная длина

(количество символов)

Наличие нулевого символа в конце строки

ShortString

255 байт

Нет

PChar

231 байт (2 Гб)

Есть

AnsiString

 231 байт (2 Гб)

Есть

WideString

230 байт (1 Гб)

Есть

Введение дополнительных типов обусловлено двумя причинами:

отсутствием единого стандарта кодирования информации в компьютерах и

разными способами представления строк в оперативной памяти в различных системах программирования.

Так, в языке С++ строка представляет собой цепочку символов, которая заканчивается символом #0. Этот символ и определяет длину строки. В языке Turbo Pascal количество символов в строке задается в первом байте строки, который имеет нулевой индекс. Таков формат представления данных в типе String. Каждый из этих форматов имеет свои достоинства и недостатки.

Тип ShortString эквивалентен типу String в языке Turbo Pascal и фактически представляет собой статический одномерный массив символов, количество символов в котором задается в объявлении. Номера значащих символов строки начинаются с 1. В нулевом символе хранится текущая длина строки.

Тип PChar позаимствован из родительского языка Turbo Pascal и представляет собой указатель на динамически выделяемую область памяти, в которой непосредственно хранится строковое значение с нулевым символом в конце. К отдельному символу строки типа PChar можно обращаться как к элементу одномерного массива, номера которого начинаются с 0.

Тип AnsiString является почти полным аналогом типа PChar. Символы в строке этого типа кодируются в стандарте ANSI. К отдельному символу строки типа AnsiString также можно обращаться как к элементу одномерного массива, номера которого начинаются с 1, в отличие от типа PChar.

Тип WideString также является указателем на динамически выделяемую область памяти, в которой непосредственно хранится строковое значение с нулевым символом в конце. Символы в строке этого типа кодируются в стандарте Unicode.

Относительным строковым типом в языке Object Pascal является тип String. Формат представления данных в переменных этого типа определяется директивой компилятора $Н. Если включена директива {$Н+} (она включена по умолчанию), то String интерпретируется компилятором как тип AnsiString — длинная строка с нулевым символом в конце. Если же выключить директиву {$Н-}, то String интерпретируется компилятором как тип ShortString — короткая строка без нулевого символа в конце.

Если в объявлении типа после ключевого слова String следует число сим­волов в квадратных скобках (например, String[7]), то, независимо от директив компилятора, тип трактуется как строка без нулевого символа в конце с ука­занным числом символов.

Для преобразований числовых значений в строковые и обратно (строковых в числовые) в Object Pascal используются следующие функции:

IntToStr(<I>):string; - преобразование целочисленного значения в строковое; <I> - целочисленное выражение;

FloatToStr(<F>):string; - преобразование вещественного значения в строковое; < F > - вещественное выражение;

StrToInt (<str>):Integer; - преобразование целочисленного значения в строковое; < str > - строковое выражение;

StrToFloat(<string>):Double; - преобразование строкового значения в вещественное; < str > - строковое выражение.

Тип Variant

Тип variant – это новый тип данных, который предназначен для использования в тех случаях, когда: тип того или иного объекта заранее не известен либо он может изменяться в процессе выполнения программы или когда какие-то функции и процедуры требуют именно такой тип аргумента.

В одной и той же переменной типа variant в различные моменты времени могут храниться данные любых типов, кроме записей, множеств, статических массивов, файлов, классов, ссылок на клас­сы, указателей и Int64.

Пример объявления переменной типа variant:

Var V:variant;

Переменные типа variant могут использоваться как опе­ранды в любых операциях, кроме ^, is и in, совместно с другими величинами типов variant, integer, real, string, boolean. Компилятор в этих случаях автома­тически осуществляет приведение типов величин в выражении.

Таким образом, для объявленной выше переменной V допустима следующая последовательность операторов:

V:=17;      // тип V становится равным integer

. . .

V:=1.2e10;           // тип V становится равным real

. . .

V: = Editl.Text;   // тип V становится равным string

При этом надо иметь в виду, что затраты памяти и времени на работу с переменными типа variant больше, чем при работе с обычными типами. К тому же недопустимые операции с переменными типа variant приводят к ошибкам в процессе выполнения, тогда как аналогичные недопустимые операции с переменными других типов выявляются на эта­пе трансляции.

Переменная типа variant занимает в оперативной памяти 16 битов и содержит код типа и значение переменной или указатель на это значение. В момент создания эти переменные инициализируются специальным значением Unassigned. Значение переменной nil свидетельствует о неизвестном или ошибочном значении переменной.

Определить действительный тип значения переменной типа variant можно с помо­щью функции VarType.

Динамические массивы

Следует отметить, что динамическое использование оперативной памяти всегда было одним из наиболее слабых мест языка Pascal, что было одним из веских аргументов тех профессиональных программистов, которые отдавали предпочтение другим алгоритмическим языкам, таким, к примеру как С++. Некое слабое подобие динамических массивов можно было сконструировать в языке Turbo Pascal с помощью переменных типа указатель. Однако действительно эффективное и удобное средство рационального использования оперативной памяти - динамические массивы появились в Object Pascal, начиная с Delphi 4. Они отличаются от обычных статических массивов тем, что в них не объявляется заранее длина — число элементов и, соответственно, не выделяется оперативная память до того момента, когда в ней возникнет необходимость. Поэтому динамические массивы удобно использовать в приложениях, где объем обрабатываемых массивов заранее неизвестен и определяется в процессе выполнения.

Объявление одномерного динамического массива содержит только его имя и тип элементов — один из базовых типов. Формат объявления одномерного динамического массива:

<имя_>: array of <базовый тип>;

Например, в следующем предложении

var A: array of integer;

объявлена переменная А как динамический массив целых чисел.

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

SetLength(А,10);

Этим оператором для массива А выделяется участок оперативной памяти для 10 элементов и задаются нулевые значения всех элементов.

Индексы динамического массива — всегда целые числа, начинающиеся с 0. Таким образом, в приведенном примере массив содержит элементы от А[0] до А[9].

Многомерный динамический массив определяется как динамический массив динамических массивов динамических массивов и т.д. Например, в предложении:

var A2: array of array of integer;

объявлен двумерный целочисленный динамический массив.

Для выделения оперативной памяти под такой массив можно использовать процедуру SetLength, но в этом случае передавать ей в качестве параметров нужно не один, а несколько размеров. Например, оператор

SetLength(A2,3,4);

выделит в оперативной памяти место для двумерного целочисленного массива из 3 строк и 4 столбцов.

Обращение к элементам многомерных динамических массивов осуществляется так же, как и для статических массивов. Например, А2[1,2] – это элемент, лежащий на пересечении второй строки и третьего столбца (индексы считаются от нуля, так что индекс 1 соответствует второй строке, а индекс 2 — третьему столбцу).

1.2. Основы объектно-ориентированного программирования

Объектно-ориентированное программирование (ООП) – это современная бурно развивающаяся технология программирования, которая «выросла» из технологии модульного программирования и в настоящее время внедрена во все современные инструментальные системы разработки сложных программных продуктов.

Исторически с момента возникновения программирования достаточно долго развивалось одна доминирующая технология программирования –процедурное программирование. В основе этой технологии лежал следующий подход: вначале для разрабатываемой программы определялись функции и действия (процедуры), которые эта программа должна выполнять, а затем они реализовывались на алгоритмическом языке в рамках одной или нескольких программ. Типичным примером процедурно-ориентированного языка является Фортран – первый и один из наиболее удачных и популярных языков программирования высокого уровня с транслятором компилирующего типа.

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

Впервые идеи ООП были реализованы в 1980 г. на языке Smalltalk. В Паскале эта технология начала поддерживаться, начиная с версии Turbo Pascal 5.5. Его основная идея заключается в создании такого типа данных, который объединяет непосредственно данные с обрабатывающими эти данные процедурами в единое целое – объект. Такое объединение (инкапсуляция) данных и алгоритмов их обработки привело к тому, что отдельно взятые и данные, и процедуры во многом потеряли самостоятельное значение, образовав новый комплексный тип данных.

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

1.2.1. Основные принципы ООП

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

Объектно-ориентированное программирование основано на трёх важнейших принципах:

инкапсуляция

наследование

полиморфизм.

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

Другим важным следствием инкапсуляции является лёгкость обмена объектами, переноса их из одной программы в другую. Можно сказать, что ООП стимулирует разработку личных библиотек объектов с целью их последующего использования при разработке других программ. Это обеспечивает высокую «живучесть» и «открытость» программных комплексов, разрабатываемых с использованием технологии ООП.

Наследование - это свойство, позволяющее использовать уже объявленные объекты для построения новых объектов, производных от них, или иначе – свойство объектов-родителей порождать объекты-потомки. Объект-потомок автоматически наследует от родителя все поля и методы, может дополнять объекты новыми полями и заменять (перекрывать) методы родителя или дополнять их. Это означает, что при описании объекта-потомка можно использовать все поля и методы родителя в потомке как если бы они уже в нем были описаны.

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

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

Полиморфизм – это свойство родственных объектов (т.е. объектов, имеющих одного общего родителя) выполнять сходные по смыслу действия методами с одним и тем же именем, причем каждый объект может «заказывать» свою особенность реализации этого действия. Изменяя алгоритм того или иного метода в потомках объекта, можно придавать потомкам отсутствующие у родителя специфические свойства. Для изменения метода необходимо перекрыть его в потомке, т.е. объявить в потомке одноименный метод и реализовать в нем нужные действия. В результате в объекте - родителе и объекте – потомке будут действовать два одноименных метода, выполняющие похожие действия, но имеющие различия применительно к своим объектам. В качестве такого действия можно привести перемещение различных объектов на плоскости или в пространстве. Сама сущность действия для любого объекта будет оставаться одной и той же - перемещение, а программная реализация в каждом случае будет различной Это и называется полиморфизмом объектов.

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

1.2.2. Описание объектов. Классы

Итак, объект – это структура, компонентами которой являются взаимосвязанные данные различных типов, которые называются полями объекта, а также процедуры и функции, которые обрабатывают эти данные (методы объекта). Специфическим видом поля является свойство.

Для реализации объекта в Object Pascal введен специальный тип данных - Class. Причем допускается использование наряду с этим ключевым словом обозначения Object, которое используется в языке Turbo Pascal для описания объекта.

Class - это тип, который описывает категорию объектов, обладающих одинаковыми свойствами и поведением. При этом объект представляет собой просто экземпляр (переменную) какого-либо класса.

Формат объявления класса следующий:

Туре

<имя класса> = Class [ (<имя класса — родителя>)]

public

// доступно всем

<поля, методы, свойства, события>

published

// видны в Инспекторе Объектов и изменяемы

<поля, свойства >

protected

// доступ только потомкам

<поля, методы, свойства, события>

private

// доступ только в модуле

<поля, методы, свойства, события>

end;

Имя класса может быть любым допустимым идентификатором. Но принято идентификаторы большинства классов начинать с символа «Т».

public, published, protected и private – директивы компилятора, определяющие степень видимости и доступности элементов класса.

При объявлении объектов должны выполняться следующие требования:

Описание типа Class должно выполняться в разделе