Перечислимый тип описывает множество, состоящее из элементов-констант, иногда называемых нумераторами или именованными константами.
Значение каждого нумератора определяется как значение типа int. По умолчанию первый нумератор определяется значением 0, второй - значением 1 и т.д. Для инициализации значений нумератора не с 0, а с другого целочисленного значения, следует присвоить это значение первому элементу списка значений перечислимого типа.
Например:
// Создание перечисленияenum eDay{sn, mn, ts, wd, th, fr, st} day1; // переменная day1 будет принимать // значения в диапазоне от 0 до 6day1=st; // day1 - переменная перечислимого типаint i1=sn; // i1 будет равно 0day1= eDay(0); // eDay(0) равно значению snenum(color1=255); // Объявление перечисления, определяющего // именованную целую константу color1int icolor=color1;enum eDay2{sn=1, mn, ts, wd, th, fr, st} day2; // переменная day2 будет принимать // значения в диапазоне от 1 до 7
Для перечислимого типа существует понятие диапазона значений, определяемого как диапазон целочисленных значений, которые может принимать переменная данного перечислимого типа.
Для перечислимого типа можно создавать указатели.
Лекция #9: Структурированные типы данных языка Object Pascal Приводится синтаксис и семантика объявления производного типа. Вводится понятие множества, массива, записи и файла.
Объявление производного типа
Кроме базовых типов данных, рассмотренных в лекции 6, язык Object Pascal предоставляет простые и структурированные типы данных.
Используя базовые, простые и структурированные типы данных, можно создавать производные типы данных.
Напомним, что к базовым типам относятся:
целочисленный тип;
действительный тип;
логический тип;
символьный тип;
строковый тип.
К простым типам относятся:
порядковый тип;
перечислимый тип.
К структурированным типам относятся:
множества;
массивы;
записи;
файлы;
объектный тип (тип класса);
тип ссылки на класс.
Для того чтобы создать переменную производного типа, сначала следует определить этот производный тип. Объявление нового производного типа указывается ключевым словом type.
Объявление произвольного типа как нового имени существующего типа в языке Object Pascal имеет следующее формальное описание:
type имя_нового_типа =
имя_существующего_типа;
Объявление произвольного типа как нового типа в языке Object Pascal имеет следующее формальное описание:
type имя_нового_типа =
type имя_существующего_типа;
При объявлении произвольного типа как нового имени типа, тип переменных производного типа и типа, используемого для его создания, будет совпадать. Если перед именем существующего типа указать ключевое слово type, то компилятор создаст новый тип. При этом указание в качестве формального параметра подпрограммы переменной существующего типа, а в качестве фактического параметра подпрограммы - переменной нового типа, созданного на базе данного существующего типа, вызовет для var-параметров ошибку времени компиляции.
В языке Object Pascal объявление типов и переменных может быть выполнено только в определенных местах программы: секция объявления типа type используется для объявлений типов, а секция var для объявления переменных.
Например:
type TValReal1 = Real;
var
// Переменные X и Y имеют
// один и тот же тип
X: Real; Y: TValReal1;
Е
type TValReal2 = type Real;
var
// Переменные X и Y имеют разные типы
X: Real; Y: TValReal2;
Множества
язык Object Pascal предоставляет два простых типа, описывающих множества значений. Это:
порядковый тип - задающий множество значений внутри указанного интервала;
перечислимый тип - задающий множество значений, перечисленных внутри фигурных скобок и через запятую.
Множество языка Object Pascal - это набор значений порядкового или перечислимого типа. Множество определяется ключевым словом set of.
Создание порядкового и перечислимого типов имеет следующее формальное описание:
Идентификатор = Начальное_значение ..
Конечное_значение;
Идентификатор = {Значение_i .,...};
Идентификатор = Значение_i..Значение_j;
Идентификатор = set of порядковый
или перечислимый тип.
Например:
type
{Объявление перечислимого типа}
TMyColor = {Red,Blue,Yellow};
{Объявление типа множество}
TMyClr = set of TMyColor;
var
{Объявление переменных созданных типов}
ValC1, ValC2: TMyClr;
begin
{Присвоение значения переменной
типа множество}
ValC1 := [Blue];
ValC2 := ValC1+[ Red];
end.
Начальным и конечным значением интервала для порядкового типа могут быть:
символы из кодовой таблицы ASCII (например, цифры или буквы);
любой диапазон значений ранее объявленного перечислимого типа.
Перед работой с переменной множественного типа ее следует проинициализировать. Значения инициализации указываются в квадратных скобках.
Например:
type TMyInts = 1..500;
TIntSet = set of TMyInts;
{Объявление переменной типа множества}
var set1: TIntSet;
begin
{Инициализация переменной
типа множества}
set1:=[1,2,3,4];
end.
Над множеством допустимы следующие операции:
объединение множеств (+);
разность (-);
умножение (*);
сравнение (<=, >=, =, <>);
принадлежность множеству (in).
Например:
type
{Порядковый тип: значения от 1 до 9}
CountType = 1..9;
LowerCharType = а..я;
AType = (A1, A2, A3, A4, A5);
{Порядковый тип: значения A3, A4 и A5}
AType3 = A3..A5;
{Множество на основе порядкового типа}
ATypeSet = set of AType3;
{Множество LetterType }
LetterType = set of 'A'..'Z;
var
{Объявление переменной типа множества}
aset: ATypeSet;
begin
{Присвоение значения переменной
множественного типа}
aset:= [A3] + [A4];
{Вычитание множеств}
aset:= aset - [A4];
end.
Каждая переменная множественного типа может иметь значение - множество. Для изменения значений переменной множественного типа могут использоваться операции объединения (+) и разности (-) множеств.
Например:
begin set1:=set1 +[5,6]; end.
Для определения принадлежности значения некоторому множеству используется операция in.
Например:
begin
if 5 in set1
then ShowMessage(
'Значение принадлежит множеству set1');
end.
Массивы
В языке Object Pascal можно создавать одномерные и многомерные массивы. Все элементы массива имеют одинаковый тип.
Одномерный массив языка Object Pascal имеет следующее формальное описание:
Идентификатор:
array [нач_индекс..кон_индекс]
of тип_массива;
Например:
type
RealArr = array[1..100] of Real;
var
int1: array[1..20] of Integer;
int2: array[1..20] of Integer;
int3, int4: array[1..10] of Integer;
real1: array[1..10] of Real;
begin
Int1 := Int2;
end.
Массивы могут объявляться как константы.
Например:
type
TCub = array[0..1, 0..1, 0..1]
of Integer;
const
cub1: TCube = (((0, 1), (2, 3)),
((4, 5), (6,7)));
{ Ёлементы массива cub1 будут
содержать следующие значения:
cub1 [0,0,0] = 0
cub1 [0,0,1] = 1
cub1 [0,1,0] = 2 и т.д.
}
Массив символов объявляется как массив переменных типа Char. Символьный массив может быть доступен через указатель на массив типа PChar.
Строки можно реализовывать как символьные массивы, ограниченные 0-символом (#0).
Например:
const
TempString: array[0..8] of Char =
'Строка'#0;
var
P: PChar; {Указатель на строку}
begin
P := 'Строка';
P := @TempString;
{Переменная P указывает на
значение 'Строка'}
end.
Массив символов типа Char и значение типа PChar эквивалентны.
Например:
var
MyArray: array[0..5] of Char;
MyPointer: PChar;
begin
MyArray := 'array';
MyPointer := MyArray;
MyProc(MyArray);
{Ёквивалентно вызову
MyProc(MyPointer);}
end.
Многомерный массив имеет следующее формальное описание:
array[диапазон_первого,индекса, ...,
диапазон_n_индекса] of базовый_тип;
array[диапазон] of array[диапазон]
of array[диапазон] of тип;
Так, объявление
type MyMassiv = array[1..10]
of array[1..70] of Integer;
эквивалентно объявлению
type MyMassiv = array[1..10, 1..70]
of Integer;
Например:
type MyAr = array[1..10, 1..50]
of Real;
begin
MyAr[1,49]:= 49;
end.
По принципу выделения памяти под массив массивы языка Object Pascal делятся на статические и динамические.
Память под статический массив выделяется при его создании.
Динамический массив не имеет фиксированного размера или длины.
При объявлении динамического массива память под него не выделяется. Выделение памяти происходит каждый раз при присвоении значения массиву или при вызове процедуры SetLength.
Создание динамического массива имеет следующее формальное описание:
array of тип_элементов_массива;
Например:
{Объявление динамического массива}
var MyAr: array of Integer;
{Определение количества элементов
в динамическом массиве:}
SetLength(MyAr, 10);
Элементы динамического массива всегда индексируются с 0. Переменная типа динамического массива является указателем, и к ней применимы операции над указателями. Для освобождения памяти, занимаемой динамическим массивом, можно присвоить значение nil переменной, ссылающейся на массив. Количество элементов динамического массива определяется процедурой SetLength. Функция Length возвращает количество элементов в динамическом массиве.
При сравнении двух переменных типа динамического массива выполняется сравнение их ссылок, а не значений их элементов. Например:
var
A, B: array of Integer;
begin
SetLength(A, 1); SetLength(B, 1);
A[0] := 2; B[0] := 2;
{A[0] = B[0] вернет значение True}
{A = B вернет значение False}
end.
Для усечения динамического массива можно использовать функцию Copy.
Например:
var
A: array of Integer;
begin
SetLength(A, 50);
{Оставляет 10 элементов массива A}
A := Copy(A, 0, 10);
end.
Если при объявлении функции или процедуры формальный параметр определяется как array of тип_массива, то эта функция или процедура может получить в качестве фактического параметра любой динамический или статический массив указанного типа вне зависимости от его размера и индексации его элементов.
Записи
Запись представляет собой структуру, состоящую из набора полей различных типов. Каждый элемент записи называется полем. Каждое поле имеет свой идентификатор и свой тип.
Запись может иметь поля двух видов:
фиксированные поля, которые всегда присутствуют в создаваемом экземпляре записи;
вариантные поля (располагаются только после фиксированных полей), для которых память при создании экземпляра записи выделяется только под одно самое наибольшее поле из варианта.
Объявление типа записи завершается ключевым словом end;.
Например:
type
MyDateRec = record
Year: Integer;
Month: 1..12;
Day: 1..31;
end;
var
{Объявление переменных}
Record1, Record2: MyDateRec;
begin
{Доступ к полям записи: }
Record1.Month := 11;
Record1.Day := 22;
{Доступ к полям записи:}
with Record1 do
begin
Year := 2004;
Month := 11;
Day := 22;
end;
{Копирование значений полей
записей одного типа }
Record2:= Record1;
Переменная типа записи может быть объявлена одновременно с определением полей записи.
Например:
var
S: record
Name: string;
Age: Integer;
end;
Запись, состоящая из одних вариантных полей, реализует тип, в некотором роде аналогичный типу union для языка С++.
Вариантные поля позволяют на основе одного типа записи создавать экземпляры записей, содержащие поля разных типов. Это позволяет значительно экономить память.
Объявление записи имеет следующее формальное описание:
type имя_типа_записи = record
список_полей: тип; {фиксированные поля}
...
case тег: тип_варианта
of {вариантные поля}
константа_или_список: (вариант_1);
...
константа_или_список: (вариант_n);
end;
Описание каждого фиксированного поля состоит из его идентификатора (или списка идентификаторов) и типа. Описания полей разделяются точкой с запятой. Вариантная часть объявления записи начинается с зарезервированного слова case.
При определении вариантной части тег и символ двоеточия могут быть опущены в том случае, если тип_варианта описывает существующий тип или каждая константа (константа_или_список) в вариантной части является значением типа тип_варианта.
Тип варианта не может быть длинной строкой, динамическим массивом, типом Variant или интерфейсом, но он может быть указателем на эти типы.
Например:
type
TPerson = record
FirstName, LastName: string[40];
case bl: Boolean
of {Вариантные поля}
True: (str1: string[40]);
False: (str2: string[20];
date1: TDate);
end;
TShapeList = (Rectangle, Circle,
Other, Ellipse);
TFigure = record
case TShapeList of
Rectangle: (Height, Width: Real);
Circle: (Radius: Real);
Ellipse, Other: ();
end;
Применяя вариантные поля, можно одинаково трактовать данные различных типов, используя для доступа к ним вариантные поля соответствующих типов.
Файлы
Файл представляет собой упорядоченный набор элементов одного типа.
Стандартные процедуры ввода и вывода используют предопределенный тип TextFile, или Text, который реализует файл как упорядоченный набор строк символов.
Объявление типа файл и создание переменных файлового типа имеет следующее формальное описание:
type тип_файла = file of тип;
var идентификатор: file of тип_файла;
Тип, указываемый после ключевых слов file of, может быть производным типом или базовым типом. Так, в качестве типа можно указать string[число_символов] или array[нач_инд..кон_инд] of Char.
Объявление нетипизированного файла имеет следующее формальное описание:
var идентификатор: file;
Для создания переменной файлового типа следует предварительно объявить файловый тип, а затем объявить переменную созданного типа, или сразу объявить переменную, указав используемый тип как файловый.
Например:
type
Phone = record Name: string[30];
PhoneNumber: string[20];
end;
{Объявление файлового типа }
PhoneList = file of Phone;
var
{Объявление переменной файлового типа}
book1: PhoneList;
{Объявление переменной с указанием типа
как файлового типа}
book2: file of Phone;
Лекция #10: С++. Классы. Механизмы наследования. Объектные типы Выполняется введение в терминологию объектно-ориентированных языков программирования. Дается понятие класса, конструктора и деструктора класса, вложенного класса. Вводится понятие объектного типа данных.
Терминология объектно-ориентированного программирования
Объектно-ориентированное программирование позволяет оперировать в терминах классов: определять классы, конструировать производные классы, создавать объекты, принадлежащие классу, - экземпляры класса.
Сначала в некоторых языках программирования появился тип struct, расширением которого стал тип class.
Класс определяет данные (переменные) и поведение (методы). Данные и методы класса также называют членами класса. Класс рассматривается как определяемый пользователем тип данных.
Объектом называется экземпляр некоторого класса. Объект создается как переменная типа класса, которая используется для доступа к данным - членам класса и для вызова методов - членов класса.
Наследованием называется механизм, позволяющий производному классу наследовать структуру данных и поведение другого класса, а также наследовать поведение, объявленное в интерфейсах и абстрактных классах.
Наследование позволяет определять новые классы в терминах существующих классов.
В объектно-ориентированном программировании наследование может быть:
множественным, позволяющим производному классу наследоваться одновременно от нескольких классов (например, так реализован механизм наследования в С++);
простым, когда производный класс имеет только один наследуемый класс (например, так реализованы языки Java и Object Pascal).
Наследуемый класс принято называть базовым классом, или родительским классом (классом - предком, суперклассом).
Производный класс, наследующий структуру данных и поведение своего базового класса, иногда также называется дочерним классом (классом - потомком, подклассом).
В производном классе можно переопределять методы базового класса и добавлять новые методы. Непосредственным базовым классом называется класс, от которого порожден производный класс следующего уровня иерархии:
А
Базовый класс класса С и непосредственный базовый класс класса B
⇓
B
Непосредственный базовый класс класса C
⇓
C
Производный класс
Полиморфизмом называется способность различных объектов по-разному обрабатывать одинаковые сообщения.
Инкапсуляция позволяет работать в терминах объектов и скрывать их переменные и методы. Использование инкапсуляции дает возможность модифицировать внутреннюю реализацию объекта без влияния на программу в целом до тех пор, пока не изменяется интерфейс с объектом.
В языках программирования инкапсуляция поддерживается реализацией модификаторов доступа, таких как protected - для защищенных членов класса на уровне класса, и private - для полностью защищенных членов класса.
Объектно-ориентированное программирование на языке С++
В настоящее время понятие языка программирования неотрывно связано со средой программирования, в которой разрабатываются приложения. Для языка С++ наиболее развитыми и популярными средами программирования являются:
Visual Studio.NET;
С++ Builder.
Среда программирования Visual Studio.NET предназначена для создания приложений не только на языке С++, но и на таких популярных языках, как C# и Visul Basic. Иногда для сокращения говорят, что проектирование приложений на C++ в Visual Studio.NET реализуется средой Visual C++.
Visual C++ позволяет разрабатывать приложения как в терминах традиционного модульного программирования, так и с применением объектно-ориентированного программирования.
Структура приложения на языке С++
Приложение состоит из модулей трансляции - файлов исходного кода на языке С++. Каждый модуль трансляции представляется файлом реализации класса и заголовочным файлом (компилятор позволяет записывать всю информацию - объявление и реализацию, в один файл, но это оправданно только для маленьких программ).
Проектом в терминологии Visual C++ называется совокупность всех модулей трансляции, файлов ресурсов и описания конфигурации.
Разработка любого приложения в Visual C++ начинается с создания проекта. Visual Studio.NET предоставляет шаблоны для создания различных видов приложений (консольные приложения, MFC-приложения, DLL-библиотеки, приложения управляемого кода и т.п.).
Консольное приложение - это приложение, не использующее Windows-окна для обработки сообщений от пользователя. Точкой входа в консольное приложение в языке С++ является метод main.
После того как создан шаблон приложения заданного вида, информация обо всех файлах проекта отображается в окне проектов среды Visual C++.
Заголовочный файл содержит объявления используемых данных. Язык C++ поддерживает соглашение о раздельной компиляции: каждый С++-модуль можно компилировать отдельно. Для того чтобы несколько модулей могли использовать одни и те же данные, объявление этих данных выносят в заголовочный файл. Принято, что имя заголовочного файла имеет расширение h.
Все подключаемые к модулю заголовочные файлы указываются в начале модуля директивой препроцессора #include.
Например:
#include "stdafx.h"#include <iostream>
Имя подключаемого файла в директиве препроцессора #include может быть указано:
в двойных кавычках - в этом случае препроцессор сначала выполняет поиск данного файла в том же каталоге, в котором расположен файл, содержащий директиву препроцессора #include, а затем в каталогах, определяемых опцией компиляции и переменной среды INCLUDE.
в угловых скобках - при этом исключается поиск имени файла в том же каталоге, в котором расположен файл, содержащий директиву препроцессора #include.
Файл, содержащий реализацию методов, объявленных в заголовочном файле, иногда называется исходным файлом.
Каждая программа должна иметь точку входа и может содержать описание одного или нескольких классов.
Точкой входа в программу в приложении, формируемом по шаблону в среде Visual C++, является метод _tmain, заменяемый при компиляции на метод main.
Минимальной программой на С++ является следующий код:
int main() {}
Эта программа определяет функцию с именем main, которая не использует параметров и не выполняет никаких действий.
Тело метода в языке С++ указывается в фигурных скобках. Перед именем метода указывается тип возвращаемого значения. Если метод не возвращает никакого значения, то его тип обозначается ключевым словом void.
Объявление и реализация класса в языке С++
Создаваемый класс должен быть объявлен и реализован.
Объявление класса в языке С++ может иметь следующее формальное описание:
class имя_класса : список_базовых_классов {public: // Модификатор доступа относится // ко всем перечисленным после // него членам до следующего // модификатора доступа // Объявление общедоступных членов класса protected: // Объявление членов класса, доступных // только для производных классов private: // Объявление защищенных членов класса};
Список базовых классов указывается после имени класса через символ двоеточия (:), разделяется запятыми и может иметь модификаторы доступа.
Например:
class MyClass : public ClassA, public ClassB, private ClassC {};
В языке С++ считается, что если модификатор доступа для класса или члена класса не указан, то по умолчанию предполагается модификатор доступа private (защищенный доступ). Для членов структур, объявляемых ключевым словом struct, по умолчанию модификатор доступа предполагается равным public.
Модификатор доступа базового класса позволяет определить, какие переменные и методы базового класса будут доступны из производного класса. Модификатор доступа, указываемый перед именем базового класса, определяет следующие правила доступа к переменным и методам базового класса из производного класса:
public - в производном классе доступны все переменные и методы базового класса с модификаторами доступа public и protected, и эти члены класса имеют те же права доступа;
protected - члены базового класса с модификаторами доступа public и protected доступны как protected, а с модификатором доступа private - недоступны.
private - члены базового класса с модификаторами доступа public и protected доступны как private, а с модификатором доступа private - недоступны.
Например:
class BaseClass{ public: int PublicFunc(); protected: int ProtectedFunc(); private: int PrivateFunc(); };class DerivedClass1 : public BaseClass {// Наследуемая функция PublicFunc доступна// как public// Наследуемая функция ProtectedFunc // доступна как protected };class DerivedClass2 : private BaseClass {// Наследуемая функция PublicFunc доступна // как private// Наследуемая функция ProtectedFunc // доступна как private };int main() { }
В теле объявления класса указываются модификаторы доступа, описывающие права доступа для переменных и методов класса:
модификатор доступа относится ко всем перечисленным после него членам до следующего модификатора доступа;
один и тот же модификатор доступа может указываться несколько раз;
после модификатора доступа ставится символ двоеточие;
если модификатор доступа не указан, то по умолчанию предполагается private.
Для доступа к членам класса используется операция принадлежности ::, указываемая после идентификатора класса. Для доступа к членам экземпляра класса используются операции . и ->.
Для доступа к объекту самого класса внутри метода члена класса используется ключевое слово this.
Например:
class A { public: int i; Func1();}A:: Func1() { return this->i; } // this - указатель класса A