В чем разница между динамической отправкой и поздней привязкой В C++?


Я недавно читал о динамической отправке на Википедия и не мог понять разницу между динамической отправкой и поздней привязкой В C++.

когда используется каждый из механизмов?

точная цитата из Википедии:

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

11 65

11 ответов:

довольно приличный ответ на это фактически включен в вопрос о поздней и ранней привязке к programmers.stackexchange.com.

короче говоря, поздняя привязка относится к объект-сторона eval, динамическая отправка относится к функциональной стороне. В конце привязки то тип переменной является вариант во время выполнения. В dynamic-dispatch выполняемая функция или подпрограмма является вариантом.

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

лучший пример, который я могу предложить для поздней привязки,-это нетипизированный "объект" в Visual Basic. Среда выполнения выполняет всю тяжелую работу с поздним привязыванием для вас.

Dim obj

- initialize object then..
obj.DoSomething()

компилятор будет на самом деле кодируйте соответствующий контекст выполнения для runtime-engine для выполнения именованного поиска метода с именем DoSomething, и если обнаружено с правильно подобранными параметрами, фактически выполните базовый вызов. На самом деле,что-то о типе объекта известно (он наследует от IDispatch и обслуживает GetIDsOfNames() и т. д.). но что касается язык это касается тип переменной совершенно неизвестно во время компиляции, и она не имеет идея, если DoSomething это даже метод для любого obj на самом деле и пока среда выполнения не достигнет точки выполнения.

Я не буду беспокоиться о сбросе виртуального интерфейса et'al на C++, поскольку я уверен, что вы уже знаете, как они выглядят. Я надеюсь, что очевидно, что язык C++ просто не может этого сделать. Он строго типизирован. Это можете (и, очевидно, делает) динамическую отправку через функцию полиморфного виртуального метода.

позднее связывание вызова метода по имени во время выполнения. У вас действительно нет этого в c++, за исключением импорта методов из DLL.
Примером для этого может быть:GetProcAddress ()

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

The ссылке сам объяснил разницу:

динамическая отправка отличается от поздней привязки (также известной как динамическая привязка). В контексте выбора операции привязка относится к процессу связывания имени с операцией. Диспетчеризация относится к выбору реализации для операции после того, как вы определили, к какой операции относится имя.

и

с динамической диспетчеризации, имя может быть привязано к полиморфной операции во время компиляции, но реализация не будет выбрана до времени выполнения (так работает динамическая отправка в C++). Однако позднее связывание подразумевает динамическую диспетчеризацию, поскольку вы не можете выбрать, какую реализацию полиморфной операции выбрать, пока не выберете операцию, на которую ссылается имя.

но они в основном равны в C++ вы можете сделать динамическую отправку с помощью виртуальных функций и таблица виртуальных методов.

C++ использует раннюю привязку и предлагает как динамическую, так и статическую отправку. Форма отправки по умолчанию-статическая. Для получения динамической отправки необходимо объявить метод как виртуальный.

В C++, оба одинаковы.

в C++ существует два вида привязки:

  • статическая привязка-которая выполняется во время компиляции.
  • динамическая привязка-которая выполняется во время выполнения.

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

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

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

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

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

диспетчерский контроль к этому согласно параметру матч

http://en.wikipedia.org/wiki/Dynamic_dispatch

надеюсь, что это поможет вам

учитывая это многословное определение Википедии, я бы соблазнился классифицировать динамическую отправку как позднюю привязку C++

struct Base {
    virtual void foo(); // Dynamic dispatch according to Wikipedia definition
    void bar();         // Static dispatch according to Wikipedia definition
};

поздняя привязка вместо этого, для Википедии, похоже, означает отправку указателя на член C++

(this->*mptr)();

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

в литературе C++ однако late binding обычно используется для того, что Википедия называет динамической диспетчеризации.

позвольте мне привести вам пример различий, потому что они не одинаковы. Да, dynamic dispatch позволяет выбрать правильный метод, когда вы ссылаетесь на объект суперклассом, но эта магия очень специфична для этой иерархии классов, и вам нужно сделать некоторые объявления в базовом классе, чтобы заставить его работать (абстрактные методы заполняют vtables, так как индекс метода в таблице не может меняться между конкретными типами). Таким образом, вы можете вызывать методы в Tabby и Lion и Тигр все с помощью общего указателя кошки и даже есть массивы кошек, наполненных Львами и Тиграми и Tabbys. Он знает, что индексы эти методы ссылаются на vtable объекта во время компиляции (статическое / раннее связывание), хотя метод выбирается во время выполнения (динамическая диспетчеризация).

теперь давайте реализуем массив, который содержит львов, тигров и медведей! ((О Боже!)). Предполагая, что у нас нет базового класса под названием Animal, в C++ у вас будет значительная работа, потому что компилятор не позволит вам выполнять динамическую отправку без общего базового класса. Индексы для vtables должны совпадать, и это не может быть сделано между несвязанными классами. Вам нужно будет иметь vtable достаточно большой, чтобы содержать виртуальные методы всех классов в системе. Программисты C++ редко рассматривают это как ограничение, потому что вы были обучены думать определенным образом о дизайне классов. Я не говорю, что это лучше или хуже.

с поздней привязкой, время выполнения заботится об этом без общего базового класса. Обычно существует система хэш-таблиц, используемая для поиска методов в классах с системой кэша, используемой в диспетчере. Где в C++, компилятор знает все типы. В языке с поздней привязкой сами объекты знают свой тип (его не безтипный, сами объекты точно знают, кто они в большинстве случаев). Это означает, что я могу иметь массивы нескольких типов объектов, если я хочу (Львы и Тигры и медведи). И вы можете реализовать пересылку сообщений и прототипирование (позволяет изменять поведение для каждого объекта без изменения класса) и всевозможные другие вещи способами, которые намного более гибки и приводят к меньшим затратам кода, чем в языках, которые не поддерживают позднюю привязку.

когда-либо программа в Android и использовать findViewById ()? Вы почти всегда заканчиваете тем, что бросаете результат, чтобы получить правильный тип, и кастинг в основном лежит на компиляторе и отказывается от всех статическое качество проверки типов, которое должно сделать статические языки превосходящими. Конечно, вы могли бы вместо этого найти findTextViewById(), findEditTextById () и миллион других, чтобы ваши возвращаемые типы совпадали, но это выбрасывает полиморфизм из окна; возможно, вся основа ООП. Язык с поздней привязкой, вероятно, позволит вам просто индексировать идентификатор и рассматривать его как хэш-таблицу и не заботиться о том, какой тип индексируется или возвращается.

вот еще один пример. Предположим, что у вас есть свой класс Lion, и его поведение по умолчанию-съесть вас, когда вы его видите. В C++, если вы хотите иметь одного "обученного" Льва, вам нужно создать новый подкласс. Прототипирование позволит вам просто изменить один или два метода этого конкретного Льва, которые необходимо изменить. Это класс и тип не меняются. C++ не может этого сделать. Это важно, так как, когда у вас есть новый "AfricanSpottedLion", который наследует от Lion, вы можете тренировать его тоже. Прототип не меняется структура класса таким образом, он может быть расширен. Обычно именно так эти языки обрабатывают проблемы, которые обычно требуют множественного наследования, или, возможно, множественное наследование-это то, как вы справляетесь с отсутствием прототипирования.

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

динамическая отправка-это то, что происходит при использовании virtual ключевое слово в C++. Так например:

struct Base
{
    virtual int method1() { return 1; }
    virtual int method2() { return 2; } // not overridden
};

struct Derived : public Base
{
    virtual int method1() { return 3; }
}

int main()
{
    Base* b = new Derived;
    std::cout << b->method1() << std::endl;
}

печати 3, потому что метод был динамически распределяемых. Стандарт C++ очень осторожен не, чтобы указать, как именно это происходит за кулисами, но каждый компилятор под солнцем делает его таким же образом. Они создают таблицу указателей функций для каждого полиморфного типа (называемого виртуальная таблица или vtable), и когда вы вызываете виртуальный метод, "реальный" метод просматривается из таблицы vtable, и эта версия вызывается. Таким образом, вы можете визуализировать что-то вроде этого псевдокода:

struct BaseVTable
{
    int (*_method1) () = &Base::method1; // real function address
    int (*_method2) () = &Base::method2;
};

struct DerivedVTable
{  
    int (*method) () = &Derived::method1;
    int (*method2) () = &Base::method2; // not overridden
};

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


С другой стороны, мое понимание термина позднее связывание это то, что указатель функции ищется имя во время выполнения, из хэш-таблицы или что-то подобное. Это то, как все делается в Python, JavaScript и (если память служит) Objective-C. Это позволяет добавлять новые методы в класс во время, что не может быть сделано непосредственно в C++. Это особенно полезно для реализации таких вещей, как миксины. Однако недостатком является то, что поиск во время выполнения обычно значительно медленнее, чем даже виртуальный вызов в C++, и компилятор не может выполнять проверку типа времени компиляции для вновь добавленных методов.

Я полагаю, что смысл заключается в том,что у вас есть два класса B, C наследует один и тот же класс отца A. Таким образом, указатель отца (тип A) может содержать каждый из типов сыновей. Компилятор не может знать, что тип содержит в указателе в определенное время, потому что он может измениться во время выполнения программы.

есть специальные функции, чтобы определить, какой тип определенного объекта в определенный момент времени. как instanceof в java, или по if(typeid(b) == typeid(A))... в c++.

этой вопрос может помочь вам.

динамическая отправка обычно относится к нескольким отправкам.

рассмотрим приведенный ниже пример. Я надеюсь, что это может помочь вам.

    class Base2;
    class Derived2; //Derived2 class is child of Base2
class Base1 {
    public:
        virtual void function1 (Base2 *);
        virtual void function1 (Derived2 *);
}

class Derived1: public Base1 {
    public:
    //override.
    virtual void function1(Base2 *);
    virtual void function1(Derived2 *);
};

Рассмотрим пример ниже.

Derived1 * d = new Derived1;
Base2 * b = new Derived2;

//Now which function1 will be called.
d->function1(b);

он позвонит function1 С Base2* не Derived2*. Это связано с отсутствием динамической множественной отправки.

поздняя привязка является одним из механизмов реализации динамической однократной отправки.

В C++, как dynamic dispatch и late binding то же самое. В основном, значение одного объекта определяет часть кода, вызываемого во время выполнения. В таких языках, как C++ и java динамическая отправка-это более конкретно динамическая одиночная отправка, которая работает, как упоминалось выше. В этом случае, поскольку привязка происходит во время выполнения, она также называется late binding. Такие языки, как smalltalk, позволяют динамическую множественную отправку, в которой метод выполнения выбирается во время выполнения на основе идентификаторов или значений больше чем один объект.

В C++ у нас действительно нет поздней привязки, потому что информация о типе известна. Таким образом, в контексте C++ или Java динамическая отправка и поздняя привязка одинаковы. Фактическое / полностью позднее связывание, я думаю, находится в таких языках, как python, который является поиском на основе метода, а не на основе типа.