Що таке раннє та пізнє зв’язування?


77

Я постійно чую про раннє та пізнє зв’язування, але не розумію, що вони є. Я знайшов таке пояснення, яке не розумію:

Рання прив'язка відноситься до присвоєння значень змінним під час проектування, тоді як пізня прив'язка - до присвоєння значень змінним протягом часу виконання.

Чи може хтось, будь ласка, визначити два типи зв’язування та порівняти їх?


1
час компіляції проти часу виконання.
барлоп

Ось добре прочитане на цю тему: en.wikibooks.org/wiki/Introduction_to_Programming_Languages/…
Джей Елстон

Відповіді:


84

У плутанині є два основні поняття: прив'язка та завантаження. Це пов'язане з концепцією DataBinding, яка десь в середині часто робить і те, і інше. Розглянувши це, я збираюся додати ще одну концепцію, завершити трифекти, відправити.

Типи

Пізнє прив'язування : тип невідомий, поки змінна не виконується під час виконання; зазвичай через присвоєння, але є й інші способи примусити тип; динамічно типізовані мови називають це основної особливістю, проте багато мов, що мають статичний тип, мають певний метод досягнення пізнього зв’язування

Часто реалізується з використанням [спеціальних] динамічних типів, самоаналізу / відображення, прапорів та параметрів компілятора або за допомогою віртуальних методів шляхом запозичення та розширення динамічної відправки

Рання прив'язка : тип відомий перед тим, як змінна виконується під час виконання, як правило, за допомогою статичних, декларативних засобів

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

Функції

Статична відправка : відома, конкретна функція або підпрограма під час компіляції; вона однозначна і узгоджується підписом

Реалізується як статичні функції; жоден метод не може мати однаковий підпис

Динамічна відправка : не конкретна функція або підпрограма під час компіляції; визначається контекстом під час виконання. Існує два різних підходи до "динамічної диспетчеризації", що відрізняються тим, яка контекстна інформація використовується для вибору відповідної реалізації функції.

В одиночній [ динамічній ] відправці для визначення відповідної реалізації функції використовується тільки тип екземпляра. У мовах статичного типу, що це означає на практиці, це те, що тип екземпляра вирішує, яка реалізація методу використовується незалежно від типу посилання, зазначеного при оголошенні / призначенні змінної. Оскільки для одержання відповідної реалізації використовується лише один тип - тип об’єкта об'єкта - такий підхід називається "разова відправка".

Існує також декілька [ динамічних ] відправлень , де типи вхідних параметрів також допомагають визначити, яку функцію виконувати. Оскільки кілька типів - як тип екземпляра, так і тип (и) параметрів (и) - впливають на те, який спосіб вибору обраний, цей підхід називається "багаторазовою відправленням".

Реалізується як віртуальна або абстрактна функції; інші підказки включають перекриті, приховані або затінені методи.

Примітка: Незалежно від того, чи передбачає перевантаження методу динамічне відправлення, залежить від мови. Наприклад, у Java перевантажені методи статично відправляються.

Цінності

Ледаче завантаження : стратегія ініціалізації об'єкта, яка відкладає призначення значення до необхідності ; дозволяє об’єкту бути фактично дійсним, але свідомо неповним станом і чекати, поки дані будуть потрібні перед завантаженням; часто виявляється особливо корисним для завантаження великих наборів даних або очікування на зовнішніх ресурсах

Часто реалізується шляхом цілеспрямованого завантаження колекції чи списку в складений об'єкт під час конструктора або ініціалізації викликів, поки хтось, хто викликає низхідний потік, не попросить переглянути вміст цієї колекції (наприклад, get_value_at, get_all_as тощо). Варіанти включають завантаження метаінформації про колекцію (наприклад, розмір або клавіші), опускаючи фактичні дані; також надає механізм для деяких періодів виконання, щоб забезпечити розробників досить безпечною та ефективною схемою впровадження одиночних програм

Eager Loading : стратегія ініціалізації об'єкта, яка негайно виконує всі присвоєння значень для того, щоб всі дані, необхідні для завершення, перед тим, як вважати себе в дійсному стані.

Часто реалізується шляхом надання якомога швидше складених об'єктів з усіма відомими їм даними, наприклад, під час виклику конструктора або ініціалізації

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

Часто реалізується як спроба забезпечити більш чітку, послідовну синхронізацію між різними аспектами програми (наприклад, модель перегляду для перегляду, модель до контролера тощо) та розповідає про такі поняття, як джерело та ціль, кінцеві точки, прив'язування / відключення, оновлення та такі події, як on_bind, on_property_change, on_explicit, on_out_of_scope


РЕДАКЦІЯ ПРИМІТКА: Остання основна редакція, щоб дати опис прикладів того, як вони часто трапляються. Конкретні приклади коду повністю залежать від реалізації / часу виконання / платформи


2
Ця відповідь здається занадто специфічною для об'єктно-орієнтованих мов.
Джек

27

Все, що вирішує компілятор під час компіляції, може посилатися на ВІДКРИЙНЕ / КОМПІЛЮВАННЯ ЧАСУ Прив'язка, і все, що вирішується в режимі 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 ++ як до об'єкта неправильного класу, але їх vtables будуть зіпсовані, і код просто порушиться.
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 може викликати або додавання, або суб, залежно від вибору користувача [під час виконання].


3

Рання та пізня прив’язка мають сенс лише в контексті типів, а не в способі їх опису. В основному всі сучасні мови набрані в тому сенсі, що всі значення мають фіксований тип. Різниця виникає, коли ми дивимося на динамічно порівняно зі статично типовими мовами. У динамічно типізованих мовах змінні не мають типів, тому вони можуть посилатися на значення будь-якого типу, і це означає, що при виклику методу на об'єкт, на який посилається якась змінна, єдиний спосіб визначити, чи дійсний цей виклик, чи не шукайте клас для об'єкта і переконайтесь, що цей метод насправді існує. Це дозволяє зробити деякі цікаві речі, такі як додавання нових методів до класів під час виконання, тому що фактичний пошук методу відкладається до останнього моменту. Більшість людей називає такий стан справ пізнім обов'язковим.

У статично типізованій мові змінні мають типи і один раз оголошені не можуть посилатися на будь-яке значення, яке не є одного типу. Це не зовсім вірно, але поки що припустимо. Тепер, якщо ви знаєте, що змінна буде коли-небудь посилатися на значення конкретного типу, тоді немає підстав з'ясовувати, чи дійсний виклик методу чи не під час виконання, оскільки ви можете визначити дійсність до того, як код коли-небудь запуститься. Це називається ранньою зв'язуванням.

Приклад демонстрації пізнього зв’язування в рубіні:

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 Scripting Runtime" під час проектування . Перевагою є те, що ви отримуєте повідомлення про помилку вже під час компіляції, коли у вас є помилка друку FileSystemObjectабо назви методів GetSpecialFolder.

Пізнє зв’язування

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

Для цього не потрібно заздалегідь встановлювати посилання, створення екземпляра та визначення типу просто відбуватиметься під час виконання. Під час спроби викликати неіснуючий метод компілятор не скаржиться x, це призведе до помилки під час виконання лише тоді, коли виконується конкретна лінія.

Отже, недоліком пізнього зв’язування є те, що у вас тут немає жодної сильної перевірки типу. Але це теж перевага - скажімо, у вас є компонент, де існує кілька версій, і кожна нова версія забезпечує деякі додаткові функції. (Приклад в реальному світі - компоненти MS Office, як інтерфейс Excel COM) Пізнє прив'язування дозволяє вам писати код, який працює разом з усіма цими версіями - ви можете спочатку визначити конкретну версію компонента, і якщо ви дізнаєтесь, що у вас є доступна лише старіша версія, уникайте виконання функцій, які не працюють з цією версією.


-2

Мабуть, найпоширеніший приклад пізнього прив’язки - це вирішення Інтернет-адрес. Він підтримує динамічні системи та великі системи, не намагаючись зв’язати та прив’язати кожен сайт у світі до того, як ви зможете дістатись до будь-якого, але з іншого боку, він виконує деяку накладну вартість (пошук DNS, набагато менше маршрутизації IP) під час виконання.

Зважаючи на це, більшість різновидів зв'язування в мовному середовищі є більш-менш ранньою, під час компіляції чи зв’язку.

Кожен вид має витрати та переваги.


Чи можете ви розмістити посилання на це визначення обов'язкового веб-сайту? Я не чув про вирішення Інтернет-адрес як "обов'язкових", хоча оскільки прив'язка є актом вирішення імен, я вважаю, що хтось стверджував, що концепція раннього / пізнього прив'язки може бути застосована до вирішення URI до Інтернет-адрес. Але це не є загальною інтерпретацією, і концепція ранньої / пізньої прив'язки передує часу, коли комп'ютери зазвичай підключалися до Інтернету.
Джей Елстон
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.