указатели — Двойная косвенность в C ++ vtables

Я написал эту очень простую программу на C ++, и мне было интересно, почему компилятор размещает vtable через две разыменования указателей. Вот программа на C ++:

class Foo {
public:
virtual void bar() {
}
};

int main(int argc, char *arv[]) {
Foo foo;
Foo *foo_p(&foo);
foo_p->bar();
}

Теперь я могу посмотреть на сборку, сгенерированную компилятором:

$ g++ -ggdb -Wall -O0 -S test.cpp

Вот соответствующие разделы:

    .loc 1 9 0
leaq    -16(%rbp), %rax  # put the address of 'foo' in %rax
movq    %rax, %rdi       # use it as the first argument of the following function
call    _ZN3FooC1Ev      # call the Foo constructor
.loc 1 10 0
leaq    -16(%rbp), %rax  # put the address of 'foo' in %rax
movq    %rax, -24(%rbp)  # create 'foo_p' on the stack
.loc 1 11 0
movq    -24(%rbp), %rax  # load 'foo_p' into %rax
movq    (%rax), %rax     # dereference the pointer, put it in %rax
# %rax now holds the hidden pointer in 'foo', which is the vtable pointer
movq    (%rax), %rdx     # dereference the pointer ::again:: (with an offset of 0), put it in %rdx
# %rdx now holds a function pointer from the vtable
movq    -24(%rbp), %rax  # create the 'this' pointer (== foo_p) and put it in %rax
movq    %rax, %rdi       # use the 'this' pointer as the first argument to the following function
call    *%rdx            # call Foo::bar (via the vtable)

Зачем нужен разыменование второго указателя? Почему «скрытый» указатель vtable в объекте не указывает непосредственно на vtable?

edit: It :: is ::, указывающий непосредственно на vtable. Я просто запутался с моими указателями 😛

4

Решение

movq    -24(%rbp), %rax  # load 'foo_p' into %rax
movq    (%rax), %rax     # fetch VTABLE
movq    (%rax), %rdx     # fetch function `bar` from VTABLE.

Вы бы лучше увидели, если бы добавили baz (или же kerflunk) в качестве второй функции для вашего класса, вы увидите, что вторая выборка будет в 8 в VTABLE.

Вы можете увидеть структуру внутри класса примерно так (обратите внимание, что это «для иллюстрации, а не для реальности»)

struct VTABLE
{
void (*bar)();
};

struct Foo
{
VTABLE *vtable;
};

Внутри конструктора для Foo [который существует, даже если вы его не объявили], есть фрагмент кода, который делает:

this->vtable = &VTABLE_Foo;

и где-то компилятор сделал (опять же, для иллюстрации, имена, конечно, разные):

VTABLE VTABLE_Foo = { foo::bar };

Так называть barмы бы сделали:

foo_p->vtable->bar(foo_p);

Что показывает компилятор:

void (*temp)() = foo_p->vtable->bar;
temp(foo_p);

Это почти наверняка является следствием использования -O0и, если вы компилируете с оптимизацией, компилятор сделает это более прямо (включая, возможно, понимание, что в этом случае ему не нужен vtable, и встроит функцию, которая ничего не делает, таким образом полностью исключая вызов).

6

Другие решения

Первый ставит виртуальные таблицы указатель на %raxа второй ставит функция указатель на %rdx,

2

По вопросам рекламы [email protected]