TGViewer
Грокаем C++ Грокаем C++ @grokaemcpp · 9.35K subscribers
Post #1039 3.1K
WAT
#опытным

Спасибо, @Ivaneo, за любезно предоставленный примерчик в рамках рубрики #ЧЗХ.

Выравнивание и так сбивает с толку молодых работяг. Поди разберись, как там компилятор добавляет паддинги. Но вот вам совсем странный пример. Две одинаковые структуры, одна с приватными полями, а другая с публичными:

class A1
{
private: // THIS IS PRIVATE
int a;
short s;
int b;
char d;
};

class A2
{
public: // THIS IS PUBLIC
int a;
short s;
int b;
char d;
};


Дальше возьмем, отнаследуемся от них и добавим в наследники чар поле:

class B1 : public A1
{
public:
char c;
};


class B2 : public A2
{
public:
char c;
};


Какими будут размеры объектов классов B1 и B2?

std::cout << sizeof(B1) << std::endl;
std::cout << sizeof(B2) << std::endl;


Можно подумать, что если набор полей и их порядок одинаковый, то размер будет одинаковый.

Но нет!

Вывод будет такой:

16
20


Убедитесь сами.

WAT? Куда делись 4 байта?

Начнем с того, что размер структур A1 и А2 одинаковый и равен 16 байтам. Компилятор в обоих случаях вставил 2 байта паддинга после short переменной и 3 после char.

Теперь давайте посмотрим на смещение поля c в наследниках относительно начала объекта. Это можно сделать с помощью трюка из этого поста:

std::cout << std::dec << std::bit_cast<std::uintptr_t>(&B1::c) << "\n";
std::cout << std::dec << std::bit_cast<std::uintptr_t>(&B2::c) << "\n";


Вывод:

13
16


Видно, что смещение поля c относительно начала объекта для В1 и В2 разное. Для В2, в котором поля публичные, ожидаемо c начинается с 16-го байта. А вот для В1, где поля приватные, c начинается ровно там, где заканчивается char поле d.

Конечно, стандартом не регламентируется выравнивание между полями базового класса и наследника. Уменьшение размера - это оптимизация компилятора.

Ключевую роль здесь играет понятие POD в терминах старого стандарта C++03, которое до сих пор влияет на решение, можно ли использовать хвостовое заполнение (tail padding) базового класса для размещения новых членов производного класса.

Хвостовое заполение - это те самые неиспользуемые байты, которые компилятор добавляет в конец объекта для удовлетворения требований выравнивания.

POD тип - массив или класс, не имеющий пользовательских конструкторов, приватных/защищённых нестатических данных, виртуальных функций, базовых классов и обладающий тривиальными копирующими операциями и деструктором.

Для pod типов нельзя оптимизировать хвостовое заполнение для сохранение ABI. Для не pod типов - можно.

Получается, что A1 - не pod тип: компилятор оптимизировал хвостовой паддинг и поместил поле c внутрь него., сэкономив место. Но A2 - pod тип, поэтому хвостовое заполнение остается.

Align yourself. Stay cool.

#optimization #compiler
  • ❤ 25
  • 🔥 13
  • 👍 11
  • 😱 4
  • 👏 2
  • ❤‍🔥 1
More from @grokaemcpp
  1. Oct 12, 2026​​WAT #новичкам Как вы думаете, результат выражения: bool math = -1 < 1U; будет правдой ил…
  2. Oct 8, 2026​​Strict weak ordering #опытным На первый взгляд, всё выглядит рабочим: мы создаём 40 зака…
  3. Oct 7, 2026​​Где-то баг... #опытным Вот вам код: struct Order { int price; int id; }; int main() { st…
  4. Oct 5, 2026Откуда spurious wakeup на кондваре? #опытным У кондваров есть метод std::condition_variabl…
  5. Oct 1, 2026​​Stacktrace. Tips #опытным Чтобы полноценно работать со стандартными трейсами, нужно знат…
  6. Sep 28, 2026​​Stacktrace #опытным Одна из проблема исключений - непонятно, откуда оно прилетело. Ну да…
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 →