2013-07-16 2 views
5

Я новичок в C++, и я только что узнал о виртуальных функциях.Принцип виртуальных функций в C++

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

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

Почему компилятор использует таблицу vtable? Не могли бы вы объяснить это?

+0

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

+5

Два слова: * динамическая отправка *. Несмотря на то, что ниже приведены хорошие объяснения, вам необходимо хотя бы услышать правильную терминологию. –

ответ

9

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

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

Рассмотрим простой пример:

struct Dude { 
    virtual void howdy() = 0; 
}; 
struct Bob : public Dude { 
    virtual void howdy() { cout << "Hi, Bob!" << endl; } 
}; 
struct Moe : public Dude { 
    virtual void howdy() { cout << "Hi, Moe!" << endl; } 
}; 
// Note the pass by reference below: passing by reference or by pointer, 
// but not by value, is important for achieving polymorphic behavior. 
void say_hi(Dude& dude) { 
    dude.howdy(); // <<== Here is the tricky line 
} 
int main(int argc, char* argv[]) { 
    Bob b; 
    Moe m; 
    Dude *d = rand() & 1 ? (Dude*)&b : &m; 
    say_hi(*d); 
} 

Примечание хитрая строки: компилятор имеет экземпляр, но он не знает класс. На самом деле, он знает класс, но не самый конкретный. Знания, которые компилятор имеет во время компиляции, достаточны, чтобы знать, что существует функция, называемая howdy, но недостаточно для определения того, какая из нескольких возможностей будет той, которую нужно вызвать во время выполнения.

Это то, где на помощь приходят vtables: компилятор знает, что в подклассах Dude будет указатель на их функцию howdy, встроенную где-то в их таблицу vtable. Это все, что им нужно знать во время компиляции! Они вставляют виртуальный вызов, который ищет указатель функции во время выполнения, достигая ожидаемого поведения (причудливым словом для такого поведения является «полиморфизм»). Вот demo of this program работает на ideone.

+0

Собственно, 'howdy' может быть' const', и вы могли бы передать ссылку 'const' на' say_hi' ... но я просто nitpicking. : P –

+1

@ DanielKamilKozar Конечно, это то, что я сделал бы в проблеме производства. Однако это не важно для понимания основных принципов полиморфизма, поэтому я лишил этот пример его минимальный минимум. – dasblinkenlight

+3

Ваш пример верный, но, возможно, не идеальный: я знаю, по крайней мере, один компилятор, который разрешит вызовы во время компиляции. Чем более классический 'Base * p; if (rand() & 1) p = new Derived1; else p = new Derived2; p-> do_something(); 'немного лучше, потому что он избегает этой« проблемы »и, на мой взгляд, в любом случае является более наглядным. –