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

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

При использовании каждого из механизмов?

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

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

Ответ 1

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

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

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

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

Dim obj

- initialize object then..
obj.DoSomething()

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

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

Ответ 2

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

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

Ответ 3

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

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

и

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

Но они в основном равны на С++, вы можете динамически отправлять виртуальные функции и vtables.

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

Ответ 4

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

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

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

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

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

Ответ 5

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

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

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

диспетчерское управление на соответствие по параметру

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

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

Ответ 6

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

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

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

(this->*mptr)();

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

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

Ответ 7

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

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

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

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

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

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

Ответ 8

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

Существуют специальные функции для определения типа определенного объекта за определенное время. например instanceof в java или if(typeid(b) == typeid(A))... в С++.

Ответ 9

Этот question может вам помочь.

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

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

    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* not Derived2*. Это связано с отсутствием динамической многократной отправки.

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

Ответ 10

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

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

Ответ 11

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

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, потому что метод был динамически отправлен. Стандарт С++ очень осторожен, чтобы не указывать, как именно это происходит за кулисами, но каждый компилятор под солнцем делает это одинаково. Они создают таблицу указателей функций для каждого полиморфного типа (называемую виртуальной таблицей или 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 другой функции. Звонки на виртуальные функции немного медленнее, чем не виртуальные вызовы, из-за дополнительного шага косвенности.


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