Nie mogę naprawdę zrozumieć, dlaczego kompilator nie nazwał funkcji instancji klasy?
To właśnie robi kompilator - zapewnia, że twój program wywoła funkcję klasy, do której należy instancja. Kluczowym słowem jest tutaj instancja: znajomość klasy instancji nie jest dostępna w czasie kompilacji.
Rozważmy prosty przykład:
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);
}
Uwaga najtrudniejsza linia: kompilator ma instancji, ale nie wiem klasę. Właściwie to zna klasę, ale nie najbardziej konkretną. Wiedza, którą kompilator ma w czasie kompilacji, jest wystarczająca, aby wiedzieć, że istnieje funkcja o nazwie howdy
, ale nie jest wystarczająca, aby zdecydować, która z kilku możliwości będzie tą, którą należy wywołać w środowisku wykonawczym.
To tutaj vtables przychodzi na ratunek: kompilator wie, że podklasy Dude
będą miały wskaźnik do ich funkcji howdy
osadzonej gdzieś w ich vtable. To wszystko, co muszą wiedzieć w czasie kompilacji! Wstawiają wywołanie wirtualne, które wyszukuje wskaźnik funkcji w czasie wykonywania, osiągając oczekiwane zachowanie (wymyślnym określeniem tego rodzaju zachowania jest "polimorfizm"). Oto demo of this program działający na idee.
Dlatego użycie vtable jest to, że gdy grupa pochodzi ponownie realizuje funkcję wirtualnego w konstruktora klasy pochodnej vtable jest aktualizowany, aby wskazać wykonania pochodzącego zamiast zasady. – Borgleader
Dwa słowa: * dynamiczna wysyłka *. Mimo że poniżej znajdują się dobre wyjaśnienia, musisz przynajmniej usłyszeć poprawną terminologię. –