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

Почему sizeof (BaseClass) == sizeof (DerivedClass), хотя я добавляю член

Из кода ниже sizeof(Base) == 24 и sizeof(Derived) == 24.

Почему их размеры равны?

В классе Base у нас есть 3 члена, а в классе Derived есть еще один член.

class Base
{
private:
    double d;
protected:
    long l;
public:
    int i;
};

class Derived : public Base
{
private:
    float f;
};
4b9b3361

Ответ 1

Так получилось, что ваш класс Base имеет 8-байтовое требование выравнивания, но его последний член имеет размер 4. Это приводит к пустой области заполнения, добавленной в конце макета памяти Base. Это дополнительное дополнение играет свою роль, когда вы создаете объекты класса Base сами по себе, как так называемые наиболее производные объекты.

Base b; // <- a most-derived object
Base a[10]; // <- an array of most-derived objects

Однако, когда вы "внедряете" Base в качестве базового класса в класс Derived, нет необходимости в дополнительном дополнении в конце встроенного субъекта Base.

Derived d; // <- object `d` contains an embedded sub-object of type `Base`

Интеллектуальный компилятор попытается повторно использовать эту область, поместив дополнительное поле класса Derived в область макета, используемую для заполнения в Base. В вашем случае дополнительное поле Derived::f, кстати, имеет тот же размер 4 байта, то есть идеально вписывается в него. Конечным результатом является то, что общий размер класса не увеличивается.

Очень похожий (по природе) эффект - это так называемая "пустая оптимизация базы". В С++ sizeof для любого типа гарантированно будет больше 0, что означает, что sizeof пустого класса всегда больше нуля. Однако, когда вы получаете какой-либо другой класс из пустого базового класса, вы можете заметить, что базовый класс вносит ровно 0 байтов в размер производного класса. Например

struct A {};
struct B {};
struct C {};
struct D {};

struct F : A, B, C, D {
  int i;
}

int main() {
  std::cout << sizeof(A) << std::endl << sizeof(B) << std::endl << 
               sizeof(C) << std::endl << sizeof(D) << std::endl;
  std::cout << sizeof(F) << std::endl;
}

Несмотря на то, что sizeof для каждого базового класса больше нуля, sizeof(F) обычно будет оцениваться до sizeof(int), как будто подобъекты базового класса вообще не существуют.

Другими словами, как показывают такие примеры, субобъекты базового класса следуют значительно более расслабленным правилам в отношении их макета памяти, чем большинство производных объектов. Эти непринужденные правила могут легко привести к ситуациям, когда sizeof базового класса будет лишь частично способствовать sizeof производного класса.

Ответ 2

Поскольку у вас есть sizeof (double) == sizeof (long) == 8, и это часто означает, что alignof (double) равно 8. Это означает, что Base должен быть выровнен по размеру на границе 8 байтов, если он хранится в массиве, и он генерирует отступ в 4 байта в конце, Derived удаляет это, чтобы поместить f вместо.

Ответ 3

Используйте pahole, чтобы понять это:

class Base {
private:

    double                     d;                    /*     0     8 */
protected:

    long int                   l;                    /*     8     8 */
    int                        i;                    /*    16     4 */


    /* size: 24, cachelines: 1, members: 3 */
    /* padding: 4 */
    /* last cacheline: 24 bytes */
};
class Derived : public Base {
public:

    /* class Base                <ancestor>; */      /*     0    24 */

    /* XXX last struct has 4 bytes of padding */
private:

    /* Bitfield combined with next fields */

    float                      f;                    /*    20     4 */


    /* size: 24, cachelines: 1, members: 2 */
    /* paddings: 1, sum paddings: 4 */
    /* last cacheline: 24 bytes */
};

Ответ 4

Заполнение из-за необходимости выравнивания:

Хотя компилятор (или интерпретатор) обычно выделяет отдельные элементы данных на выровненных границах, структуры данных часто имеют членов с различными требованиями к выравниванию. Чтобы поддерживать правильное выравнивание переводчик обычно вставляет дополнительные неназванные элементы данных, поэтому что каждый элемент правильно выровнен. Кроме того, структура данных в целом может быть дополнен конечным неназванным членом. Это позволяет член массива структур, который должен быть правильно выровнен.

Подробнее об этом здесь:
http://en.wikipedia.org/wiki/Data_structure_alignment#Data_structure_padding