Предположим, у нас есть такой код:
struct Granny {
int g;
};
struct Mom : virtual Granny {
int m;
};
struct Son : Mom {
int s;
};
int main() {
int x;
std::cin >> x;
Mom* mom = (x ? new Son : new Mom);
Granny* granny = static_cast<Granny*>(mom);
}
Классы не полиморфны, поэтому у Granny нет виртуальной таблицы. Теперь, в зависимости от x, под указателем Mom может быть разное расположение памяти. Таким образом, подобъект Granny будет сдвинут от начала объекта либо на 16, либо на 12 байт.
Вопрос: статический_cast вместо «простого указателя сдвига» скомпилирован в «разыменование vptr(vtable) в объекте Mom, затем просмотрите какой-то индекс, чтобы найти виртуальное смещение, а затем, наконец, сместите указатель на это смещение»? Итак, будет ли static_cast иметь две дополнительные операции во время выполнения? Если нет, пожалуйста, объясните, что происходит.
Кажется, тебя волнует производительность. Тогда вам нужно учитывать, что ваш код ничего не делает с granny
и что приведение типов не должно ничего делать.
оптимизаторы - звери. Невозможно предугадать, что они сделают. Но если человек видит способ транслировать ваш код таким образом, чтобы приведение требовало 0 операций, то компилятор наверняка тоже это увидит. Также обратите внимание, что vtable — это деталь реализации. На самом деле ничто не требует от компилятора каких-либо действий с vtable или vptr.
@TedLyngmo Я понимаю, что у бабушки или мамы должен быть виртуальный деструктор. Я думаю, что цель моего вопроса — static_cast и что он делает в описанном случае. Поэтому я решил не загромождать текст и код ненужными вещами. Что касается «простого сдвига указателя», я думаю, что без виртуального наследования бабушка, мама и сын будут размещаться один за другим, поэтому static_cast в ассемблере будет просто «сдвинуть указатель мамы на x байт назад или вперед (в этом случае 0 байт)»
@ 463035818_is_not_an_ai "похоже, тебя заботит производительность". Моя цель — понять поведение static_cast, а не найти наиболее оптимизированный способ сделать это. «Оптимизаторы — звери». Здесь я полагаю, что компилятор не может выполнять никаких оптимизаций. «vtable — это деталь реализации» Да, но теперь я полагаюсь на свои общие знания о виртуальных таблицах и наследовании. Поэтому я могу не понимать, как это работает (в таком случае, пожалуйста, объясните мне)
тогда vtable и смещения еще менее актуальны. Поведение заключается в том, что указатель, полученный в результате приведения, указывает на подобъект Granny
объекта. vtable и vptr — это не «поведение», а детали реализации, на которые нельзя полагаться.
не поймите меня неправильно. Я понимаю, откуда вы, но, по моему мнению, важно понимать ограниченность ответа, который вы ищете.
@ 463035818_is_not_an_ai Я понимаю, что это действительно абстрактный вопрос из-за реализации виртуальных таблиц, а виртуальное наследование вообще не стандартизировано. Но меня не устраивает, что «результат static_cast является указателем на Granny», я хочу знать его механизм и более подробно, что происходит под капотом, даже с impl. определенные виртуальные таблицы и т. д.
да, это я понимаю. Я хочу сказать, что то, что вы видите реализованным без оптимизаций, практически не имеет ничего общего с реализацией, как только вы их включите.
Абсолютно согласен. Спасибо!
Вы можете просто бросить оба случая в проводник компилятора:
Granny* from_Son(Son* x) { return static_cast<Granny *>(x); }
Granny* from_Mom(Mom* x) { return static_cast<Granny *>(x); }
компилируется в следующие три инструкции (с проверкой ошибок):
mov rax, QWORD PTR [rdi]
add rdi, QWORD PTR [rax-24]
mov rax, rdi
В этом фрагменте rdi
— это x
. [rdi]
— это ваша виртуальная таблица, и смещение до Granny
, таким образом, сохраняется за 24 байта до виртуальной таблицы.
Спасибо! Наверное, мне стоит выучить базовый ассемблер, чтобы проверить его самостоятельно и не задавать здесь такие вопросы.
Как вы планируете позже удалить объект, указанный
mom
? Еще одинif
с приведением обратно кSon
, если понадобится? Если да, то почему бы не использовать полиморфизм? Откуда берутся кавычки «просто сдвинуть указатель» и т. д.? Я не знаю, что это значит.