Подтвердить что ты не робот

Как объяснить этот результат sizeof при наследовании из пустого класса

Предположим, что у меня есть следующие определения структур С++.

struct Empty {};
struct Tmp : public Empty { int x; };
struct Tmp2 : public Empty { Tmp tmp;};
struct Tmp3 { Tmp tmp; };

Ожидается sizeof(Tmp3) == sizeof(Tmp) == 4. Однако sizeof(Tmp2) == 8, я не могу придумать, почему...? Не могли бы вы объяснить? Спасибо!

4b9b3361

Ответ 1

В случае Tmp разрешена пустая оптимизация базового класса (EBCO), и ваш компилятор ее применяет.

В случае Tmp2, EBCO отключен, поскольку элемент данных Tmp2::tmp также является Empty. Это связано с тем, что Tmp2 имеет два под-объекта одного типа (под-объект базового класса и его первый элемент данных). Они должны иметь разные адреса *. В противном случае было бы невозможно отличить одно от другого.

Это означает, что базовый класс вносит вклад в размер производного класса.


* От 1.8 Объектная модель С++: "Если объект не является бибовым или субобъектом базового класса нулевого размера, адрес этого объекта является адресом первого байта, который он занимает. Два объекта, которые не являются битовыми полями, могут иметь один и тот же адрес, если один является подобъектом другого или если хотя бы один является подобъектом базового класса нулевого размера, и они имеют разные типы, в противном случае они должны имеют разные адреса."

Ответ 2

Правило состоит в том, что два разных объекта одного и того же типа должны иметь разные адреса, что также является причиной того, что пустой объект по-прежнему имеет положительный размер. Так как это относится только к объектам того же типа, два разных типа могут совместно использовать хранилище, которое является основой "Пустой оптимизации базового слова".

Теперь ваш Tmp содержит (как базовый класс) подобъект Empty. Кроме того, Tmp2 имеет два таких подобъекта, один через свой базовый класс, другой - как простой элемент. Кроме того, он имеет int. По этой причине вам нужно больше места, чем только для int, т.е. sizeof (int) + 1. Вероятно, из-за заполнения, он заканчивается восемью байтами.

BTW: рассмотрим Tmp4, который наследует от Tmp, а также содержит Tmp. Вероятно, этот размер будет иметь тот же размер, что и Tmp2.

Ответ 3

Это ужасный нестандартный хак... но если по какой-то причине вы хотите sizeof(Tmp2) == 4, есть способ, который работает во всех компиляторах, которые я тестировал.

struct EvilEmpty
{
    char evil[0];
};

Предоставляя незаконный (но широко принятый в качестве расширения) массив нулевого размера в качестве члена, он больше не считается пустым, а компилятор обманом не понимает, что он может незаконно размещать два разных объекта с одинаковыми класс по тому же адресу. (Обратите внимание, что это меньше, чем пустой пустой класс.)

Очевидно, что это подвержено всем проблемам, которые привели к тому, что как стандарты C, так и С++ запрещают в первую очередь объекты с нулевым размером.