TGViewer
Грокаем C++ Грокаем C++ @grokaemcpp · 9.35K subscribers
Post #253 3.53K
​Память виртуально наследованных классов

Как было сказано в предыдущей статье, «просто так эти области памяти не могут быть объединены...». Возможность совместного использования памяти для общего родительского класса есть!

С помощью ключевого слова virtual, объявляется виртуально наследованный класс:
struct Base                 {..};
struct P1 : virtual Base {..};
struct P2 : virtual Base {..};
struct Child : P2, P1 {..};

Таким образом мы сообщаем компилятору, что память родительского класса Base будет использоваться совместно классами P2 и P1, которые виртуально наследуются от него.

Стандарт языка не регламентирует реализацию виртуального наследования и виртуальных методов. Большинство компиляторов придерживаются спецификации Itanium C++ ABI (в частности, GCC и LLVM Clang). Однако, различия всё равно могут быть. Нам важно получить именно понимание, какие могут быть нюансы и как они могут быть решены.

Давайте сразу посмотрим, как это будет представлено в памяти. Рассмотрим живой пример 5:
struct Base                {..};
struct P1 : virtual Base {..};
struct P2 : virtual Base {..};
struct Child: P2, P1 {..};

Layout of Child:
0: [ Memory of struct P2 ] <- P2*, Child*
16: [ Memory of struct P1 ] <- P1*
32: [ Memory of struct Child ]
40: [ Memory of struct Base ] <- Child::Base*, P2::Base*, P1::Base*

Да, как и раньше при множественном наследовании, адресы P1 и P2 будут отличны друг от друга, но вот их родительский класс Base теперь вынесен отдельно и существует в единственном исполнении.

Мы тут же сталкиваемся с одной интересной проблемой. В рамках класса Child мы можем вычислить смещение до полей класса Base. Но сам по себе класс Child может быть далеко не единственным классом, который наследует P1 или P2. Я хочу сказать, что мы не можем просто "запомнить" это смещение и использовать его для других классов. Нам так же непонятно, как вычислить это смещение, если мы работаем абстрагировано с классом P1 или P2. Вдруг это самостоятельный объект, а может быть это родительский класс Child или Child2? Более того, сам Child может быть унаследован другими классами, что добавит новые поля и изменит итоговое смещение. Вообще говоря, эта информация может даже меняться во время выполнения программы. Вот тут и начинается веселье!

Как может решаться эта проблема? В большинстве случаев, во все классы, которые используют виртуальное наследование, неявно добавляется виртуальный указатель на виртуальную таблицу смещений. Она генерируется компилятором и хранится в read-only памяти приложения. Размер класса, естественным образом, увеличивается сразу же на размер указателя для выбранной архитектуры:
struct Base                {..};
struct P1 : virtual Base {..};

Layout of P1:
0 | (P1 vtable pointer) // + 8 байт
8 | uint64_t data_of_P1
16 | struct Base (virtual base)
16 | uint64_t data_of_Base

При обращении к полям виртуально наследованного класса, будет выполняться дополнительная операция чтения виртуального указателя vtable pointer для доступа к виртуальной таблице смещений. И вуаля, теперь мы уже знаем, где у нас лежит наша Base.

Как вы догадываетесь, за такие фокусы приходится платить тактами процессора. Это подтверждают результаты бенчмарка. Действительно, доступ к памяти виртуально наследованных классов будет работать медленнее.

В следующей статье мы поговорим о возможном способе оптимизации скорости доступа к памяти виртуально наследованных классов.

#cppcore #compiler
  • 👍 16
  • 🔥 10
  • ❤ 3
More from @grokaemcpp
  1. Oct 14, 2026​​Что внутри std::condition_variable? #опытным Более менее все знакомы с примитивом синхро…
  2. Oct 12, 2026​​WAT #новичкам Как вы думаете, результат выражения: bool math = -1 < 1U; будет правдой ил…
  3. Oct 8, 2026​​Strict weak ordering #опытным На первый взгляд, всё выглядит рабочим: мы создаём 40 зака…
  4. Oct 7, 2026​​Где-то баг... #опытным Вот вам код: struct Order { int price; int id; }; int main() { st…
  5. Oct 5, 2026Откуда spurious wakeup на кондваре? #опытным У кондваров есть метод std::condition_variabl…
  6. Oct 1, 2026​​Stacktrace. Tips #опытным Чтобы полноценно работать со стандартными трейсами, нужно знат…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →