Что такое раннее и позднее связывание?


77

Я продолжаю слышать о раннем и позднем связывании, но я не понимаю, что это такое. Я нашел следующее объяснение, которое я не понимаю:

Раннее связывание относится к присвоению значений переменным во время разработки, тогда как позднее связывание относится к присвоению значений переменным во время выполнения.

Может ли кто-нибудь определить два типа привязки и сравнить их?


1
время компиляции против времени выполнения.
Барлоп

Вот хорошее прочтение на эту тему: en.wikibooks.org/wiki/Introduction_to_Programming_Languages/…
Джей Элстон,

Ответы:


84

В замешательстве есть две основные концепции: привязка и загрузка. Это связано с концепцией DataBinding, которая где-то посередине часто делает и то, и другое. После рассмотрения я собираюсь добавить еще одну концепцию, завершить trifecta, рассылку.

Типы

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

Реализуется часто с использованием [специальных] динамических типов, самоанализа / отражения, флагов и параметров компилятора или с помощью виртуальных методов, заимствуя и расширяя динамическую диспетчеризацию

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

Реализуется часто с использованием стандартных примитивных типов

функции

Static Dispatch : известная, специфическая функция или подпрограмма во время компиляции; это однозначно и соответствует подписи

Реализовано как статические функции; ни один метод не может иметь одинаковую подпись

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

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

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

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

NB. Зависит ли перегрузка метода от динамической диспетчеризации, зависит от языка. Например, в Java перегруженные методы отправляются статически.

Ценности

Ленивая загрузка : стратегия инициализации объекта, которая откладывает присвоение значения до момента необходимости ; позволяет объекту находиться в по существу действительном, но заведомо неполном состоянии и ожидать, пока данные не понадобятся, перед его загрузкой; часто оказывается особенно полезным для загрузки больших наборов данных или ожидания на внешних ресурсах

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

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

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

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

Реализуется часто как попытка обеспечить более чистую, согласованную синхронизацию между различными аспектами приложения (например, модель представления для просмотра, модель для контроллера и т. Д.) И рассказывает о таких понятиях, как источник и цель, конечные точки, привязка / отмена привязки, обновление и такие события, как on_bind, on_property_change, on_explicit, on_out_of_scope


ПРИМЕЧАНИЕ РЕДАКТИРОВАНИЯ: Последнее основное редактирование, чтобы предоставить описание примеров того, как это часто происходит. Конкретные примеры кода полностью зависят от реализации / среды выполнения / платформы


2
Этот ответ кажется слишком специфичным для объектно-ориентированных языков.
Джек

27

Все, что решает компилятор при компиляции, может быть связано с привязкой EARLY / COMPILE TIME, а все, что должно быть решено в RUNTIME , называется привязкой LATE / RUNTIME .

Например,

Метод Перегрузка и методы Перекрытие .

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

2) В методе Overriding в RUNTIME определяется, какой метод будет вызван. Так что это называется поздним связыванием .

Старался сделать его простым и легким для получения. Надеюсь это поможет.


9

Позднее связывание - это когда поведение оценивается во время выполнения. Это необходимо, когда вы действительно хотите определить, как действовать, основываясь на информации, которой вы располагаете только во время работы программы. Самым ярким примером, на мой взгляд, является механизм виртуальных функций, особенно в C ++.

class A
{
public:
    void f() {}
    virtual void g() {}
};

class B : public A
{
    void f() {}
    virtual void g() {}
};

int main()
{
    A* a = new B;
    a->f();
    a->g();
}

В этом примере a->f()фактически вызовет void A::f(), потому что он ранний (или статически) ограничен, и поэтому программа во время выполнения думает, что это просто указатель на Aпеременную типа, тогда как на a->g()самом деле вызовет void B::g(), потому что компилятор, видящий g()виртуальный, вводит код для просмотра вверх адрес правильной функции для вызова во время выполнения.


1
"Время выполнения"? Вы говорите о C ++. C ++ компилируется прямо в машинный код, ему не требуется среда выполнения для разрешения виртуальных методов.
tdammers

3
@tdammers C ++ действительно нуждается в библиотеке времени выполнения, но не для виртуальных вызовов. Если вы внимательно прочитаете, вы заметите, что в этом ответе говорится, что компилятор «внедряет код для поиска адреса правильной функции [...] во время выполнения».

Хорошо, но этот «код для поиска адреса правильной функции», по сути, является просто разностным указателем без учета типов, за которым следует вызов функции. Здесь нет «мышления»; единственная причина, по которой он работает надежно, заключается в том, что компилятор выполняет проверку типов во время компиляции ; во время выполнения сгенерированный код доверяет компилятору выполнить домашнюю работу по проверке типов. Если вы используете небезопасные приведения (например, приведения указателей в стиле C), вы можете на законных основаниях рассматривать объекты C ++ как объекты неправильного класса, но их таблицы будут полностью испорчены, а код просто сломается.
tdammers

@tdammers Я старался держаться подальше от такого ответа, потому что это деталь реализации компиляторов, которая может быть, а может и не быть верной для какого-то эзотерического компилятора. Важна концепция.
Ям Маркович

1
@tdammers И под "временем выполнения" я подразумеваю "программу во время выполнения". Очевидно, что C ++ не управляется. Но так как вы показали мне, что это может вызвать путаницу, я перехожу на полную формулировку.
Ям Маркович

5

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

  int add(int x,int y)
  {
    return x+y;
  }
  int sub(int x,int y)
  {
      return x-y;
  }


    int main()
    {
     //get user choice
     int(*fp)(int,int);
     //if add
      fp=add;
     //else if sub
     fp=sub;
     cout<<fp(2,2);
    }

здесь функции add и sub являются функциями (его адрес связан в компоновщике времени компиляции)

но указатель на функцию запаздывает, fp может вызывать add или sub в зависимости от выбора пользователя [во время выполнения].


3

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

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

Пример для демонстрации позднего связывания в ruby:

a = 1 # a is an integer at this point
a.succ # asking for its successor is valid

class A
  def method_a
    # some code
  end
end

a = A.new
a.method_a # this is also valid
a.succ # this is not valid


class A # we can re-open the class and add a method
  def succ
    # some more code
  end
end
a.succ # now this is valid

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


1

Вместо того, чтобы дать вам академическое определение, я постараюсь показать вам некоторые различия на примере реального мира с использованием VBA:

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

Dim x As FileSystemObject
Set x = New FileSystemObject
Debug.Print x.GetSpecialFolder(0)

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

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

Dim x As Object
Set x = CreateObject("Scripting.FileSystemObject")
Debug.Print x.GetSpecialFolder(0)

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

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


-2

Возможно, самый распространенный пример позднего связывания - это разрешение интернет-URL. Он поддерживает динамические системы и большие системы, не пытаясь связать и связать каждый сайт в мире, прежде чем вы сможете получить доступ к любому, но с другой стороны он несет некоторые издержки (поиск DNS, намного меньше IP-маршрутизации) во время выполнения.

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

Каждый вид имеет свои издержки и выгоды.


Можете ли вы разместить ссылку на это определение связывания? Я не слышал о разрешении интернет-адресов как о «связывании», хотя, поскольку связывание является актом разрешения имен, я предполагаю, что кто-то утверждал, что концепция раннего / позднего связывания может быть применена к разрешению URI для интернет-адресов. Но это не общее толкование, и концепция раннего / позднего связывания предшествует тому времени, когда компьютеры обычно были подключены к Интернету.
Джей Элстон
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.