Студопедия

КАТЕГОРИИ:

АвтоАвтоматизацияАрхитектураАстрономияАудитБиологияБухгалтерияВоенное делоГенетикаГеографияГеологияГосударствоДомЖурналистика и СМИИзобретательствоИностранные языкиИнформатикаИскусствоИсторияКомпьютерыКулинарияКультураЛексикологияЛитератураЛогикаМаркетингМатематикаМашиностроениеМедицинаМенеджментМеталлы и СваркаМеханикаМузыкаНаселениеОбразованиеОхрана безопасности жизниОхрана ТрудаПедагогикаПолитикаПравоПриборостроениеПрограммированиеПроизводствоПромышленностьПсихологияРадиоРегилияСвязьСоциологияСпортСтандартизацияСтроительствоТехнологииТорговляТуризмФизикаФизиологияФилософияФинансыХимияХозяйствоЦеннообразованиеЧерчениеЭкологияЭконометрикаЭкономикаЭлектроникаЮриспунденкция

Структура класса и видимость




Для объявления класса используют ключевое слово class. При этом, как и любые другие пользовательские типы, объявлению класса или классов всегда предшествует другое ключевое слово - type. Далее следует перечисление всех его полей, свойств и методов, сгруппированных по 4 разделам, определяющих уровень видимости. В результате для описания класса мы имеем следующую структуру:

type <Имя класса> = class (<Имя родительского класса>)

private

<частные описания>

protected <защищенные описания>

public <общедоступные описания>

published <опубликованные описания>

end;

В представленной структуре описаниями являются объявления свойств и методов, при этом большинство составных частей в объявлении класса являются необязательными. В частности, если класс не имеет специфического предка, то имя родительского класса можно опустить, при этом подразумевается, что такой класс происходит от общего для всех классов предка - TObject. Что касается секций, определяющих уровень видимости, то их в ряде случаев так же можно опустить. При этом будет подразумеваться, что все объявленные в классе свойства и методы будут принадлежать к категории опубликованных (published). Таким образом, следующие объявления классов TClass1 и TClass2 будут полностью эквивалентны:

type TClass1 = class (TObject)

 published

 Property1: integer;

 end;

 type TClass2 = class

Property1: integer;

 end;

Что касается области видимости, называемых так же правами доступа то, как следует из синтаксиса определения класса, их может быть 4 вида. Так, разделы private и protected содержат защищенные описания, а public и published - общедоступные. Более тонкие различия между этими категориями заключаются в следующем:

  • Категория Private является наиболее защищенной. Доступ к помещенным в эту группу свойствам и методам можно получить только в том же самом модуле;
  • В категорию Protected помещают те свойства класса, доступ к которым возможен из дочерних классов, объявленных так же и вне данного модуля;
  • Раздел Public содержит свойства и методы, которые должны быть видны во всех местах программы, где используется данный класс, т.е. "публичные".

Что касается опубликованных (published) свойств, то эта категория специально создана для библиотеки VCL. Эти те самые свойства, которые отображаются в инспекторе объекта, в остальном они аналогичны публичным свойствам. Все свойства, категория которых в классе явно не задана, считаются опубликованными.

Теперь обратимся собственно к содержимому этих групп. В их качестве могут выступать поля - по аналогии с записями, а так же методы - подпрограммы (как функции, так и процедуры). При этом если имена классов принято начинать с буквы T, то имена полей обычно начинают с буквы F (field). Что касается названий методов, то для их наименования руководствуются теми же принципами, что и при написании обычных подпрограмм, т.е. дают название, характеризующее выполняемое данным методом действие. Для примера создаем класс TUser, содержащий имя учетной записи (login) и пароль (password), а так же некий метод, проверяющий имя и пароль:

type TUser = class

fLogin: string;

fPassword: string;

function Connect: boolean;

end;

При реализации метода - в данном случае функции Connect, следует указывать имя класса, к которому она принадлежит. Например, в нашем случае она может выглядеть следующим образом:

functionTUser.Connect: boolean;

var

s,p: string;

begin

writeln('Input username:');

readln(s);

writeln('Input password:');

readln(p);

result:= (s=fLogin) and (p=fPassword);

end;

Фактически, составление подпрограммы как метода класса, ничем не отличается от написания обычной процедуры или функции, с той лишь разницей, что перед ее названием указывается имя класса, а в самой функции в качестве переменных доступны все поля этого класса - в данном случае fLogin и fPassword.

Кроме полей и методов, существуют собственно свойства (properties), являющиеся чем-то средним: они могут использовать, скажем, поле, для хранения значения, и функцию для его изменения. Фактически, свойства реализуют механизм доступа к полям. В таком случае поля, как правила, делают защищенными (private), а свойства - общедоступными (published). Допустим, в нашем классе TUser, который хранит некую информацию о пользователе, можно реализовать процедуру смены пароля таким образом, чтобы при попытке установки нового пароля он проверялся на допустимость - скажем, подходил по количеству знаков. В таком случае можно создать свойство Password, которое будет обращаться непосредственно к полю fPassword в случае, когда пароль надо просто считать, и вызывать процедуру проверки при попытке изменения этого поля. Попутно создадим свойство Login, но без процедуры проверки. Объявление такого класса может выглядеть следующим образом:

 

 

type TUser = class

private

fLogin: string;

fPassword: string;

procedure setPassword(newpass: string);

published

property Login: read fLogin write fLogin;

 property Password: read fPassword write setPassword;

 function Connect: boolean;

end;

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

При обращении к свойству Password на чтение (read) будет автоматически возвращаться значение, хранящееся в поле fPassword, а при попытке записать (write) в поле новое значение будет вызываться процедура setPassword. Сама эта процедура может выглядеть следующим образом:

procedureTUser.setPassword(newpass: string);

 begin

if Length(newpass)>3 then fPassword:=newpass

 else writeln('Error! Passwordistooshort!');

end;

Эта подпрограмма проверяет новое значение пароля на длину, и если оно содержит более 3 символов, то присваивается полю fPassword, в противном случае значение поля не меняется, и выводится сообщение об ошибке. В самой же программе обращение к свойству класса выглядит точно так же, как к полю записи, т.е. с использованием доступа через точку:

User.Login:='Serge';

Конструкторы и деструкторы

В самом начале предыдущего примера мы использовали метод Create для создания экземпляра класса TUser, а в конце - метод Destroy для его удаления, хотя сами эти методы определены в классе не были. Но вспомним, что в ООП существует такое понятие, как наследование, а все классы в Delphi происходят от класса TObject. Именно по наследству от TObject классу TUser эти методы и достались.

Метод Create является конструктором класса, т.е. подпрограммой, создающий объект (экземпляр класса) в памяти. В том случае, если при создании экземпляра класса никаких дополнительных действий производить не требуется, используют метод Create, доставшийся от родительского класса. Если же при создании объекта требуется выполнить какие-либо действия, то метод Create переписывают заново. Например, если при инициализации класса TUser нам надо было бы задать какие-либо имя пользователя и пароль по умолчанию, то мы могли бы определить конструктор заново:

 

 

type TUser = class

...

constructor Create;

end; constructor TUser.Create;

begin

inherited Create;

fLogin:='User';

fPassword:='password';

 end;

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

Есть всего одно условие, касающееся расположения конструкторов и деструкторов в объявлении класса: они должны быть объявлены в общедоступной части, т.е. в public или в published.

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

constructorTUser.Create;

begin

inheritedCreate;

MegaInfo:=TMegaUserInfo.Create;

...

end;

Что касается метода Destroy, то он призван освободить память, занимаемую экземпляром класса, и называется деструктором (destructor). Соответственно, в том случае, если среди полей класса будут другие объекты, использование переопределенного деструктора обязательно. Например, для класса TUser с полем типа TMegaUserInfo мы получим следующий деструктор:

destructorTUser.Destroy;

begin

 ...

MegaInfo.Destroy;

 inherited Destroy;

 end;

Обратите внимание на то, что если в конструкторе наследуемый метод Create всегда вызывается первым, то в деструкторе наследованный метод Destroy - последним.

Однако просматривая исходные коды других программ (включая библиотеку VCL) вы вряд ли обнаружите вызов метода Destroy напрямую. Дело в том, что к моменту уничтожения того же объекта TUser поле MegaInfo уже может быть освобождено, например, путем присвоения ему в программе значения "nil" (нулевого указателя):

User.MegaInfo:=nil;

User.Destroy; // здесь во время выполнения возникнет ошибка!

 Таким образом, попытка повторно освободить память при помощи метода Destroy приведет к ошибке выполнения программы. Поэтому на практике, вместо метода Destroy всегда используют метод Free, который сначала проверяет, не является ли ссылка на объект нулевым указателем, и если нет - то только тогда вызывает деструктор. Соответственно, вид деструктора и его вызов из программы будут выглядеть таким вот образом:

destructorTUser.Destroy;

begin

...

MegaInfo.Free;

 inherited Destroy;

end;

... User.MegaInfo:=nil;

User.Free; // теперьошибкинебудет

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

Конструкторы и деструкторы, подобно другим подпрограммам, могут принимать значения в качестве параметров. Например, если при создании нового экземпляра класса TUser нам нужно было бы сразу получить его имя, то конструктор получился бы таким:

constructor TUser.Create(str: string);

 begin

inherited Create;

fLogin:=str;

fPassword:='password';

 end;

Соответственно, создание объекта типа TUser в программе теперь будет выглядеть следующим образом:

User:=TUser.Create('Вася');

Аналогичным образом параметры можно передавать и деструкторам, при этом, в зависимости от значения параметров, они могут, скажем, выполнять или не выполнять перед уничтожением объекта какие-либо действия, вроде сохранения данных на диск, вывода того или иного сообщения и т.д.










Последнее изменение этой страницы: 2018-06-01; просмотров: 168.

stydopedya.ru не претендует на авторское право материалов, которые вылажены, но предоставляет бесплатный доступ к ним. В случае нарушения авторского права или персональных данных напишите сюда...