ТОП 10:

В объектно-ориентированных языках



В объектно-ориентированных языках класс является абстрактным типом данных.[Прим. 1] Полиморфизм реализуется с помощью наследования классов и виртуальных функций. Класс-потомок наследует сигнатуры методов класса-родителя, а реализация, в результате переопределения метода, этих методов может быть другой, соответствующей специфике класса-потомка. Другие функции могут работать с объектом как с экземпляром класса-родителя, но если при этом объект на самом деле является экземпляром класса-потомка, то во время исполнения будет вызван метод, переопределенный в классе-потомке. Это называется поздним связыванием. [Примером использования может служить обработка массива, содержащего экземпляры как класса-родителя, так и класса-потомка: очевидно, что такой массив может быть объявлен только как массив типа класса-родителя и у объектов массива могут вызываться только методы этого класса, но если в классе-потомке какие-то методы были переопределены, то в режиме исполнения для экземпляров этого класса будут вызваны именно они, а не методы класса-родителя.]

Класс-потомок сам может быть родителем. Это позволяет строить сложные схемы наследования — древовидные или сетевидные.

Абстрактные (или чисто виртуальные) методы не имеют реализации вообще (на самом деле некоторые языки, например C++, допускают реализацию абстрактных методов в родительском классе). Они специально предназначены для наследования. Их реализация должна быть определена в классах-потомках.

Класс может наследовать функциональность от нескольких классов. Это называется множественным наследованием. Множественное наследование создаёт известную проблему (в C++), когда класс наследуется от нескольких классов-посредников, которые в свою очередь наследуются от одного класса (так называемая «Проблема ромба»): если метод общего предка был переопределён в посредниках, неизвестно, какую реализацию метода должен наследовать общий потомок. Решается эта проблема путём отказа от множественного наследования для классов и разрешением множественного наследования для полностью абстрактных классов (то есть интерфейсов) (C#, Delphi,Java), либо через виртуальное наследование (C++).

Динамическое создание объектов на форме с помощью классов.

Статья посвящена динамическому созданию обычных и визуальных объектов, а также наследованию объктов

Задача формулируется следующим образом- дан целочисленный массив содержимого формы:0- combobox,1-spinedit,2-edit,3 и 4-panel,необходимо создать эти различные объекты на форме или на панелях, для удобства доступа к ним. Естественно предложить решение без динамического создания- размещаем объекты на форме, устанавливаем единый обработчик события ( так как формат обработчиков одинаков-один параметр Sender:TObject, то такое возможно), а в свойстве Tag объектов храним его id(не номер типа а номер объекта в массиве), для каждого значения tag исполняем то же код. Однако же если массив двумерный как в редакторе кампаний hi, то сложно использовать этот метод, а если формат размещен в файле, базе данных или присваивается при выполении программы то без динамических объектов не обойтись.

Пример использовался для написания редактора кампаний, когда разные свойства требовали разных объектов, например количество сценариев требовало наличие spinedit,для названия кампании нужен edit,а для имен файлов карт panel, и не простая, а чтобы при клике вызывался opendialog.

Как это делается:

Во первый объявляем саму структуру объектов - у нас будет двумерный массив объектов, двумерный массив подписей к нему, одномерный массив панелей для объектов и также хранится массив количества свойств(для двумерных массивов, например свойств кампании всего 2, а свойств сценариев уже 5).

tcampa_struct=class num:array[0..2] of array of integer; p:array[0..2]of TPanel; l:array[0..2]of array of TLabel; wc:array[0..2] of array of TWinControl; constructor Create(Sender:TObject); destructor Free; end;

Теперь опишем как создавать объекты. Сначала обратим внимание на TwinControl -это наибольший предок всех,используемых мною, объектов. Для любого объекта в delphi необходимо сделать такое присвоение: object:=TObject.Create(); , то есть создать экземляр класса, далее для визуальных объектов необходимо присвоить родителя object.parent:=hisparent; после этого объект создастся со свойствами по умолчанию, а затем нужно присвоить часть свойств нужным вам значениям. Я изменяю размер и расположение объектов. В зависимости от типа объекта конструкторы разные и вызываются дочерние: wc[i,j]:=TComboBox.Create(p[i]);

constructor tcampa_struct.Create(Sender:TObject);var i,j:integer;beginfor i:=0 to 2 do begin setlength(num[i],campa_structC[i]); setlength(l[i],campa_structC[i]); setlength(wc[i],campa_structC[i]); end; num[0,0]:=2;num[0,1]:=1;num[1,0]:=2;num[1,1]:=3;num[1,2]:=4;num[1,3]:=1;num[1,4]:=1;num[2,0]:=0;num[2,1]:=0;num[2,2]:=1;num[2,3]:=0;num[2,4]:=1; for i:=0 to 2 do begin p[i]:=TPanel.Create(TComponent(Sender)); p[i].Parent:=TWinControl(Sender); p[i].Left:=500; p[i].Width:=340; p[i].Height:=30*campa_structC[i]; end;p[0].Top:=60;p[1].Top:=130;p[2].Top:=300; for i:=0 to 2 do for j:=0 to campa_structC[i]-1 do begin l[i,j]:=TLabel.Create(p[i]); l[i,j].Parent:=p[i]; l[i,j].Left:=2; l[i,j].Top:=j*30+2; l[i,j].Height:=28; l[i,j].Width:=166; end; for i:=0 to 2 do for j:=0 to campa_structC[i]-1 do begin case num[i,j] of 0:wc[i,j]:=TComboBox.Create(p[i]); 1:wc[i,j]:=TSpinEdit.Create(p[i]); 2:wc[i,j]:=TEdit.Create(p[i]); 3:wc[i,j]:=TPanel.Create(p[i]); 4:wc[i,j]:=TPanel.Create(p[i]); end; wc[i,j].Parent:=p[i]; wc[i,j].Left:=172; wc[i,j].Top:=j*30; wc[i,j].Height:=25; wc[i,j].Width:=166; end; l[0,0].Caption:='Название';l[0,1].Caption:='Количество сценариев'; l[1,0].Caption:='Имя сценария';l[1,1].Caption:='Имя файла';l[1,2].Caption:='Имя ИИ файла';l[1,3].Caption:='Главный игрок';l[1,4].Caption:='Количество бонусов'; l[2,0].Caption:='Номер';l[2,1].Caption:='Уточненный номер';l[2,2].Caption:='Значение';l[2,3].Caption:='Тип адресата';l[2,4].Caption:='Номер адресата';end;

Все создано кроме небольших оговорок необходимо присвоить события и некоторые начальные значение, скажем номер игрока не должен выходить за пределы 1 до 8. События для объектов должны быть тоже объявлены в форме. Вот я вызываю при клике на панель раличные opendialog и пишу имя файла в название.

procedure TFormC1.him_click(Sender:TObject);beginif OpenDialogm.Execute then TPanel(Sender).Caption:=OpenDialogm.FileName;end; procedure TFormC1.hiai_click(Sender:TObject);beginif OpenDialogAi.Execute then TPanel(Sender).Caption:=OpenDialogAI.FileName;end;

При закрытии формы не забываем вызвать деструктор.

destructor tcampa_struct.free();var i,j:integer;beginfor i:=0 to 2 do for j:=0 to campa_structC[i]-1 do begin l[i,j].Free; wc[i,j].Free; end;for i:=0 to 2 do p[i].Free;end;

Данная процедура в дополнении к конструктору. Обратим внимание на приведение к дочерним типам TcomboBox(Campa_struct.wc[2,3]).

procedure TFormC1.CS_init;var i,j:integer;beginTPanel(Campa_struct.wc[1,1]).OnClick:=him_click;TPanel(Campa_struct.wc[1,2]).OnClick:=hiai_click;TSpinEdit(Campa_struct.wc[1,3]).Value:=1;TSpinEdit(Campa_struct.wc[1,3]).MinValue:=1;TSpinEdit(Campa_struct.wc[1,3]).MaxValue:=8;ReadFiles; TComboBox(Campa_struct.wc[2,3]).Clear;for i:=0 to 8 do TComboBox(Campa_struct.wc[2,3]).Items.Add(campa_actors[i]);TComboBox(Campa_struct.wc[2,3]).OnChange:=Campa_change_actor;TComboBox(Campa_struct.wc[2,0]).OnChange:=Campa_change_actor2;end;

Классы

Для поддержки ООП в язык Delphi введены объектные типы данных, с помощью которых одновременно описываются данные и операции над ними. Объектные типы данных называют классами, а их экземпляры — объектами.

Классы объектов определяются в секции type глобального блока. Описание класса начинается с ключевого слова class и заканчивается ключевым словом end. По форме объявления классы похожи на обычные записи, но помимо полей данных могут содержать объявления пользовательских процедур и функций. Такие процедуры и функции обобщенно называют методами, они предназначены для выполнения над объектами различных операций. Приведем пример объявления класса, который предназначен для чтения текстового файла в формате "delimited text" (файл в таком формате представляет собой последовательность строк; каждая строка состоит из значений, которые отделены друг от друга символом-разделителем):

type TDelimitedReader = class // Поля FileVar: TextFile; Items: array of string; Delimiter: Char; // Методы procedure PutItem(Index: Integer; const Item: string); procedure SetActive(const AActive: Boolean); function ParseLine(const Line: string): Integer; function NextLine: Boolean; function GetEndOfFile: Boolean; end;

Класс содержит поля (FileVar, Items, Delimiter) и методы (PutItem, SetActive, ParseLine, NextLine, GetEndOfFile). Заголовки методов, (всегда) следующие за списком полей, играют роль упреждающих (forward) описаний. Программный код методов пишется отдельно от определения класса и будет приведен позже.

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

Класс содержит несколько полей:

  • FileVar — файловая переменная, необходимая для доступа к файлу;
  • Delimiter — символ, который служит разделителем элементов;
  • Items — массив элементов, полученных разбором последней считанной строки;

Класс также содержит ряд методов (процедур и функций):

  • PutItem — помещает элемент в массив Items по индексу Index; если индекс превышает верхнюю границу массива, то размер массива автоматически увеличивается;
  • SetActive — открывает или закрывает файл, из которого производится чтение строк;
  • ParseLine — осуществляет разбор строки: выделяет элементы из строки и помещает их в массив Items; возвращает количество выделенных элементов;
  • NextLine — считывает очередную строку из файла и с помощью метода ParseLine осуществляет ее разбор; в случае успешного чтения очередной строки функция возвращает значение True, а иначе — значение False (достигнут конец файла);
  • GetEndOfFile — возвращает булевское значение, показывающее, достигнут ли конец файла.

Обратите внимание, что приведенное выше описание является ничем иным, как декларацией интерфейса для работы с объектами класса TDelimitedReader. Реализация методов PutItem, SetActive, ParseLine, NextLine и GetEndOfFile на данный момент отсутствует, однако для создания и использования экземпляров класса она пока и не нужна.

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

StringGrid.

Компонент StringGrid находится на странице Additionalпалитры компонентов. Там находятся "дополнительные" компоненты, но StringGrid Delphi, на мой взгляд, достоин большего уважения, лично я разместил бы его на странице Standart! StringGrid - компонент для отображения различных данных в табличной форме. Как следует из названия, ячейки компонента StringGrid Delphi могут содержать данные, имеющие тип String, а также отображать графику.  

Таблица StringGrid состоит из выделенных серым FixedCols и FixedRows - зафиксированных ячеек-заголовков, и обычных, белых ячеек. Содержимое Fixed ячеек недоступно редактированию, и меняется только программно. За возможность редактирования обычных ячеек отвечает одно из значений свойства Options.

Итак, компонент StringGrid имеет возможность адресации каждой отдельной ячейки по номеру столбца и строки. Содержимое ячейки (i, j), где где i - номер столбца, j - номер строки, имеет вид

StringGrid1.Cells[i, j]

и доступно как для чтения, так и для записи. Здесь, как и всегда, номера столбцов ( i ) и строк ( j ) отсчитываются от 0.

Выделенная ячейка таблицы имеет

номер столбца: StringGrid1.Col
номер строки: StringGrid1.Row

поэтому содержимое выделенной ячейки будет адресоваться так:

S:=StringGrid1.Cells[StringGrid1.Col, StringGrid1.Row];

Не правда ли, написание такой строки - утомительный процесс. Поэтому пользуйтесь оператором присоединения with:

with StringGrid1 do
S:=Cells[Col, Row];


А лучше сразу задать в свойстве Name имя покороче, например SG.

За многие свойства компонента Delphi StringGrid отвечает свойство Options. В Инспекторе Объектов Options - это раскрывающийся список, представляющий собой элементы данногомножества. Если значение элемента равно True, то он присутствует в множестве, если False - то нет.

 

 
Свойство Значение
goFixedVertLine Наличие вертикальных разделительных линий между "фиксированными" ячейками
goFixedHorzLine Наличие горизонтальных разделительных линий между "фиксированными" ячейками
goVertLine Наличие вертикальных разделительных линий между "обычными" ячейками
goHorzLine Наличие горизонтальных разделительных линий между "обычными" ячейками
goRangeSelect Возможность выделить диапазон ячеек
goDrawFocusSelected Закрашивание ячейки с фокусом ввода
goRowSizing Возможность менять высоту строк мышкой
goColSizing Возможность менять ширину столбцов мышкой
goRowMoving Возможность менять номер строки, то есть перемещать её, мышкой
goColMoving Возможность менять номер столбца, то есть перемещать его, мышкой
goEditing Возможность редактировать содержимое ячейки с клавиатуры
goTabs При значении True фокус смещается на следующую ячейку в таблице,False - на следующий компонент
goRowSelect Выделяется вся строка с "фокусированной" ячейкой
goAlwaysShowEditor При значении True содержимое ячейки при получении фокуса сразу доступно редактированию, False - сначала необходимо щёлкнуть по ней мышкой, либо нажать Enter или F2 (прим.: не действует приgoRowSelect=True)
goThumbTracking При значении True перемещение "бегунка" прокрутки мышкой вызывает немедленное перемещение ячеек,False - ячейки перемещаются только при отпускании "бегунка"


Как следует из таблицы, за возможность редактировать содержимое ячеек с клавиатуры отвечает элемент goEditing свойства-множества Options. В Инспекторе Объектов установите его значение вTrue. Чтобы управлять этой возможностью программно, нужно включить или исключить из множества данный элемент:

StringGrid1.Options:=StringGrid1.Options+[goEditing]; //Включаем редактирование, другие элементы не трогаем

StringGrid1.Options:=StringGrid1.Options-[goEditing]; //Выключаем редактирование, другие элементы не трогаем

StringGrid1.Options:=[goEditing, goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine, goRowSelect]; //Задаём список необходимых элементов

Если элементы заданы списком, это аналогично присвоению в Инспекторе Объектов этим элементам значения True, остальным - False.

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

Количество строк в Delphi StringGrid равно StringGrid1.RowCount.
Количество столбцов в Delphi StringGrid равно StringGrid1.ColCount.

Если ячейки не помещаются в таблице, появляются полосы прокрутки. При прокручивании

 

StringGrid1.LeftCol Номер столбца, видимого самым левым
StringGrid1.TopRow Номер строки, видимой самой верхней
StringGrid1.VisibleColCount Количество столбцов, видимых в рамках таблицы
StringGrid1.VisibleRowCount Количество строк, видимых в рамках таблицы


У таблицы StringGrid также есть свойство и для управления размером ячеек.Для всех ячеек

 

DefaultRowHeight - высота строк по умолчанию
DefaultColWidth - ширина столбцов по умолчанию


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

 

RowHeights[i] - массив, содержащий высоты строк с номером i
ColWidths[i] - массив, содержащий ширины столбцов с номером i


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

Отдельно требуется осветить вопрос очистки содержимого таблицы StringGrid. Так как таблицаStringGrid, в отличие от, например, компонента Memo, не имеет метода для очистки содержимого сразу всех ячеек, то для удаления внесённых в таблицу ранее данных приходится очищать каждую ячейку отдельно. Делается это двумя вложенными циклами for, пробегающими по столбцам и строкам:

var i, j: Integer;
begin
with StringGRid1 do
for i:=1 to RowCount-1 do //Заголовки строк не трогаем
for j:=1 to ColCount-1 do //Заголовки столбцов не трогаем
Cells[j, i]:='';
end;

Хотя, оказывается, есть метод для очищения содержимого целого столбца или строки:

StringGrid1.Cols[i].Clear; //Очищается столбец с номером i
StringGrid1.Rows[i].Clear; //Очищается строка с номером i

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

var i, j: Integer;
begin
with StringGRid1 do
for i:=1 to RowCount-1 do //Заголовки столбцов не трогаем - цикл от 1
begin
Rows[i].Clear;
Cells[0, i]:="Заголовок строки i";
end;
end;

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

Компонент StringGrid умеет не хранить в своих ячейках не только текстовую информацию, но и графику. Графические возможности StringGrid определяются наличием у таблицы свойстваCanvas - холста, на котором можно воспроизводить любую графику стандартными методами Delphi. Кроме того, компонент StringGrid имеет дополнительные методы, помогающие выводу графики в ячейки компонента. Графическими методами в таблице StringGrid можно, например, выводить текст в ячейке не только в одну, но и в несколько строк, произвольно раскрашивать ячейки, размещать рисунки и т.д.

Работа с графическими свойствами компонента StringGrid происходит в обработчике OnDrawCell. По событию OnDrawCell происходит перерисовка таблицы, и следовательно, код в обработчике этого события будет управлять выводом на холст таблицы необходимой графики. Переменные этого обработчика помогут определить и прямоугольник, в котором будет происходить вывод графики:

procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
Rect: TRect; State: TGridDrawState);
begin
//ACol - индекс столбца
//ARow - индекс строки
//Rect - прямоугольник вывода, заданный ячейкой (ACol, ARow)
end;

Для начала давайте выведем в ячейку (1, 1) компонента StringbGrid какой-нибудь рисунок. Вывести рисунок в ячейку компонента StringGrid проще всего, предварительно загрузив его в компонент Image:

Image1.Picture.LoadFromFile('Имя_файла');

Загрузить рисунок в компонент Image можно, конечно, уже на этапе проектирования, в Инспекторе Объектов, вызвав графический редактор нажатием кнопочки в свойстве Picture.

Затем нужно определить размеры загруженного рисунка:

W:=Image1.Picture.Width;
H:=Image1.Picture.Height;

Далее, готовим ячейку под размещение рисунка. Для этого нужно задать её размеры кратными размерам рисунка. Например, сделаем размеры ячейки в 10 раз меньше размеров рисунка:

StringGrid1.ColWidths[1]:=Round(W/10);
StringGrid1.RowHeight[1]:=Round(H/10);

Все эти манипуляции делаем предварительно, в обработчике OnCreate Формы, например. Ну и, наконец, в обработчике OnDrawCell выводим рисунок:

procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
Rect: TRect; State: TGridDrawState);
begin
if ACol*ARow=1 then //Условие ACol*ARow=1 тождественно (ACol=1)and(ARow=1)
StringGrid1.Canvas.StretchDraw(Rect, Image1.Picture.Graphic);
end;

Таким образом, при перерисовке таблица просматривает все ячейки и, встретив комбинацию ACol=1 и ARow=1, выводит в эту ячейку рисунок.

Таким же способом можно и раскрасить заданные по любому условию ячейки в нужные цвета. Например, раскрасим ячейки с положительными числами в зелёный цвет, с отрицательными - в красный, с равными нулю в синий:

procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
Rect: TRect; State: TGridDrawState);
var X: Real;
begin
with StringGrid1 do
begin
try
X:=StrToFloat(Cells[ACol, ARow]);
if X>0 then Canvas.Brush.Color:=clGreen;
if X<0 then Canvas.Brush.Color:=clRed;
if X=0 then Canvas.Brush.Color:=clBlue;
except
end;
Canvas.FillRect(Rect); //Текст тоже будет закрашен, его нужно перерисовать:
Canvas.TextOut(Rect.Left+2, Rect.Top+2, Cells[ACol, ARow]);
end;
end;

Осталось рассмотреть возможности компонента StringGrid по выводу текста в ячейку таблицы в несколько строк. Если строки для вывода уже подготовлены, достаточно просто выводить строки со сдвигом по вертикали на высоту строки. Высота ячейки таблицы должна позволять вывод нескольких строк текста. Например, заголовок первого столбца:

//В обработчике OnCreate Формы подготавливаем высоту строки:
StringGrid1.RowHeight[0]:=(StringGrid1.Canvas.TexHeight('A')+2)*N; //N - количество строк
//теперь в обработчике OnDrawCell выводим текст:
if (ACol=1) and (ARow=0) then
begin
Canvas.TextOut(Rect.Left+2, Rect.Top+2, 'Многострочный заголовок');
Canvas.TextOut(Rect.Left+2, Canvas.TextHeight('A')+Rect.Top+2, 'Вторая строка');
Canvas.TextOut(Rect.Left+2, Canvas.TextHeight('A')*2+Rect.Top+2, 'Третья строка');
end;

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

     

17. Процедуры работы с памятью. Приведите примеры.







Последнее изменение этой страницы: 2016-04-08; Нарушение авторского права страницы

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.229.118.253 (0.015 с.)