Может ли С++ увеличить размер производного класса, если нет новых переменных-членов по сравнению с базовым классом?

Предположим, что у меня есть базовый класс с некоторыми переменными-членами и нет виртуальных функций:

class Base {
   int member;
};

и производный класс, который выводится не виртуальным способом из Base и не имеет новых переменных-членов, а также нет виртуальных функций:

class Derived : Base {
};

Очевидно, что sizeof(Derived) не может быть меньше sizeof(Base).

Требуется, чтобы sizeof(Derived) был равен sizeof(Base)?

Ответ 1

Из 5.3.2 [expr.sizeof]

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

От 1.8 [intro.object]

Если это не бит-поле (9.6), наиболее производный объект должен иметь ненулевой размер и должен занимать один или несколько байтов хранения. Субъекты базового класса могут иметь нулевой размер. Объект типа POD (3.9) должен занимать смежные байты хранения.

и примечание:

Фактический размер подобъекта базового класса может быть меньше результата применения sizeof к подобъекту из-за виртуальных базовых классов и менее строгих требований дополнения к подобъектам базового класса.

Поместите их вместе, и я думаю, что это говорит вам, что у вас нет никаких гарантий относительно того, что sizeof может вам рассказать, кроме результата будет больше нуля. На самом деле, это даже не гарантирует, что sizeof(Derived) >= sizeof(Base)!

Ответ 2

Нет такого требования.

Единственная соответствующая часть языка, о которой я могу думать, состоит в том, что каждый объект, будь то полный или нет, и является ли он самым производным или нет, имеет идентификатор, который задается парой его адреса и его типом. Ср С++ 11 1.8/6:

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

Таким образом, как самый производный объект, так и базовый подобъект вашего примера должны иметь разные тождества.

Конечно, для компилятора было бы разумно дать как Base, так и Derived размер 1, но это необязательно. Было бы приемлемо, если бы размер Base имел размер 1729 и Derived имел размер 2875.

Ответ 3

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

Рассмотрим эту тривиальную программу на С++:

class A {};

class B : public A {
    int m_iInteger;
};

int _tmain(int argc, _TCHAR* argv[])
{
    printf("A: %d\r\n", sizeof(A));
    printf("B: %d\r\n", sizeof(B));
    printf("int: %d\r\n", sizeof(int));

    return 0;
}

Что вы ожидаете от вывода, если sizeof(int) равно 4? Возможно, что-то вроде:

A: 0
B: 4
int: 4

?

Мой компилятор - Embarcadero С++ Builder 2010 - дает результат:

A: 8
B: 8
int: 4

Другими словами, добавление дополнительного поля в производном классе не делает производный класс более крупным.

Существует некоторое представление о том, почему с помощью темы файла справки на опции совместимости пустой пустой базовый класс с нулевой длиной.

Обычно размер класса составляет по крайней мере один байт, даже если класс не определяет каких-либо элементов данных. Когда вы установите этот параметр, компилятор игнорирует этот неиспользуемый байт для макета памяти и общего размер любых производных классов; пустые базовые классы не потребляют пространство в производных классах. По умолчанию = False

Похоже, что размер класса с настройками компилятора по умолчанию для этого компилятора составляет 8 байтов, а не один, и на самом деле изменение этого параметра для этого примера кода не имеет эффекта.

Вы также можете найти эту статью о размерах базового класса и вышеописанной оптимизации. В нем обсуждается, почему классы должны иметь размер как минимум одного байта, что делает оптимизация, а также вводит представление функций-членов и т.д.:

Действительно, стандарт требует, чтобы размер объекта никогда не был 0; он также требует, чтобы в элементах данных производных объектов базовый класс должен появиться перед объявленными пользователем данными членами производного класса. Однако подобъект базового класса не рассматривается как полный объект. Поэтому его можно удалить базовый класс подобъектом из производного объекта без нарушения правил. В другими словами, в объекте t смещение S и x может перекрываться...

Пожалуйста, прочитайте статью для полного контекста этой цитаты.

Ответ 4

class Base {
 // int member;            I have just created an empty base class.
};

class Derived : Base {
};

Теперь gcc-компилятор даст "размеp > 0" для обоих объектов, созданных классами Base и Derived. gcc-компилятор просто обеспечивает наличие адреса для объекта пользователю.

  Note:Derived class would contain base class members, so obviously we can think of 
  sizeof(derived class) greater then sizeof(base class). But this depends on the compiler 
  if it allocates some extra space while defining the derived class. 

Мой нынешний gcc-компилятор показал, что размер объектов как Base, так и Derived одинаковый.