Что такое "Thunk" в контексте виртуальных функций?(Как работает?)
В некоторых реализациях термин "thunk" означает "промежуточную вызывалку" для виртуальной функции. "Промежуточная вызывалка" может быть нужна тогда, когда для правильного вызова виртуального метода необходимо предварительно вычислить корректное значение указателя this
, но способ этого вычисления зависит от контекста времени выполнения. Thunk также может использоваться для реализации корректной функциональности указателей на методы в ситуациях, когда такие указатели указывают на виртуальные методы.
Например
#include <iostream>
struct Base
{
virtual void foo() { std::cout << "base" << std::endl; }
};
struct Derived : Base
{
virtual void foo() { std::cout << "derived" << std::endl; }
};
int main()
{
void (Base::*ptr)() = &Base::foo; // 1
Base b;
(b.*ptr)(); // 2
Derived d;
(d.*ptr)(); // 3
}
В этом коде указатель ptr
вроде бы "привязывается" к функции Base::foo
в точке 1. Однако в ситуациях, когда Base::foo
является виртуальной функцией, спецификация языка требует, чтобы решение о конкретной функции для вызова принималось не в точке инициализации (точка 1), а в точке вызова (то есть в точках 2 и 3) на основе анализа динамического типа объекта. В точке 2 должна быть вызвана Base::foo
, а в точке 3 - Derived::foo
.
Поэтому компилятор не может в точке 1 заставить указатель ptr
указывать напрямую на Base::foo
. Он вынужден использовать какой-то промежуточный код, который уже на основе анализа динамического типа объекта в точках 2 и 3 примет решение о том, какую же функцию надо вызвать в этом месте.
Одним из вариантов реализации такого механизма является заведение в классе Base
скрытого невиртуального метода-"вызывалки", который внутри себя делает обычный виртуальный вызов метода foo
. Указатель ptr
ставится именно на этот скрытый метод
struct Base
{
virtual void foo() { std::cout << "base" << std::endl; }
void foo_thunk() { foo(); }
};
...
void (Base::*ptr)() = &Base::foo;
// на самом деле транслируется в
void (Base::*ptr)() = &Base::foo_thunk;
В результате мы получаем требуемое поведение при вызовах через ptr
в точках 2 и 3. Т.е. в обоих случаях сначала вызывается именно Base::foo_thunk
, а уже оттуда через обычный виртуальный механизм управление попадает в правильное foo
.
Вот этот скрытый метод foo_thunk
- это и есть так называемый "thunk". Таким способом реализует вызовы через указатели на виртуальные методы компилятор MSVC++. Компилятор GCC при компиляции вышеприведенного простейшего примера будет использовать иной подход (без thunk), но стоит ввести в пример множественное наследование, как в сгенерированном GCC коде тоже появятся thunks.
В том числе именно для поддержки такого подхода к реализации указателей на методы стандарт языка С++ говорит, что результат сравнения двух указателей на методы не специфицирован, если хотя бы один из них указывает на виртуальный метод. Дело в том, что на низком уровне один и тот же thunk может переиспользоваться для обслуживания совершенно разных виртуальных методов. Т.е. устанавливая указатель на разные виртуальные методы вы можете тем не менее получить одно и то же физическое значение в указателе (указатель на один и тот же thunk).
Виртуальный выделенный сервер (VDS) становится отличным выбором
Я начинающий в программированииЧитая книгу о С++ в главе посвященной указателям (в частности оператору delete), я наткнулся на то, что в среде...
У меня на странице несколько ViewНужно чтобы каждый анимировался после предыдущей