C++: прототип виртуального указателя
Я не уверен, документировано ли это где-нибудь. Все мы знаем, что в случае виртуальных функций каждый класс содержит vptr, указатель на массив указателей функций, называемый виртуальной таблицей. Я хочу знать, что является прототипом vptr. Например, если класс объявлен следующим образом,
class A
{
int a;
public: A(){}
virtual void display();
virtual void setValue(int x);
};
Теперь у нас есть два указателя на функцию в vtable класса A. Как один vptr способен на два определения разных прототипов?
Пожалуйста, дайте мне знать, если мое понимание неверно.
Спасибо! Рахул.
3 ответа
Vptr - это деталь реализации, и поэтому он не имеет прототипа.
Виртуальная таблица (в реализациях, использующих такую вещь) является продуктом "магии компилятора". Ему не нужно иметь конкретный прототип, потому что никакой код C++ никогда не использует его напрямую. Вместо этого компилятор генерирует его по своему усмотрению, чтобы соответствовать каждому классу, который нуждается в нем. Компилятор также генерирует код для доступа к каждому элементу, поэтому он может гарантировать доступ к каждому элементу безопасным для типов способом.
Например, компилятор знает, что слот для A::setValue
Метод содержит указатель на функцию, которая соответствует setValue
подпись, потому что компилятор - тот, который поместил это туда во-первых. Кроме того, единственным кодом, который непосредственно обращается к этому слоту, является машинный код, сгенерированный компилятором, и перед созданием такого кода компилятор уже подтвердил, что исходный код C++ вызывал setValue
функция. Таким образом, не беспокойтесь о том, что setValue
слот может содержать что-либо, кроме setValue
указатель на функцию Также нет никаких опасений, что вместо этого можно получить доступ к другому слоту; если это произойдет, это будет ошибка компилятора, а не то, что произойдет в результате обычного пользовательского кода.
Элементы таблицы никогда не рассматриваются как группа, поэтому не требуется, чтобы все они имели одинаковый тип. В лучшем случае все они имеют тип "общего указателя или смещения, подходящего для перехода к процессору". Так как на самом деле это не C++, "тип" не должен соответствовать какому-либо конкретному типу C++.
Как отметил Оли Чарльзуорт, виртуальные указатели являются деталями реализации, поэтому этот вопрос не имеет смысла с точки зрения C++. Тем не менее, следующая ручная реализация (некоторые функции) виртуальных функций может быть полезна для вашего понимания:
struct vtable {
void (*display)(void*);
void (*setValue)(void*, int);
};
void A_display(void *this_) { /*Cast this_ to A* and do A stuff*/ }
void A_setValue(void *this_, int x) { /*Cast this_ to A* and do A stuff*/ }
vtable A_vtable = {A_display, A_setValue};
struct A {
vtable *vptr = &A_vtable;
int a;
public: A(){}
};
void B_display(void *this_) { /*Cast this_ to B* and do B stuff*/ }
void B_setValue(void *this_, int x) { /*Cast this_ to B* and do B stuff*/ }
vtable B_vtable = {B_display, B_setValue};
struct B {
vtable *vptr = &B_vtable;
int a;
public: B(){}
};
void display(void *obj) {
((*static_cast<vtable**>(obj))->display)(obj);
}
void setValue(void *obj, int) {
((*static_cast<vtable**>(obj))->setValue)(obj, int);
}
Конечно, это дает лишь небольшое подмножество возможностей виртуальных функций, но должно быть довольно просто увидеть, что vptrs
указывают на коллекции указателей на функции с фиксированными типами.