- •1. Понятие модуля. Принципы модульного программирования. Понятие объекта как динамического модуля.
- •2. Понятие класса. Понятие метода. Представление метода в виде обычной процедуры. Понятие конструктора и деструктора.
- •3. Понятие свойства. Методы получения и установки значений свойств. Свойства-массивы (в некоторых языках программирования). Индексаторы (в некоторых языках программирования).
- •Информация о типе времени выполнения программы
- •5. Классы в программных модулях. Атрибуты доступа к элементам объектов. Термин «инкапсуляция».
- •Термин «инкапсуляция»
- •Virtual – в базовом классе
- •7. Понятие ссылки на метод объекта (или делегата – в зависимости от языка программирования). Понятие события. Применение ссылок на методы для расширения объектов.
- •8. Понятие метакласса (в некоторых языках программирования). Методы, применяемые к классам. Виртуальные конструкторы (в некоторых языках).
- •Понятие метакласса (в некоторых языках программирования)
- •Виртуальные конструкторы (в некоторых языках)
- •3) Finally – вызвать free.
- •Если глобально-уникальный идентификатор назначается интерфейсу, то он записывается после ключевого слова interface и заключается в квадратные скобки, например:
- •Microsoft Visual Studio
- •Import Network;
- •Var Stat: SocketStat;
- •Var SocketStatCollection: … ;
- •Конструкторы и деструкторы
- •Стандартные конструкторы
- •Создание объектов по значению (на стеке) и по ссылке (в динамической памяти)
- •Операторы new и delete
- •Размещающий оператор new
- •Порядок конструирования и разрушения объектов
- •Вложенные определения классов
- •«Друзья» класса
- •Статические члены класса
2. Понятие класса. Понятие метода. Представление метода в виде обычной процедуры. Понятие конструктора и деструктора.
Понятие класса
Класс – тип данных для создания объектов.
Объект – экземпляр класса.
Классы объектов определяются в секции type глобального блока. Описание класса начинается с ключевого слова class и заканчивается ключевым словом end. По форме похоже на обычные записи, но помимо полей данных классы могут содержать объявления пользовательских процедур и функций.
Type
TDelimitedReader = class
// поля
FileVar : TextFile;
Items : array of string;
//заголовки методов
procedure PutItem(Index: Integer; constItem: string);
function GetEndOfFile: Boolean;
procedure SetActive(const _Active: Boolean);
end;
Класс содержит поля и методы. Заголовки методов, следующие за списком полей, играют роль упреждающих (forward) описаний.
Класс обычно описывает сущность, моделируемую в программе.
Объявление ссылки на объект класса:
var Reader: TDelimitedReader;
Объекты в Delphi – динамические данные, т.е. распределяются в динамической памяти. Поэтому переменная Reader — это просто ссылка на экземпляр (объект в памяти), которого физически еще не существует.
Объекты могут выступать в программе не только в качестве переменных, но также элементов массивов, полей записей, параметров процедур и функций. Кроме того, они могут служить полями других объектов. Во всех этих случаях программист фактически оперирует указателями на экземпляры объектов в динамической памяти. Следовательно, объекты изначально приспособлены для создания сложных динамических структур данных, таких как списки и деревья. Указатели на объекты для этого не нужны.
В некоторых случаях требуется, чтобы объекты разных классов содержали ссылки друг на друга. Возникает проблема: объявление первого класса будет содержать ссылку на еще не определенный класс. Она решается с помощью упреждающего объявления:
type
TReadersList = class; // упреждающее объявление классаTReadersList
TDelimitedReader = class
Owner: TReadersList;
...
end;
TReadersList = class
Readers: array ofTDelimitedReader;
...
end;
Классы удобно помещать в модули: описание помещается в секцию interface, а код методов — в секцию implementation. Создавая модули классов, нужно придерживаться следующих правил:
– все классы, предназначенные для использования за пределами модуля, следует определять в секции interface;
– описание классов, предназначенных для употребления внутри модуля, следует располагать в секции implementation;
– если модуль B использует модуль A, то в модуле B можно определять классы, порожденные от классов модуля A.
Понятие метода
Метод – процедура или функция, определенная внутри класса, предназначенная для выполнения действий над объектами. Предварительное объявление методов выполняется при описании класса в секции interface модуля, а их программный код записывается в секции implementation. Однако в отличие от обычных процедур и функций заголовки методов должны иметь уточненные имена, т.е. содержать наименование класса.
procedure TDelimitedReader.SetActive(const _Active: Boolean);
begin
if _Active then
Reset(FileVar)
else
CloseFile(FileVar);
end;
Внутри методов обращения к полям и другим методам выполняются как к обычным переменным и подпрограммам без уточнения экземпляра объекта. Такое упрощение достигается путем использования в пределах метода псевдопеременной Self (стандартный идентификатор). Физически Self – дополнительный неявный параметр, передаваемый в метод при вызове. Он указывает экземпляр объекта, к которому данный метод применяется.
Представление метода в виде обычной процедуры
Reader.SetActive(True) (ООП-вариант) = TDelimitedReader_SetActive(Reader, True) (вариант процедурного программирования)
Любой метод можно представить в виде обычной процедуры, которая получает неявный аргумент this (self).
Понятие конструктора и деструктора
Когда начинается работа с объектом, то он размещается в памяти.
Чтобы сконструировать объект (выделить память для экземпляра):
Reader := TDelimitedReader.Create;
Create — это конструктор объекта; он всегда присутствует в классе и служит для создания и инициализации экземпляров. При создании объекта в памяти выделяется место только для его полей. Методы, как и обычные процедуры и функции, помещаются в область кода программы; они умеют работать с любыми экземплярами своего класса и не дублируются в памяти.
После создания объект можно использовать в программе: получать и устанавливать значения его полей, вызывать его методы. Доступ к полям и методам объекта происходит с помощью уточненных имен, например:
Reader.NextLine;
Кроме того, как и при работе с записями, допустимо использование оператора with, например:
with Reader do NextLine;
Если объект становится ненужным, он должен быть удален вызовом специального метода Destroy, например:
Reader.Destroy; // Освобождение памяти, занимаемой объектом
Destroy – это деструктор объекта; он присутствует в классе наряду с конструктором и служит для удаления объекта из динамической памяти. После вызова деструктора переменная Reader становится несвязанной и не должна использоваться для доступа к полям и методам уже несуществующего объекта. Чтобы отличать в программе связанные объектные переменные от несвязанных, последние следует инициализировать значением nil.
Reader := nil;
...
if Reader <> nil then
Reader.Destroy;
Вызов деструктора для несуществующих объектов недопустим и при выполнении программы приведет к ошибке. Чтобы избавить программистов от лишних ошибок, в объекты ввели предопределенный метод Free, который следует вызывать вместо деструктора. Метод Free сам вызывает деструктор Destroy, но только в том случае, если значение объектной переменной не равно nil.
if Reader <> nil then ~ Reader.Free
Reader.Destroy
После уничтожения объекта переменная Reader сохраняет свое значение, продолжая ссылаться на место в памяти, где объекта уже нет. Если эту переменную предполагается еще использовать, то желательно присвоить ей значение nil, чтобы программа могла проверить, существует объект или нет.
Reader.Free; ~ FreeAndNil(Reader);
Reader := nil;
Значение одной объектной переменной можно присвоить другой. При этом объект не копируется в памяти, а вторая переменная просто связывается с тем же объектом, что и первая:
var
R1, R2: TDelimitedReader; // Переменные R1 и R2 не связаны с объектом
begin
R1 := TDelimitedReader.Create; // Связывание переменной R1 с новым объектом
// Переменная R2 пока еще не связана ни с каким объектом
R2 := R1; // Связывание переменной R2 с тем же объектом, что и R1
// Теперь обе переменные связаны с одним объектом
R2.Free; // Уничтожение объекта
// Теперь R1 иR2 не связаны ни с каким объектом
end;
Создание объекта включает выделение памяти под экземпляр и инициализацию его полей, а разрушение – очистку полей и освобождение памяти. Действия по инициализации и очистке полей специфичны для каждого конкретного класса объектов. По этой причине Delphi позволяет переопределить стандартный конструктор Create и стандартный деструктор Destroy для выполнения любых полезных действий. Можно даже определить несколько конструкторов и деструкторов (имена им назначает сам программист), чтобы обеспечить различные процедуры создания и разрушения объектов.
type
TDelimitedReader = class
...
Constructor Create(const FileName: string; const ADelimiter: Char = ';');
Destructor Destroy; override;
...
end;
constructor TDelimitedReader.Create( const FileName: string;
const ADelimiter: Char = ';');
begin
AssignFile(FileVar, FileName);
Delimiter := ADelimiter;
end;
destructor TDelimitedReader.Destroy;
begin
…
end;
Если объект содержит встроенные объекты или другие динамические данные, то конструктор – это как раз то место, где их нужно создавать.
Конструктор применяется к классу или к объекту.
Если он применяется к классу,
Reader := TDelimitedReader.Create('MyData.del', ';');
то выполняется следующая последовательность действий:
1) в динамической памяти выделяется место для нового объекта;
2) выделенная память заполняется нулями. В результате все числовые поля и поля порядкового типа приобретают нулевые значения, строковые поля становятся пустыми, а поля, содержащие указатели и объекты получают значение nil;
3) затем выполняются заданные программистом действия конструктора;
4) ссылка на созданный объект возвращается в качестве значения конструктора. Тип возвращаемого значения совпадает с типом класса, использованного при вызове.
Если конструктор применяется к объекту,
Reader.Create('MyData.del', ';');
то конструктор выполняется как обычный метод. Другими словами, новый объект не создается, а происходит повторная инициализация полей существующего объекта. В этом случае конструктор не возвращает никакого значения. Далеко не все объекты корректно себя ведут при повторной инициализации, поскольку программисты редко закладывают такую возможность в свои классы. Поэтому на практике повторная инициализация применяется крайне редко.
Деструктор уничтожает объект, к которому применяется:
Reader.Destroy;
В результате:
1) выполняется заданный программистом код завершения;
2) освобождается занимаемая объектом динамическая память.
В теле деструктора обычно должны уничтожаться встроенные объекты и динамические данные, как правило, созданные конструктором. Как и обычные методы, деструктор может иметь параметры, но эта возможность используется редко.
Директива Override должна быть указана в определении деструктора, так как мы отменяем виртуальный метод уничтожения TObject.
В конце деструктора, вы должны вызвать Inherited для вызова родительского деструктора.
Пример, что деструктор явно можно не вызывать:
procedure TForm1.FormCreate(Sender:Object);
var words : TWords; begin words := TWords.Create(4); end;
