Вам знакомо понятие «выравнивание»?
В зависимости от модели, процессор либо вообще не умеет читать невыравненные данные (ex: попытка чтения четырех байтового слова по адресу, не кратному 4 приводит к аппаратному исключению) либо делает это очень медленно.
Атомарные операции также работают только с выравненными данными.
Таким образом, поле типа long должно быть выравнено на границу 8 байт. Так как объекты могут располагаться в массивах подряд друг за дружкой, размер объекта также должен быть кратен 8. В общем случае — необходима кратность максимальному выравниванию среди полей. В результате получается следующий расклад: 8 байт long, 4 байт int, 4 байт паддинга. Если выравнивание на 8 байт не нужно (отсутствует long поле), то необходимости «подгонять» размер объекта тоже нет, и паддинга не возникает.
Теперь самое интересное — почему есть эффект от пустого конструктора?
Снова обратимся к теории. В C++ есть понятие
POD типа. Можно сказать, это такая декларация, для которой гарантируется совместимость с Си. Для структур в языке Си непосредственно в стандарте прописаны правила «раскладки» полей в памяти, паддинги и все такое. До тех пор, пока Point не имеет пользовательского конструктора, он является POD, и следовательно должен иметь в конце «неприкосновенный» padding.
Напротив, для не-POD типов стандарт не фиксирует представление в памяти. Например классы вполне законно представлять хоть хеш-таблицей, именно поэтому в C++ запрещено использование
offsetof для полей класса. Поэтому компилятор вполне вправе творчески переиспользовать padding в объете Point для полей Point3D. Замечу, на другом компиляторе вы могли получить другой результат, и это было бы все равно ок с точки зрения языка C++.
Что любопытно, объявления с ключевым словом class все еще могут быть POD-типами. Классы и структуры перестают быть POD типами наприемр если есть наследование или пользовательские конструкторы или виртуальные функции.