Что такое связывание программирование

Урок №166. Раннее и Позднее Связывания

Обновл. 15 Сен 2021 |

Как мы уже знаем из предыдущих уроков, выполнение программы в языке C++ происходит последовательно, строка за строкой, начиная с функции main(). Когда компилятор встречает вызов функции, то точка выполнения переходит к началу кода вызываемой функции. Откуда компилятор знает, что это нужно сделать?

При компиляции программы компилятор конвертирует каждый стейтмент программы в одну или несколько строк машинного кода. Каждой строке машинного кода присваивается собственный уникальный адрес. Так же и с функциями: когда компилятор встречает функцию, она конвертируется в машинный код и получает свой адрес.

Связывание — это процесс, который используется для конвертации идентификаторов (таких как имена переменных или функций) в адреса. Хотя связывание используется как для переменных, так и для функций, на этом уроке мы сосредоточимся только на функциях.

Раннее связывание

Большинство вызовов функций, которые встречает компилятор, являются прямыми вызовами функций. Прямой вызов функции — это стейтмент, который напрямую вызывает функцию. Например:

Прямые вызовы функций выполняются с помощью раннего связывания. Раннее связывание (или «статическая привязка») означает, что компилятор (или линкер) может напрямую связать имя идентификатора (например, имя функции или переменной) с машинным адресом. Помните, что все функции имеют свой уникальный адрес. Поэтому, когда компилятор (или линкер) встречает вызов функции, он заменяет его инструкцией машинного кода, которая сообщает процессору перейти к адресу функции.

Рассмотрим простую программу-калькулятор, в которой используется раннее связывание:

Позднее связывание

В некоторых программах невозможно знать наперёд, какая функция будет вызываться первой. В таком случае используется позднее связывание (или «динамическая привязка»). В языке C++ для выполнения позднего связывания используются указатели на функции. Вкратце, указатель на функцию — это тип указателя, который указывает на функцию вместо переменной. Функция, на которую указывает указатель, может быть вызвана через указатель и оператор вызова функции. Например, вызовем функцию add():

Вызов функции через указатель на функцию также известен как непрямой (или «косвенный») вызов функции. Следующая программа-калькулятор идентична вышеприведенной программе, за исключением того, что вместо прямых вызовов функций используется указатель на функцию:

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

На следующем уроке мы рассмотрим, как позднее связывание используется для реализации виртуальных функций.

Источник

Что такое связывание программирование


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

при определении языка;

при реализации компилятора;

во время трансляции, включающей в себя:

при работе препроцессора (макропроцессор)

во время лексического, синтаксического и семантический анализа, генерации кода и его оптимизации;

при компоновке (связывании);

во время загрузки программы;

во время выполнения программы, в том числе:

при входе в модуль (процедуру, функцию);

в произвольной точке выполнения программы.

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

2. Конкретная размерность переменной int определяется при реализации соответствующего компилятора.

4. Если переменная определяется обычным способом в виде int a ; то связывание переменной с соответствующим ей типом происходит во время трансляции (на фазе семантического анализа).

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

при трансляции переменная привязывается к некоторому относительному адресу в сегменте данных объектного модуля (то есть ее размещение фиксируется только относительно начала модуля)

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

6. Если переменная определяется как автоматическая (локальная внутри тела функции или блока), то она размещается в стеке программы:

во время трансляции определяется ее размерность и генерируются команды, которые резервируют под нее память в стеке в момент входа в тело функции (блок). То есть в процессе трансляции переменная связывается только с относительным адресом в стеке программы;

связывание локальным переменной с ее адресом в сегменте стека осуществляется при выполнении в момент входа в тело функции (блок). Благодаря такому способу связывания в рекурсивной функции существует столько «экземпляров» локальных переменных, сколько раз функция вызывает сама себя.

7. Тип операции “+” в конкретном выражении a + b определяется при трансляции в зависимости от типов операндов. В данном случае генерируется операция целого сложения.

8. С точки зрения времени связывания понятие инициализация внешних переменных можно определить как связывание переменных с их значениями в процессе трансляции программы ( int a =10;) С этой точки зрения обычное присваивание можно рассматривать как связывание переменной с ее значением во время выполнения программы.

обычное статическое определение массива предполагает его связывание с памятью во время трансляции программы, поэтому тип сохраняемых в нем элементов и их количество должны быть величинами неизменными:

p = malloc(sizeof(double)*n); // или

// ПРИМЕР СИНТАКСИСА ДИНАМИЧЕСКОГО МАССИВА

p = realloc(p, sizeof(double)*n);

// ПРИМЕР СИНТАКСИСА ВИРТУАЛЬНОГО МАССИВА

p = realloc(p, sizeof(void *)*n);

// ПРИМЕР СИНТАКСИСА ВИРТУАЛЬНОГО МАССИВА

// С ПРОИЗВОЛЬНЫМИ ТИПАМИ ЭЛЕМЕНТОВ

Из рассмотренного примера видно, что реализация позднего связывания в языках программирования связана с использованием неявных (скрытых) указателей на структуры данных. Это предположение можно подтвердить и примером связывания для к такого объекта, как функция:

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

если функция вызывается в том же самом модуле, где она определена, то связывание вызова функции с ее телом (адресом) осуществляется в процессе трансляции;

если функция определена в другом модуле (частный случай, библиотека), то связывание вызова функции с ее телом (адресом) осуществляется при компоновке. Для этой цели тело функции в объектном модуле сопровождается информацией, именуемой “точкой входа”, сам вызов сопровождается “внешней ссылкой”, а для корректного формирования вызова Си-компилятору необходимо объявить прототип внешней функции. Для библиотечных функций необходимо подключить заголовочный файл, в котором они содержатся.

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

Источник

BestProg

Данная тема есть продолжением темы:

Содержание

Поиск на других ресурсах:

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

Если классы образовывают иерархию наследования, то при обращении к элементам класса, компилятор может реализовывать один из двух возможных способов связывания кода:

Выбор того или иного вида связывания для каждого отдельного элемента (метода, свойства, индексатора и т.п.) определяется компилятором по следующим правилам:

Необходимые условия для реализации позднего связывания:

Что такое связывание программирование. Смотреть фото Что такое связывание программирование. Смотреть картинку Что такое связывание программирование. Картинка про Что такое связывание программирование. Фото Что такое связывание программирование

Рисунок 1. Позднее и раннее связывание. Отличия

В случае раннего связывания, как только компилятор встречает строку

Как следствие, после вызова

будет вызван метод Print() класса B.

Что такое связывание программирование. Смотреть фото Что такое связывание программирование. Смотреть картинку Что такое связывание программирование. Картинка про Что такое связывание программирование. Фото Что такое связывание программирование

Вызов метода Print() по ссылке на объект класса C

2. Что такое полиморфизм? Динамический полиморфизм

Полиморфизм – это свойство программного кода изменяться в зависимости от ситуации, которая возникает в момент выполнения программы.

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

Использование преимуществ полиморфизма возможно в ситуациях:

3. Для каких элементов класса можно применять полиморфизм?

Полиморфизм можно применять для следующих элементов:

4. Схематическое объяснение полиморфизма

На рисунке 3 демонстрируется применение полиморфизма на примере двух классов.

Что такое связывание программирование. Смотреть фото Что такое связывание программирование. Смотреть картинку Что такое связывание программирование. Картинка про Что такое связывание программирование. Фото Что такое связывание программирование

5. Полиморфизм в случае передачи в метод ссылки на базовый класс. Позднее связывание

В любой метод может быть передана ссылка на базовый класс. С помощью этой ссылки также можно вызвать методы, свойства которые поддерживают полиморфизм.

Пример.

6. Какие требования накладываются на элемент класса для того, чтобы он поддерживал полиморфизм?

Для того, чтобы элемент класса (например метод) поддерживал полиморфизм, его нужно сделать виртуальным. Чтобы элемент класса был виртуальным, нужно выполнить следующие требования:

7. Использование ключевого слова new в цепочке виртуальных методов. Пример

Как известно, элемент класса, который объявлен виртуальным ( virtual ), передает возможность реализовать полиморфизм в одноименных элементах унаследованных классов. Таким образом, виртуальные элементы образовывают цепочку вниз по иерархии.

Текст демонстрационной программы следующий

На рисунке 4 схематично изображен вызов метода Print() в случае использования ключевого слова new.

Что такое связывание программирование. Смотреть фото Что такое связывание программирование. Смотреть картинку Что такое связывание программирование. Картинка про Что такое связывание программирование. Фото Что такое связывание программирование

Результат работы программы

Из класса Figure унаследовать класс Rectangle (прямоугольник), который содержит следующие поля:

В классе Rectangle реализовать следующие методы и функции:

В функции main() выполнить следующие действия:

Источник

Сильное и слабое связывание в программировании

Определения

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

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

Понятие «связывания» как и многие определения, относящиеся к области архитектуры программных приложений, не имеет единого определения. «Связывание» не является комплексом правил или спецификаций, поэтому мы дадим следующее определение:

Связанность определяет технические и организационные аспекты интеграции, а также направляет развитие интегрированной системы целиком.

Отмечу сразу, что я не приветствую перевод этого термина, данный в русской Википедии как «зацепление». В литературе и русскоязычной прессе 2000-х годов, когда вместе с сервис-ориентированной моделью продвигалась идея слабого связывания, под ним понималось слово coupling, а не cohesion. На мой взгляд, большинство профессионалов отрасли (а не несколько теоретиков, на которые ссылается Вики) слово «связывание» относит к слову «coupling», а не к слову cohesion. Однако если вам удобнее считать по-другому, просто переставьте в уме эти два термина, читая мою статью.

Выделяют два диаметрально противоположных вида связывания, и эволюция всех интеграционных технологий последовательно перемещалась от одного к другому: сильное (tightly) и слабое (loosely) связывание (coupling).

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

Например, сильно связанными являются методы одного приложения: они выполняются в одном адресном пространстве, разделяют общие переменные, вызов и передача информации происходит с минимальным задержками. В распределенной среде признаками сильного связывания обладают прямой сетевой вызов или удаленный вызов процедур (RPC), позволяющий разнородным процессам вызывать методы друг друга безотносительно к их расположению в сетевой среде.

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

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

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

Пример сильного и слабого связывания

Приведу мой любимый пример, который всегда привожу для студентов на лекциях.

А теперь представьте себе такую ситуацию. Проходит 10 лет и нескольких сотен ваших безмятежных поездок. В очередной раз вы звоните любимой тете. Но вдруг узнаете, что тетя Галя по не зависящим от вас причинам недавно уволилась с работы кассиром (оказалось, что она нарушала правила использования корпоративной скидки). Что теперь делать?! Вы бросаетесь в панику. Начинаете узнавать у знакомых, как покупать билеты. Бежите на вокзал. Покупаете сначала не туда. Потом не на то время. Страдаете, бегаете сдавать билеты и в конечном итоге пропускаете необходимый поезд и командировку.

А теперь представим, как бы развивалась ситуация, если бы вы покупали билеты с помощью слабого связывания. В конце 90-х годов вы бы ходили на вокзальные кассы (сейчас же есть и сайт, и приложение РЖД). Подходили ли бы к любому кассиру, называли бы поезд, дату и покупали бы желаемый билет. Для вас не имело бы значения, как зовут кассира, сколько ему лет, кто его родственники и как он живет. Кассир действовал бы с вами через строго определенный интерфейс, заданный его служебными инструкциями.

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

Сравнение сильного и слабого связывания

Рассмотрим технические аспекты различий сильного и слабого связывания:

ФакторСлабое связываниеСильное связывание
Физическая связьНепрямая, через промежуточное программное обеспечениеПрямая
Тип взаимодействияАсинхронныйСинхронный
Передача сообщенийДокументно-ориентированнаяRPC
Путь передачи данныхМаршрутизируетсяЖестко фиксирован
Объединение технологийГетерогенноеГомогенное
Типы данныхНезависимыеЗависимые
Определение синтаксисаПо взаимному соглашениюПубликуется в открытом доступе
Связывание (компонентов во время выполнения)Фиксированное и предварительноеПозднее, отложенное
Цель интеграцииПовторное использование, повышение эффективностиШирокая применимость
Последствия интеграцииОжидаемыеНепредскауземые

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

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

Источник

18.5 – Раннее и позднее связывание

Когда программа компилируется, компилятор преобразует каждую инструкцию в вашей программе на C++ в одну или несколько строк машинного кода. Каждой строке машинного кода дается собственный уникальный адрес в последовательности адресов. То же самое и с функциями – когда встречается функция, она преобразуется в машинный код и получает следующий доступный адрес. Таким образом, каждая функция получает уникальный адрес.

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

Раннее связывание

Большинство вызовов функций, с которыми сталкивается компилятор, будут прямыми вызовами функций. Прямой вызов функции – это инструкция, которая вызывает функцию напрямую. Например:

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

Давайте посмотрим на простую программу калькулятора, в которой используется раннее связывание:

Позднее связывание

В некоторых программах до момента выполнения (когда программа запущена) невозможно узнать, какая функция будет вызвана. Это называется поздним связыванием (или динамическим связыванием). В C++ один из способов получить позднее связывание – использовать указатели на функции. Вкратце, указатель на функцию – это тип указателя, который указывает на функцию, а не на переменную. Функция, на которую указывает указатель, может быть вызвана с помощью применения оператора вызова функции ( () ) к указателю.

Например, следующий код вызывает функцию add() :

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

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

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

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *