Влияние наличия конструктора на расположение элементов внутри класса?

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

Рассмотрим такой код (запускается на 64-ех битной системе):

#include <stdio.h>

class Point
{
public:
    // long x; // (1)
    int a;
    // Point() { } // (2)

};

class Point3D : public Point
{
public:
    int b;
};

int main()
{
    printf("%d %d\n", (int)sizeof(Point), (int)sizeof(Point3D));
    return 0;
}



Обратите внимание на закоментированные строки (1) и (2). В таблице ниже результат работы в зависимости от того, какие из них раскомментированы:

Что раскомментироватьВывод
4 8
(1)16 24
(2)4 8
(1), (2)16 16


(результаты для g++ и clang++)


Вопросы:

1. Если первая строка закоментирована, то размер класса А всего четыре байта. Почему в этом случае нет padding? Если добавить восьмибайтное поле (раскомментировать (1)), то размер становится 16 байт, то есть padding появляется.

2. Почему когда padding есть, наличие конструктора влияет на то, как располагается поле класса Point3D? Без консутрктора ((2) закомментирована) оно размещается после padding'а класса Point, а с конструктором ((2) раскомментирована) — внутри padding.
  • Вопрос задан
  • 3283 просмотра
Решения вопроса 1
mejedi
@mejedi
Вам знакомо понятие «выравнивание»?

В зависимости от модели, процессор либо вообще не умеет читать невыравненные данные (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 типами наприемр если есть наследование или пользовательские конструкторы или виртуальные функции.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы