2014-11-03 7 views
1

Я использую GCC в Linux.Что нужно сделать, чтобы увидеть статическую и динамическую привязку в действии? [C++]

Я хочу понять рабочие виртуальные функции.

Какой код C++ я должен писать, чтобы видеть и понимать, как именно статическое и динамическое связывание происходит с виртуальными функциями и без них?

И как «видеть», как они были окончательно связаны и что именно произошло во время процесса?

+1

Статические и динамические связи в основном ортогональны виртуальным и не виртуальным функциям. –

+0

@JonathanLeffler Я думаю, что он означает статическую или динамическую привязку *. – 5gon12eder

+0

@ 5gon12eder Есть ли разница между связыванием и привязкой? –

ответ

1

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

-1
class Base { 
public: 
    int Foo(); 
    virtual int Bar(); 
}; 

class D1 : public Base { 
public: 
    int Foo(); 
    virtual int Bar(); 
}; 

class D2 : public Base { 
public: 
    int Foo(); 
    virtual int Bar(); 
}; 

main() 
{ 
    Base * b = (rand() < 100) ? new D1 : new D2; 
    // Always calls Base::Foo() 
    b->Foo(); 
    // Call either D1::Bar() or D2::Bar() 
    b->Bar(); 
} 
+1

Вы не удаляете свой объект, и если бы вы сделали это, это было бы неправильно, потому что у вас нет виртуального деструктора. –

+0

@NeilKirk Спасибо за ввод, но вопрос был о привязке, а не о том, как написать полный класс. Добавление всех методов, которые класс должен был сделать, менее ясным. –

+1

Лучше иметь «менее ясный» ответ, который не поощряет плохие вещи. –