Какой размер у пустого класса в C++?

Ответ

Размер объекта пустого класса в C++ не может быть нулевым. Согласно стандарту, каждый объект должен иметь уникальный адрес в памяти, поэтому компилятор выделяет как минимум 1 байт.

Демонстрация:

#include <iostream>

class TrulyEmpty {}; // Пустой класс

int main() {
    TrulyEmpty e1, e2;
    std::cout << "Size of TrulyEmpty: " << sizeof(TrulyEmpty) << " byte(s)" << std::endl; // Вывод: 1
    std::cout << "Address of e1: " << &e1 << std::endl;
    std::cout << "Address of e2: " << &e2 << std::endl; // Адреса будут различаться
    return 0;
}

Важная оптимизация: Empty Base Optimization (EBO). Когда пустой класс используется как базовый, компилятор может исключить его из размера производного класса, чтобы избежать накладных расходов.

class EmptyBase {};
class Derived : public EmptyBase {
    int value;
};
// Без EBO: sizeof(Derived) могло бы быть sizeof(int) + 1 (плюс выравнивание).
// С EBO: sizeof(Derived) часто равен просто sizeof(int) (например, 4 байта).

Эта оптимизация активно используется в STL (например, std::allocator часто является пустым базовым классом). На практике я учитываю это при проектировании полиморфных иерархий или политик (policy-based design).

Ответ 18+ 🔞

Слушай, вот это да, классика жанра! Каждый раз, когда кто-то про размер пустого класса спрашивает, у меня в голове сразу: «Ну ёпта, опять про этот один байт». Но не всё так просто, как кажется, тут целая философия, ей-богу.

Представь себе: ты создал класс, в нём нихуя нет. Ни полей, ни виртуальных функций — пустота, вакуум. И думаешь: «Раз пустой, значит, и места в памяти не занимает, да похуй». Ан нет! Стандарт C++ — хитрая жопа, он говорит: каждый объект должен иметь свой уникальный адрес. Чтобы адреса были разные, нужно хоть что-то, хоть один байтик. Поэтому компилятор, такой добрый, выделяет ему целый 1 байт. Не ноль, а именно один. Вот тебе и волнение ебать.

Смотри, как это выглядит в коде:

#include <iostream>

class TrulyEmpty {}; // Класс, который реально пустой

int main() {
    TrulyEmpty e1, e2;
    std::cout << "Size of TrulyEmpty: " << sizeof(TrulyEmpty) << " byte(s)" << std::endl; // Вывод: 1
    std::cout << "Address of e1: " << &e1 << std::endl;
    std::cout << "Address of e2: " << &e2 << std::endl; // Адреса будут различаться
    return 0;
}

Запустишь — увидишь, что адреса у e1 и e2 разные, а размер — один байт. Всё логично, но на первый взгляд кажется, будто память тратится впустую. Терпения ноль ебать, когда такое видишь в первый раз.

Но тут C++ выкидывает свой главный финт ушами — Empty Base Optimization (EBO). Это когда пустой класс используется не как член, а как базовый класс. И тут компилятор, если он не совсем мудак, может сказать: «А зачем мне выделять под него место отдельно? Давайте я его, этого пустого родителя, просто в производный класс встрою, как призрака». И размер производного класса может стать равен размеру его нестатических полей, без лишнего байта.

class EmptyBase {};
class Derived : public EmptyBase {
    int value;
};
// Без EBO: sizeof(Derived) могло бы быть sizeof(int) + 1 (плюс выравнивание).
// С EBO: sizeof(Derived) часто равен просто sizeof(int) (например, 4 байта).

Вот это уже овердохуища полезная штука! В STL, например, эту оптимизацию любят и уважают. Тот же std::allocator часто сидит в классах как пустой базовый, и никто даже не замечает. Когда проектируешь что-то на policy-based design, про EBO забывать нельзя — иначе получишь манду с ушами из-за раздутых объектов.

Так что запомни: пустой класс — это не ноль байт, это один байт. Но если использовать его с умом (как базовый), то можно этот байт сэкономить. C++ — он всегда такой, подкинет задачку на сообразительность. Сам от себя охуеешь, когда в десятый раз на это смотришь.