Вот пример. Вы можете создать и запустить этот код с помощью функции, установленной как виртуальная функция, или нет. Чтобы получить виртуальное поведение, динамическую отправку, динамическое связывание, постройте его с помощью макроса препроцессора IS_VIRTUAL
. Чтобы увидеть статическую привязку, создайте ее, не определяя этот макрос.
#include <iostream>
#if defined(IS_VIRTUAL)
#define CONDITIONAL_VIRTUAL virtual
#else
#define CONDITIONAL_VIRTUAL
#endif
struct A {
CONDITIONAL_VIRTUAL void foo() { std::cout << "A\n"; }
};
struct B : A {
CONDITIONAL_VIRTUAL void foo() { std::cout << "B\n"; }
};
// global objects
A a; B b;
enum object_type { get_A, get_B };
A *get_object(object_type t) {
switch (t) {
case get_A: return &a;
case get_B: return &b;
}
}
int main() {
std::cout << "Choose A or B: ";
char c;
std::cin >> c;
A *x = get_object(c == 'A' ? get_A : get_B);
x->foo();
}
Связывание имеет отношение к оценке x->foo()
. Компилятор должен выяснить, какой код выполнить для этого выражения. С статическим и динамическим привязкой компилятор смотрит на x
и видит его тип A*
, поэтому он смотрит на struct A
и ищет объявление foo()
.
Со статической привязкой компилятор обнаружил, что foo()
не virtual
, поэтому компилятор просто идет вперед и генерирует код, вызывающий этот метод foo()
. Просто.
С динамическим связыванием, компилятор видит, что метод, помеченный как virtual
, и поэтому компилятор вместо этого генерирует код, который, во время выполнения, использовать таблицу указателей функций, связанные с объектом x
для выбора способа вызова, а затем вызов любого метода. Компилятор также генерирует код в другом месте для создания таблиц для глобальных объектов a
и b
. Для глобального объекта a
он указывает, что таблица указывает на A::foo()
, а для глобального b
он делает таблицу точкой B::foo()
. Поэтому, если x
указывает на объект b
, тогда поиск таблицы приведет к B::foo()
, и это функция, которая будет вызвана.
В общем, компилятор должен убедиться, что все объекты, имеющие виртуальные методы, также имеют таблицу с ними, которая указывает на нужные функции для вызова, так что в любой момент, когда виртуальный вызов выполняется на объекте, во время выполнения, получите таблицу, связанную с объектом, и найдите правильный метод для вызова.
Итак, создайте вышеуказанную программу как в статическом, так и в динамическом режимах, а затем запустите ее и наблюдайте за выходом, который вы получаете для каждого входа. Заполните приведенную ниже таблицу с помощью вывода, который вы получите для каждой комбинации ввода и типа привязки.
Binding | static dynamic
Input
-----
A ? ?
B ? ?
Во всех случаях на выходе получают путем оценки одного и того же вызова метода x->foo()
. В каких случаях это динамическая привязка к доказательствам? Соответствует ли это вашему пониманию приведенного выше объяснения динамической привязки?
Статические и динамические связи в основном ортогональны виртуальным и не виртуальным функциям. –
@JonathanLeffler Я думаю, что он означает статическую или динамическую привязку *. – 5gon12eder
@ 5gon12eder Есть ли разница между связыванием и привязкой? –