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 указывают на коллекции указателей на функции с фиксированными типами.

Другие вопросы по тегам