Студопедия

КАТЕГОРИИ:


Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748)

Реализация интерфейсов в Delphi

Реализация интерфейса в Delphi всегда осуществляется в классе. Для этого в объявлении класса необходимо указать, какие интерфейсы он реализует.

Type TMyClass = class(TComponent, IMyInterface, IDropTarget)

// Реализация методов

end;

Класс TMyClass реализует интерфейсы IMyInterface и IDropTarget. Необходимо понимать, что реализация классом нескольких интерфейсов не означает множественного наследования и вообще наследования класса от интерфейса. Указание интерфейсов в описании класса означает лишь то, что в данном классе реализованы все эти интерфейсы.

Класс должен иметь методы, точно соответствующие по именам и спискам параметров всем методам всех объявленных в его заголовке интерфейсов. Рассмотрим более подробный пример:

type

ITest = interface

['{61F26D40-5CE9-11D4-84DD-F1B8E3A70313}']

procedure Beep;

end;

TTest = class(TInterfacedObject, ITest)

procedure Beep;

destructor Destroy: override;

end;

procedure TTest.Beep;

begin

Windows.Beep(0,0);

end;

destructor TTest.Destroy;

begin

inherited;

MessageBox(0,'TTest.Destroy', nil, 0);

end;

Здесь класс TTest реализует интерфейс ITest. Рассмотрим использование интерфейса из программы:

procedure TForm1.Button1Click(Sender: TObject);

var

Test:ITest;

begin

Test:= TTest.Create;

Test.Beep;

end;

В этом примере можно увидеть некоторые особенности поддержки СОМ-технологий в Delphi.

Во-первых, оператор присваивания при приведении типа данных к интерфейсу неявно вызывает метод _AddRef. При этом количество ссылок на интерфейс увеличивается на 1.

Во-вторых, в коде не делается никаких попыток освободить память, выделенную под объект TTest. Тем не менее, если выполнить эту программу, то на экран будет выведено сообщение о том, что вызывался деструктор. Это происходит потому, что при выходе переменной, ссылающейся на интерфейс, за область видимости (либо при присвоении ей другого значения) компилятор Delphi генерирует код для вызова метода _Release, информируя реализацию о том, что ссылка на нее больше не нужна.

 

Если нужно уничтожить реализацию интерфейса немедленно, не дожидаясь выхода переменной за область видимости следует присвоить ей значение nil:

var

Test: ITest;

Т: TTest;

begin

Т:= TTest.Create;

Test:= Т;

Test.Beep;

Test:= nil; // Неявно вызываются IUnknown._Release

end:

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

· при приведении типа объекта к интерфейсу вызывается метод _AddRef;

· при выходе переменной, ссылающейся на интерфейс, за область видимости, а также при присвоении ей другого значения вызывается метод _Release;

· единожды запросив у объекта интерфейс, в дальнейшем не следует освобождать объект вручную.

В рассмотренных примерах код для получения интерфейса у класса генерировался (с проверкой типов) на этапе компиляции. Если класс не реализует требуемого интерфейса, то программа не компилируется. Однако существует возможность запросить интерфейс и во время выполнения программы. Для этого служит оператор as, который вызывает метод QueryInterface и, в случае успеха, возвращает ссылку на полученный интерфейс. В противном случае генерируется исключение.

Например, следующий код будет успешно откомпилирован, но при выполнении вызовет ошибку Interface not supported (интерфейс не поддерживается):

var

Test: ITest;

begin

Test:= TInterfacedObject.Create as Itest;

Test.Beep;

end;

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

var

Test: ITest;

begin

Test:= TTest.Create as ITest;

Test.Beep;

end;

Проведем сравнение интерфейсов с классами, сведения о которых известны из объектно-ориентированного программирования. Для интерфейсов, так же как и для классов, определено понятие иерархии. Суть этого понятия: каждый класс (или интерфейс) может иметь одного или нескольких потомков. Во всех потомках данного класса (интерфейса) сохраняются все методы (и данные для классов), которые имеются у родителей. В Delphi классы (точно так же, как и интерфейсы) могут иметь только одного родителя, поэтому они образуют иерархическое дерево. Его можно увидеть в Delphi при выборе команды View > Browser в меню среды разработки. В вершине иерархического дерева классов находится родоначальник всех классов — TObject, а интерфейсов — Unknown. Все остальные потомки содержат методы TObject (IUnknown).

В классах Delphi различают три типа методов — статические, динамические и виртуальные. При генерации компилятором кода для статического метода класса указывается его относительный адрес в памяти, и при вызове такого метода управление просто передается по указанному адресу. Соответственно, одна копия такого метода в памяти обслуживает все экземпляры данного класса и его потомков. При вызове же динамических и виртуальных методов процесс обращается к таблицам виртуальных или динамических методов. Эти таблицы хранятся в ОЗУ компьютера, и уже оттуда извлекаются адреса вызываемых методов. Далее управление передается по найденному адресу.

Абстрактным методом называется виртуальный или динамический метод, который объявлен, но не реализован. Классы, в которых имеются абстрактные методы, называются абстрактными классами. При объявлении абстрактного метода в таблицу виртуальных (динамических) методов заносится значение nil. При попытке вызвать абстрактный метод из экземпляра такого класса генерируется исключение EAbstractError.

Абстрактные методы требуют их перекрытия в потомках данного класса. Например, пусть имеется абстрактный класс TStream, в котором определены два абстрактных метода — Read и Write. В потомках этого класса — TMemoryStream и TFileStream — эти методы реализованы так, что происходит чтение (запись) данных в память для первого класса и в файл — для второго. Поэтому достаточно написать всего одну процедуру для сохранения данных в поток данных (stream), при этом параметром этого метода является объект типа TStream. Далее, при вызове этой процедуры с объектом TMemoryStream все данные запишутся в память, а с объектом TFileStream — в файл.

Все методы интерфейсов являются виртуальными и абстрактными. Иными словами, они только объявлены, но не реализованы. Дело в том, что интерфейс создается в одном модуле (в основном на СОМ-сервере), а используется в другом (в СОМ-клиенте). Для того чтобы клиент знал, какие методы имеются в данном интерфейсе и какие параметры необходимо указать при вызове данного метода, используются абстрактные методы. Получив ссылку на созданный сервером интерфейс, клиент знает, например, что имеется метод (функция) QueryInterface с первым параметром типа TGUID и со вторым в виде нетипизированной переменной, причем после выполнения метод возвращает переменную типа HResult. Соответственно, клиент может вызвать этот метод с данным списком параметров, и сервер обязан его выполнить.

Следует обратить внимание на то, что главным для интерфейса является не название метода (QueryInterface), а список параметров данного метода и то, каким по очереди он был объявлен. Порядок объявления методов определяет место методов в виртуальной таблице.

Таким образом, вызов методов интерфейса осуществляется следующим образом. Интерфейс создается на сервере, а клиент получает на него ссылку. После этого клиент для вызова метода QueryInterface производит следующие операции.

1. В стек помещаются адрес, где находится переменная HResult, и адреса переменных Р и IID.

2. На основании полученной ссылки на интерфейс вычисляется адрес таблицы виртуальных методов.

3. Находится третий столбец данной таблицы (поскольку метод QueryInterface реализован третьим).

4. Из таблицы виртуальных методов извлекается адрес метода и ему передается управление.

5. После возвращения управления очищается стек.

 

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

 

<== предыдущая лекция | следующая лекция ==>
Интерфейс IUnknown | Интерфейс ICIassFactory и использование системного реестра
Поделиться с друзьями:


Дата добавления: 2014-01-05; Просмотров: 1156; Нарушение авторских прав?; Мы поможем в написании вашей работы!


Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет



studopedia.su - Студопедия (2013 - 2024) год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! Последнее добавление




Генерация страницы за: 0.009 сек.