- •Общие сведения Сведения об эумк
- •Методические рекомендации по изучению дисциплины
- •Рабочая учебная программа
- •Протокол согласования учебной программы по изучаемой учебной дисциплине с другими дисциплинами специальности
- •Пояснительная записка
- •Содержание дисциплины
- •1. Лабораторные занятия, их характеристика
- •2. Контрольные работы, их характеристика
- •3. Курсовые работы (проекты), их характеристика
- •4. Литература
- •4.1.Основная
- •4.2.Дополнительная
- •5. Перечень компьютерных программ, наглядных и других пособий, методических указаний и материалов и технических средств обучения
- •Тема 2. Классы. Объекты. Конструкторы и деструкторы. Методы.
- •Тема 3. Свойства. Методы получения и установки значений свойств. Свойства-массивы. Свойство-массив как основное свойство объекта. Методы, обслуживающие несколько свойств.
- •Тема 4. Наследование. Прародитель всех классов. Перекрытие атрибутов в наследниках. Совместимость объектов различных классов. Контроль и преобразование типов.
- •Тема 5. Виртуальные методы. Механизм вызова виртуальных методов. Абстрактные виртуальные методы. Динамические методы. Методы обработки сообщений.
- •Тема 6. Классы в программных модулях. Разграничение доступа к атрибутам объектов. Указатели на методы объектов
- •Тема 7. Метаклассы. Ссылки на классы. Методы классов. Виртуальные конструкторы. Информация о типе времени выполнения программы - rtti.
- •Тема 9. Защита от утечки ресурсов. Приемы надежного программирования
- •Тема 10. Интерфейс. Описание интерфейса. Расширение интерфейса. Глобально-уникальный идентификатор интерфейса.
- •Тема 12. Совместимость интерфейсов. Совместимость класса и интерфейса. Получение интерфейса через другой интерфейс
- •Тема 13. Подсчет ссылок. Механизм подсчета ссылок. Представление интерфейса в памяти. Применение интерфейса для доступа к объекту dll-библиотеки.
- •Тема 15. Перегрузка идентификаторов. Предопределенные аргументы в подпрограммах.
- •Тема 18. Множественное наследование. Проблема повторяющихся базовых классов. Типовой пример применения множественного наследования - observer.
- •Тема 19. Виртуальные методы. Абстрактые методы и классы. Подстановочные функции
- •Тема 21. Ссылки. Рекомендации по работе со ссылками. Типичные ошибки при работе со ссылками.
- •Тема 23. Перегрузка операторов. Перегрузка бинарных операторов. Перегрузка унарных операторов. Перегрузка операторов преобразования типа.
- •Тема 24. Шаблоны функций. Перегрузка шаблонов функций. Шаблоны классов. Специализации шаблонов. Создание новых типов данных на базе шаблонов
- •Тема 26. Перспективные технологии ооп.
- •Практический раздел Контрольные работы
- •Контрольная работа №1 Указания по выбору варианта
- •Теоретическая часть (вопросы)
- •Практическая часть Контрольное задание №1. Пример использования объектно-ориентированного программирования в языке Delphi
- •Исходные данные к контрольному заданию №1
- •Контрольная работа №2 Указания по выбору варианта
- •Теоретическая часть (вопросы)
- •Практическая часть
Тема 6. Классы в программных модулях. Разграничение доступа к атрибутам объектов. Указатели на методы объектов
Классы очень удобно собирать в модули. При этом их описание помещается в секцию interface, а код методов — в секцию implementation. Создавая модули классов, нужно придерживаться следующих правил:
все классы, предназначенные для использования за пределами модуля, следует определять в секции interface;
описание классов, предназначенных для употребления внутри модуля, следует располагать в секции implementation;
если модуль B использует модуль A, то в модуле B можно определять классы, порожденные от классов модуля A.
Соберем рассмотренные ранее классы TTextReader, TDelimitedReader и TFixedReader в отдельный модуль ReadersUnit:
unit ReadersUnit;
interface
type
TTextReader = class
private
// Поля
FFile: TextFile;
FItems: array of string;
FActive: Boolean;
// Методы
procedure PutItem(Index: Integer; const Item: string);
// Методы чтения и записи свойств
procedure SetActive(const AActive: Boolean);
function GetItemCount: Integer;
function GetEndOfFile: Boolean;
protected
// Методы чтения и записи свойств
function GetItem(Index: Integer): string;
// Абстрактные методы
function ParseLine(const Line: string): Integer; virtual; abstract;
public
// Конструкторы и деструкторы
constructor Create(const FileName: string);
destructor Destroy; override;
// Методы
function NextLine: Boolean;
// Свойства
property Active: Boolean read FActive write SetActive;
property Items[Index: Integer]: string read GetItem; default;
property ItemCount: Integer read GetItemCount;
property EndOfFile: Boolean read GetEndOfFile;
end;
TDelimitedReader = class(TTextReader)
private
// Поля
FDelimiter: Char;
protected
// Методы
function ParseLine(const Line: string): Integer; override;
public
// Конструкторы и деструкторы
constructor Create(const FileName: string; const ADelimiter: Char = ';');
// Свойства
property Delimiter: Char read FDelimiter;
end;
TFixedReader = class(TTextReader)
private
// Поля
FItemWidths: array of Integer;
protected
// Методы
function ParseLine(const Line: string): Integer; override;
public
// Конструкторы и деструкторы
constructor Create(const FileName: string;
const AItemWidths: array of Integer);
end;
TMyReader = class(TDelimitedReader)
property FirstName: string index 0 read GetItem;
property LastName: string index 1 read GetItem;
property Phone: string index 2 read GetItem;
end;
implementation
{ TTextReader }
constructor TTextReader.Create(const FileName: string);
begin
inherited Create;
AssignFile(FFile, FileName);
FActive := False;
end;
destructor TTextReader.Destroy;
begin
Active := False;
inherited;
end;
function TTextReader.GetEndOfFile: Boolean;
begin
Result := Eof(FFile);
end;
function TTextReader.GetItem(Index: Integer): string;
begin
Result := FItems[Index];
end;
function TTextReader.GetItemCount: Integer;
begin
Result := Length(FItems);
end;
function TTextReader.NextLine: Boolean;
var
S: string;
N: Integer;
begin
Result := not EndOfFile;
if Result then // Если не достигнут конец файла
begin
Readln(FFile, S); // Чтение очередной строки из файла
N := ParseLine(S); // Разбор считанной строки
if N <> ItemCount then
SetLength(FItems, N); // Отсечение массива (если необходимо)
end;
end;
procedure TTextReader.PutItem(Index: Integer; const Item: string);
begin
if Index > High(FItems) then // Если индекс выходит за границы массива,
SetLength(FItems, Index + 1); // то увеличение размера массива
FItems[Index] := Item; // Установка соответствующего элемента
end;
procedure TTextReader.SetActive(const AActive: Boolean);
begin
if Active <> AActive then // Если состояние изменяется
begin
if AActive then
Reset(FFile) // Открытие файла
else
CloseFile(FFile); // Закрытие файла
FActive := AActive; // Сохранение состояния в поле
end;
end;
{ TDelimitedReader }
constructor TDelimitedReader.Create(const FileName: string;
const ADelimiter: Char = ';');
begin
inherited Create(FileName);
FDelimiter := ADelimiter;
end;
function TDelimitedReader.ParseLine(const Line: string): Integer;
var
S: string;
P: Integer;
begin
S := Line;
Result := 0;
repeat
P := Pos(Delimiter, S); // Поиск разделителя
if P = 0 then // Если разделитель не найден, то считается, что
P := Length(S) + 1; // разделитель находится за последним символом
PutItem(Result, Copy(S, 1, P - 1)); // Установка элемента
Delete(S, 1, P); // Удаление элемента из строки
Result := Result + 1; // Переход к следующему элементу
until S = ''; // Пока в строке есть символы
end;
{ TFixedReader }
constructor TFixedReader.Create(const FileName: string;
const AItemWidths: array of Integer);
var
I: Integer;
begin
inherited Create(FileName);
// Копирование AItemWidths в FItemWidths
SetLength(FItemWidths, Length(AItemWidths));
for I := 0 to High(AItemWidths) do
FItemWidths[I] := AItemWidths[I];
end;
function TFixedReader.ParseLine(const Line: string): Integer;
var
I, P: Integer;
begin
P := 1;
for I := 0 to High(FItemWidths) do
begin
PutItem(I, Copy(Line, P, FItemWidths[I])); // Установка элемента
P := P + FItemWidths[I]; // Переход к следующему элементу
end;
Result := Length(FItemWidths); // Количество элементов постоянно
end;
end.
Как можно заметить, в описании классов присутствуют новые ключевые слова private, protected и public. С их помощью регулируется видимость частей класса для других модулей и основной программы. Назначение каждого ключевого слова поясняется ниже.
Разграничение доступа к атрибутам объектов
Программист может разграничить доступ к атрибутам своих объектов для других программистов (и себя самого) с помощью специальных ключевых слов: private, protected, public, published (последнее не используется в модуле ReadersUnit).
Private. Все, что объявлено в секции private недоступно за пределами модуля. Секция private позволяет скрыть те поля и методы, которые относятся к так называемым особеностям реализации. Например, в этой секции класса TTextReader объявлены поля FFile, FActive и FItems, а также методы PutItem, SetActive, GetItemCount и GetEndOfFile.
Public. Поля, методы и свойства, объявленные в секции public не имеют никаких ограничений на использование, т.е. всегда видны за пределами модуля. Все, что помещается в секцию public, служит для манипуляций с объектами и составляет программный интерфейс класса. Например, в классе TTextReader в эту секцию помещены конструктор Create, метод NextLine, свойства Active, Items, ItemCount.
Protected. Поля, методы и свойства, объявленные в секции protected, видны за пределами модуля только потомкам данного класса; остальным частям программы они не видны. Так же как и private, директива protected позволяет скрыть особенности реализации класса, но в отличие от нее разрешает другим программистам порождать новые классы и обращаться к полям, методам и свойствам, которые составляют так называемый интерфейс разработчика. В эту секцию обычно помещаются виртуальные методы. Примером такого метода является ParseLine.
Published. Устанавливает правила видимости те же, что и директива public. Особенность состоит в том, что для элементов, помещенных в секцию published, компилятор генерирует информацию о типах этих элементов. Эта информация доступна во время выполнения программы, что позволяет превращать объекты в компоненты визуальной среды разработки. Секцию published разрешено использовать только тогда, когда для самого класса или его предка включена директива компилятора $TYPEINFO.
Перечисленные секции могут чередоваться в объявлении класса в произвольном порядке, однако в пределах секции сначала следует описание полей, а потом методов и свойств. Если в определении класса нет ключевых слов private, protected, public и published, то для обычных классов всем полям, методам и свойствам приписывается атрибут видимости public, а для тех классов, которые порождены от классов библиотеки VCL, — атрибут видимости published.
Внутри модуля никакие ограничения на доступ к атрибутам классов, реализованных в этом же модуле, не действуют. Кстати, это отличается от соглашений, принятых в некоторых других языках программирования, в частности в языке C++.
Указатели на методы объектов
В языке Delphi существуют процедурные типы данных для методов объектов. Внешне объявление процедурного типа для метода отличается от обычного словосочетанием of object, записанным после прототипа процедуры или функции:
type
TReadLineEvent = procedure (Reader: TTextReader; const Line: string) of object;
Переменная такого типа называется указателем на метод (method pointer). Она занимает в памяти 8 байт и хранит одновременно ссылку на объект и адрес его метода.
type
TTextReader = class
private
FOnReadLine: TReadLineEvent;
...
public
property OnReadLine: TReadLineEvent read FOnReadLine write FOnReadLine;
end;
Методы объектов, объявленные по приведенному выше шаблону, становятся совместимы по типу со свойством OnReadLine.
type
TForm1 = class(TForm)
procedure HandleLine(Reader: TTextReader; const Line: string);
end;
var
Form1: TForm1;
Reader: TTextReader;
Если установить значение свойства OnReadLine:
Reader.OnReadLine := Form1.HandleLine;
и переписать метод NextLine,
function TTextReader.NextLine: Boolean;
var
S: string;
N: Integer;
begin
Result := not EndOfFile;
if Result then // Если строки для считывания еще есть, то
begin
Readln(FFile, S); // Считывание очередной строки
N := ParseLine(S); // Выделение элементов строки (разбор строки)
if N <> ItemCount then
SetLength(FItems, N);
if Assigned(FOnReadLine) then
FOnReadLine(Self, S); // уведомление о чтении очередной строки
end;
end;
то объект Form1 через метод HandleLine получит уведомление об очередной считанной строке. Обратите внимание, что вызов метода через указатель происходит лишь в том случае, если указатель не равен nil. Эта проверка выполняется с помощью стандартной функции Assigned, которая возвращает True, если ее аргумент является связанным указателем.
Описанный выше механизм называется делегированием, поскольку он позволяет передать часть работы другому объекту, например, сосредоточить в одном объекте обработку событий, возникающих в других объектах. Это избавляет программиста от необходимости порождать многочисленные классы-наследники и перекрывать в них виртуальные методы. Делегирование широко применяется в среде Delphi. Например, все компоненты делегируют обработку своих событий той форме, в которую они помещены.
