Что такое позднее связывание
Урок №166. Раннее и Позднее Связывания
Обновл. 15 Сен 2021 |
Как мы уже знаем из предыдущих уроков, выполнение программы в языке C++ происходит последовательно, строка за строкой, начиная с функции main(). Когда компилятор встречает вызов функции, то точка выполнения переходит к началу кода вызываемой функции. Откуда компилятор знает, что это нужно сделать?
При компиляции программы компилятор конвертирует каждый стейтмент программы в одну или несколько строк машинного кода. Каждой строке машинного кода присваивается собственный уникальный адрес. Так же и с функциями: когда компилятор встречает функцию, она конвертируется в машинный код и получает свой адрес.
Связывание — это процесс, который используется для конвертации идентификаторов (таких как имена переменных или функций) в адреса. Хотя связывание используется как для переменных, так и для функций, на этом уроке мы сосредоточимся только на функциях.
Раннее связывание
Большинство вызовов функций, которые встречает компилятор, являются прямыми вызовами функций. Прямой вызов функции — это стейтмент, который напрямую вызывает функцию. Например:
Прямые вызовы функций выполняются с помощью раннего связывания. Раннее связывание (или «статическая привязка») означает, что компилятор (или линкер) может напрямую связать имя идентификатора (например, имя функции или переменной) с машинным адресом. Помните, что все функции имеют свой уникальный адрес. Поэтому, когда компилятор (или линкер) встречает вызов функции, он заменяет его инструкцией машинного кода, которая сообщает процессору перейти к адресу функции.
Рассмотрим простую программу-калькулятор, в которой используется раннее связывание:
Позднее связывание
В некоторых программах невозможно знать наперёд, какая функция будет вызываться первой. В таком случае используется позднее связывание (или «динамическая привязка»). В языке C++ для выполнения позднего связывания используются указатели на функции. Вкратце, указатель на функцию — это тип указателя, который указывает на функцию вместо переменной. Функция, на которую указывает указатель, может быть вызвана через указатель и оператор вызова функции. Например, вызовем функцию add():
Вызов функции через указатель на функцию также известен как непрямой (или «косвенный») вызов функции. Следующая программа-калькулятор идентична вышеприведенной программе, за исключением того, что вместо прямых вызовов функций используется указатель на функцию:
Позднее связывание менее эффективное, так как присутствует «посредник» между процессором и функцией. С ранним связыванием процессор может перейти непосредственно к адресу функции. С поздним связыванием процессор должен прочитать адрес, хранящийся в указателе, и только затем перейти к этому адресу. Этот дополнительный шаг и замедляет весь процесс. Однако преимущество позднего связывания заключается в том, что оно более гибкое, нежели раннее связывание, так как не нужно решать, какую функцию следует вызывать до, собственно, запуска самой программы.
На следующем уроке мы рассмотрим, как позднее связывание используется для реализации виртуальных функций.
Позднее связывание в C#
Интерфейс положим в Interface.dll. Эта библиотека будет общей для модулей и основного приложения. В нашем случае, модуль будет работоспособен, если в конечном итоге dll-ка модуля будет лежать в каталоге с Interdace.dll.
Каждый модуль будет предполагать реализацию метода someMethod и свойства name. Свойство будет возвращать имя модуля.
Класс каждого плагина (модуля) будет реализовывать интерфейс из Interdace.dll, которую необходимо добавить в референсы плагинов. Разрабатывая плагин, мы имеем лишь интерфейс, необходимый для реализации.
Итак, модули готовы. Приступим к написанию основного приложения MainApp. MainApp должно отслеживать появление новых модулей в определенной директории, подключение таковых, создание объектов классов модулей и приведение их к типу интерфейса (так же доступного в MainApp).
Поиск:
С помощью статичного метода LoadFile класса Assembly мы загрузили сборку, передав в метод название сборки. Класс Activator — это специальный класс для создания экземпляров типов и получения ссылок на удаленные объекты. С помощью метода CreateInstance класса Activator мы создали объект класса модуля, передав в метод тип класса оздаваемого объекта.
Вызывая loadPlugins() при запуске приложения, мы динамически подгрузим все модули, находящиеся в директории dlls, у которых классы реализуют нужный интерфейс.
Далее дело за вызовом методов. По какому-либо условию выбираем модуль, у которого собираемся вызвать метод.
Здесь условие выбора представляло собой комбобокс. Соответственно запускается Plugin1.pl1.someMethod() или Plugin2.pl2.someMethod().
А как же быть со статичными методами? Для работы с ними интерфейс помочь не может. Будем делать Invoke() метода, передавая имя.
Так вызовется статичный метод у класса плагина_2. Заметьте, первым параметром Invoke() мы передали null.
Для того, чтобы с помощью Invoke вызвать нестатичный метод, необходимо лишь передать объект, содержащий метод.
Итак, мы динамически загружали сборки, создавали новые типы и вызывали методы (в том числе и статические) во время выполнения — одним словом, попробовали на вкус рефлексию.
Исходники лежат здесь
Позднее статическое связывание в PHP (Часть I)
Позднее Статическое Связывание (Late Static Binding, LSB) является бурно темой обсуждений последние три года в кругах разработчиков PHP (и наконец мы его получили в PHP 5.3). Но зачем оно нужно? В данной статье, как раз и будет рассматриваться, как позднее статическое связывание может значительно упростить ваш код.
На встрече разработчиков PHP, которая проходила в Париже в ноябре 2005 года, тема позднего статического связывания официально обсуждалась основной командой разработчиков. Они согласились реализовать его, наряду со многими другими темами, которые стояли на повестке дня. Детали должны были быть согласованы в ходе открытых дискуссий.
С тех пор как позднее статическое связывание было объявлено как грядущая фишка, прошло два года. И вот наконец LSB стало доступно для использования в PHP 5.3. Но это событие прошло незаметно для разработчиков использующих PHP, из заметок только страничка в мануале.
Если кратко, новая функциональность позднего статического связывания, позволяет объектам все также наследовать методы у родительских классов, но помимо этого дает возможность унаследованным методам иметь доступ к статическим константам, методам и свойствам класса потомка, а не только родительского класса. Давайте рассмотрим пример:
Этот код выдаст такой результат:
Класс Ale унаследовал метод getName(), но при этом self все еще указывает на класс в котором оно используется (в данном случае это класс Beer). Это осталось и в PHP 5.3, но добавилось слово static. И снова рассмотрим пример:
Новое ключевое слово static указывает, что необходимо использовать константу унаследованного класса, вместо константы которая была определена в классе где объявлен метод getStaticName(). Слово static было добавлено, чтобы реализовать новый функционал, а для обратной совместимости self работает также как и в предыдущих версиях PHP.
Внутренне, основное отличие (и, собственно, причина почему связывание назвали поздним) между этими двумя способами доступа, в том, что PHP определят значение для self::NAME во время «компиляции» (когда симовлы PHP преобразуются в машинный код, который будет обрабатываться движком Zend), а для static::NAME значение будет определено в момент запуска (в тот момент, когда машинный код будет выполнятся в движке Zend).
Это еще один инструмент для PHP-разработчиков. Во второй части рассмотрим как его можно использовать во благо.
BestProg
Данная тема есть продолжением темы:
Содержание
Поиск на других ресурсах:
При изучении темы полиморфизма важно понять понятие позднего и раннего связывания, которое используется компилятором при построении кода программы в случае наследования.
Если классы образовывают иерархию наследования, то при обращении к элементам класса, компилятор может реализовывать один из двух возможных способов связывания кода:
Выбор того или иного вида связывания для каждого отдельного элемента (метода, свойства, индексатора и т.п.) определяется компилятором по следующим правилам:
Необходимые условия для реализации позднего связывания:
Рисунок 1. Позднее и раннее связывание. Отличия
В случае раннего связывания, как только компилятор встречает строку
Как следствие, после вызова
будет вызван метод Print() класса B.
Вызов метода Print() по ссылке на объект класса C
2. Что такое полиморфизм? Динамический полиморфизм
Полиморфизм – это свойство программного кода изменяться в зависимости от ситуации, которая возникает в момент выполнения программы.
Главный принцип полиморфизма – один интерфейс, много реализаций (методов). В терминах языка программирования, полиморфизм – это возможность с помощью ссылки на базовый класс обращаться к элементам (методов) экземпляров унаследованных классов единым унифицированным способом.
Использование преимуществ полиморфизма возможно в ситуациях:
3. Для каких элементов класса можно применять полиморфизм?
Полиморфизм можно применять для следующих элементов:
4. Схематическое объяснение полиморфизма
На рисунке 3 демонстрируется применение полиморфизма на примере двух классов.
5. Полиморфизм в случае передачи в метод ссылки на базовый класс. Позднее связывание
В любой метод может быть передана ссылка на базовый класс. С помощью этой ссылки также можно вызвать методы, свойства которые поддерживают полиморфизм.
Пример.
6. Какие требования накладываются на элемент класса для того, чтобы он поддерживал полиморфизм?
Для того, чтобы элемент класса (например метод) поддерживал полиморфизм, его нужно сделать виртуальным. Чтобы элемент класса был виртуальным, нужно выполнить следующие требования:
7. Использование ключевого слова new в цепочке виртуальных методов. Пример
Как известно, элемент класса, который объявлен виртуальным ( virtual ), передает возможность реализовать полиморфизм в одноименных элементах унаследованных классов. Таким образом, виртуальные элементы образовывают цепочку вниз по иерархии.
Текст демонстрационной программы следующий
На рисунке 4 схематично изображен вызов метода Print() в случае использования ключевого слова new.
Результат работы программы
Из класса Figure унаследовать класс Rectangle (прямоугольник), который содержит следующие поля:
В классе Rectangle реализовать следующие методы и функции:
В функции main() выполнить следующие действия:
Использование раннего и позднего связывания в автоматизации
Сводка
Способ связывания с сервером автоматизации может повлиять на многие компоненты программы, такие как производительность, гибкость и удобство поддержки.
В этой статье описываются типы привязки, доступные для клиентов автоматизации, а также обе стороны каждого метода.
Дополнительные сведения
Автоматизация это процесс, при котором один программный компонент обменивается данными с другим компонентом программного обеспечения и/или управляет им с помощью модели COM (Component Object Model) Майкрософт. Это основа для большинства межкомпонентных коммуникаций, используемых в таких языках, как Visual Basic или Visual Basic для приложений, и стал нормальной частью большинства программ.
Исторически объект автоматизации — это любой объект, поддерживающий интерфейс IDispatch. Этот интерфейс позволяет клиентам вызывать методы и свойства во время выполнения без необходимости знать точный объект, с которым они взаимодействуют во время конструирования; процесс, называемый поздней привязкой. Однако сегодня термин объект автоматизации можно применить практически к любому COM-объекту, даже к тем, которые не поддерживают IDispatch (и поэтому не могут иметь позднюю границу). В этой статье предполагается, что объект, который вы автоматизируют, поддерживает оба метода привязки.
Что такое привязка?
Binding — это процесс согласования вызовов функций, написанных программистом на фактическом коде (внутреннем или внешнем), реализующем функцию. Он выполняется при компиляции приложения, а все функции, вызываемые в коде, должны быть привязаны, прежде чем можно будет выполнить код.
Чтобы понимать процесс, подумайте о том, что «Binding» в плане публикации книги. Представьте, что ваш код похож на текст книги, где в определенном абзаце вы написали что-то вроде «ознакомьтесь с главой 12, страница x для получения дополнительных сведений.» Вы не знаете, что представляет собой номер страницы до завершения книги, поэтому прежде чем приступить к чтению абзаца, все страницы книги должны быть привязаны друг к другу и вставлен правильный номер страницы в абзац. Прежде чем ссылаться на другие части книги, дождитесь ее припривязки к книге.
Программное обеспечение для привязки аналогично. Код состоит из частей, которые необходимо отсоединить до того, как код будет доступен для чтения. Binding — это операция замены имен функций с адресами памяти (или смещениями памяти, что точнее), где код будет «переход к» при вызове функции. Для COM-объектов Address — это смещение памяти в таблице указателей (называемой v-таблицей), удерживаемой объектом. Когда функция COM привязана, она связывается с помощью v-таблицы.
Структура COM-объекта проста. Если код содержит ссылку на объект, он содержит косвенный указатель на верхнюю часть таблицы v. V-таблица — это массив адресов памяти, где каждая запись является другой функцией, которая может быть вызвана для этого объекта. Чтобы вызвать третью функцию для объекта COM, перейдите по трем записям в таблице, а затем перейдите к расположению памяти, в котором она присутствует. Это приводит к выполнению кода для функции и, по завершении, возвращает сведения о готовности к выполнению следующей строки кода.
В примере выше показано, что происходит при освобождении COM-объекта. Так как все COM-объекты наследуют от IUnknown, первые три записи в таблице являются методами для IUnknown. Когда необходимо освободить объект, код вызывает третью функцию в v-table (IUnknown:: Release).
К счастью, Visual Basic работает в фоновом режиме. Программисту Visual Basic не придется напрямую работать с таблицей v. Однако эта структура связана с тем, как все объекты COM связаны, и важно знать, что такое привязка.
Раннее связывание
Приведенный выше пример называется ранним (или v-табличным) связыванием. Для всех объектов COM такая форма привязки выполняется при каждом вызове интерфейса IUnknown COM-объекта. Но что делать с другими функциями объекта? Как вызвать метод Refresh или его родительское свойство? Это настраиваемые функции, которые обычно являются уникальными для объекта. Если их расположение в таблице v не может быть предполагаемым, как найти адреса функций, необходимые для их вызова?
Ответ, конечно, зависит от того, знаете ли вы заранее, как выглядит таблица v. В противном случае вы можете выполнить такой же процесс раннего связывания для пользовательских методов объекта, как и в методах IUnknown. Как правило, это называется «раннее связывание».
Чтобы использовать раннее связывание с объектом, необходимо знать, как выглядит его таблица v. В Visual Basic это можно сделать, добавив ссылку на библиотеку типов, описывающую объект, его интерфейс (v-table) и все функции, которые можно вызвать для объекта. После этого можно объявить объект как определенный тип, а затем задать и использовать этот объект с помощью v-таблицы. Например, если вы хотите автоматизировать Microsoft Office Excel, используя раннее связывание, необходимо добавить ссылку на библиотеку объектов Microsoft Excel 8,0 из проекта | Диалоговое окно References, а затем объявите переменную как имеющую тип «Excel. Application». После этого все вызовы, выполненные в объектной переменной, будут выполняться с ранней привязкой:
Этот метод прекрасно работает в большинстве случаев, но что делать, если вы не знаете точный объект, который будет использоваться во время создания? Например, что делать, если вам нужно поговорить с несколькими версиями Excel или, возможно, к объекту «Unknown»?
Позднее связывание
COM включает IDispatch. Объекты, реализующие IDispatch, имеют disp-интерфейс (если это единственный поддерживаемый интерфейс) или сдвоенный интерфейс (если у них также есть настраиваемый интерфейс, к которому можно выполнить раннее связывание). Клиенты, которые привязываются к интерфейсу IDispatch, называются «поздней привязкой», так как они определяются во время выполнения с помощью методов IDispatch для их обнаружения. Вернемся к предыдущему примеру книги, представьте, что он похож на сноску, которая направит вам оглавление, где вы хотите «найти» номер страницы в «время чтения» вместо того, чтобы он уже печатался в тексте.
Возможности интерфейса контролируются двумя функциями: Жетидсофнамес и Invoke. Первое сопоставление имен функций (строк) с идентификатором (называемым DISPID), представляющим функцию. Зная идентификатор функции, которую вы хотите вызвать, вы можете вызвать ее с помощью функции Invoke. Такая форма вызова метода называется «поздней привязкой».
Опять же, в Visual Basic способ привязки объекта задается объявлением объекта. Если вы объявили объектную переменную как «объект», на самом деле, поговорите Visual Basic для использования IDispatch и, следовательно, позднее привязку:
Как видите, оставшаяся часть кода одинакова. Единственное различие между ранней привязкой и поздней привязкой (в терминах написанного кода) находится в объявлении переменной.
Важно отметить, что «поздняя привязка» — это функция, которая вызывается, а не так, как она вызывается. Из предыдущего рассмотрения привязки в целом следует обратить внимание на то, что интерфейс IDispatch — это «ранняя привязка:», которая указывает на то, что Visual Basic устанавливает свойство Visible с помощью записи v-table (IDispatch:: Invoke), как и для любого вызова COM. Сам COM-объект отвечает за переадресацию вызова правильной функции, чтобы сделать Excel видимым. Это косвенное действие позволяет скомпилировать клиент Visual Basic (связанный с допустимым адресом функции), но по-прежнему не знает точную функцию, которая фактически выполняет работу.
Привязка DISPID
Некоторые клиенты автоматизации (наиболее заметно для MFC и Visual Basic 3,0, но также Visual Basic 5,0 и 6,0 по отношению к элементам управления ActiveX) используют гибридную форму позднего связывания, называемую привязкой DISPID. Если объект COM известен во время создания, идентификаторы DISPID для вызываемых функций могут кэшироваться и передаваться напрямую в IDispatch:: Invoke без необходимости вызывать Жетидсофнамес во время выполнения. Это может значительно повысить производительность, так как вместо двух вызовов COM на функцию необходимо выполнить только одну.
Привязку DISPID не является возможностью, как правило, можно выбрать в Visual Basic 5,0 или 6,0. Он используется для объектов, на которые ссылается библиотека типов, но не содержит настраиваемого интерфейса (только для объектов с disp-интерфейсом) и для агрегированных элементов управления ActiveX, но в общем случае Visual Basic использует раннее связывание, которое вы обычно используете Привязка DISPID.
Какую форму привязки следует использовать?
Ответ на этот вопрос зависит от структуры проекта. Майкрософт рекомендует использовать раннее связывание практически во всех случаях. Однако могут возникнуть причины для выбора позднего связывания.
Раннее связывание является предпочтительным методом. Это лучший участник, так как приложение связывается непосредственно с адресом вызываемой функции и при поиске во время выполнения нет дополнительных издержек. С точки зрения общей скорости выполнения он по крайней мере вдвое быстрее, чем с поздней привязкой.
Раннее связывание также обеспечивает безопасность типов. Если у вас есть ссылка на библиотеку типов компонента, Visual Basic предоставляет поддержку IntelliSense, которая помогает правильно кодировать каждую функцию. Visual Basic также предупреждает, если тип данных параметра или возвращаемого значения неверен, при написании и отладке кода сохраняется много времени.
Позднее связывание по-прежнему полезно в ситуациях, когда точный интерфейс объекта неизвестен во время конструирования. Если ваше приложение обращается к нескольким неизвестным серверам или необходимо вызывать функции по имени (например, с помощью функции Visual Basic 6,0 CallByName), необходимо использовать позднее связывание. Позднее связывание также помогает обойти проблемы совместимости между несколькими версиями компонента, которые неправильно изменились или подстроили его интерфейс между версиями.
Преимущества раннего связывания по возможности делают ее наилучшим выбором.
Поддержка совместимости в нескольких версиях
Если вы будете использовать компонент, который не передается вместе с пакетом установки, и не можете быть уверенным в точной версии, с которой вы будете взаимодействовать во время выполнения, следует уделять особое внимание предварительной привязке к интерфейсу, совместимому со всеми версии компонента или (в некоторых случаях) используют позднее связывание для вызова метода, который может существовать в определенной версии, и некорректно, если этот метод отсутствует в версии, установленной в клиентской системе.
Приложения Microsoft Office предоставляют хороший пример таких серверов COM. Приложения Office, как правило, расширяют свои интерфейсы, добавляя новые функции или исправляет предыдущие недостатки между версиями. Если необходимо автоматизировать приложение Office, рекомендуется выполнить предварительную привязывание к самой ранней версии продукта, которая может быть установлена в системе клиента. Например, если необходима возможность автоматизации Excel 95, Excel 97, Excel 2000 и Excel 2002, следует использовать библиотеку типов для Excel 95 (XL5en32. ОЛБ) для поддержания совместимости со всеми тремя версиями.
Приложения Office также демонстрируют, что объектные модели с крупными сдвоенными интерфейсами могут настрадаться от маршалинга на некоторых платформах. Для обеспечения оптимальной работы кода на всех платформах используйте IDispatch.
Ссылки
Дополнительные сведения о COM, v-таблицах и использовании автоматизации можно найти в следующих книгах:
Рожерсон, Дале, внутри COM, МСПРЕСС, ISBN: 1-57231-349-8.
Курланд, Мэтт, Advanced Visual Basic 6, DevelopMent, 0201707128.