C ++: Каков размер объекта пустого класса?


111

Мне было интересно, каков может быть размер объекта пустого класса . Конечно, это не может быть 0 байтов, так как на него должна быть возможность ссылаться и указывать на него, как на любой другой объект. Но насколько велик такой объект?

Я использовал эту небольшую программу:

#include <iostream>
using namespace std;

class Empty {};

int main()
{
    Empty e;
    cerr << sizeof(e) << endl;
    return 0;
}

Результат, который я получил на компиляторах Visual C ++ и Cygwin-g ++, составлял 1 байт ! Для меня это было немного удивительно, поскольку я ожидал, что он будет размером с машинное слово (32 бита или 4 байта).

Кто-нибудь может объяснить, почему размер 1 байт? Почему не 4 байта? Это тоже зависит от компилятора или машины? Кроме того, может ли кто-нибудь дать более убедительную причину, почему пустой объект класса не будет иметь размер 0 байтов?


Я не вижу причин, по которым он не может быть нулевым. Но, задав ему размер, другие вещи в компиляторе проще. Если у вас есть массив этих вещей, тогда каждому элементу нужен уникальный адрес. Размер 1 упрощает это.
Мартин Йорк,

2
Он может быть нулевого размера, если это подобъект базового класса.
Йоханнес Шауб - лит

Ответы:


129

Цитируя FAQ Бьярна Страуструпа по стилю и технике C ++ , причина, по которой размер не равен нулю, заключается в том, чтобы «гарантировать, что адреса двух разных объектов будут разными». И размер может быть 1, потому что выравнивание здесь не имеет значения, так как на самом деле не на что смотреть.


55
Ба, что, черт возьми, он знает о C ++? :-)
paxdiablo

18
@Lazer, потому что в C. нет пустых структур
aib

7
@nurabha В этот указатель указывает на объект. Он не хранится в объекте. Однако, если есть виртуальные функции, объект содержит указатель на vtable.
tbleher

4
@krish_oza: ты ошибаешься. Когда вы выделяете переменную в стеке, она не может занимать нулевые байты (потому что разные переменные нуждаются в разных адресах), следовательно, размер не менее 1. После этого дело за компилятором. Аналогично с new; когда пространство выделено, другое выделение должно иметь другой адрес. И так далее. В пустом классе не обязательно участвует указатель; могут быть указатели на переменные (или ссылки, или локальные / стековые выделения), но они не нулевого размера и не обязательно являются размером указателя.
Джонатан Леффлер,

3
@Destructor, не умнее, но я знаю, что такое смайлик :-) Возможно, вы захотите перечитать комментарий в этом свете и понять, что это был юмор.
paxdiablo

30

В стандарте указано, что все производные объекты имеют sizeof ()> = 1:

Если это не битовое поле (class.bit), наиболее производный объект должен иметь ненулевой размер и занимать один или несколько байтов памяти. Подобъекты базового класса могут иметь нулевой размер. ISO / IEC FDIS 14882: 1998 (E) intro.object


1
Я считаю, что трудно поверить. Стандарт старается изо всех сил, чтобы гарантировать, что у реализаций есть свобода действий для хорошей работы по оптимизации, связывая руки разработчику, как это не похоже на то, что обычно делает стандарт (я могу ошибаться)
Martin York

4
@eSKay - требуется, чтобы разные объекты получали разные адреса. У вас не может быть карты указателей на объекты, например, если разные экземпляры имеют одинаковый адрес.
Брайан Нил

14

Это действительно деталь реализации. Когда-то давно я думал, что это может быть ноль или тысяча байтов, что не имеет отношения к спецификации языка. Но, посмотрев на стандарт (раздел 5.3.3), sizeofопределяется как всегда возвращающий один или больше, независимо от того, что.

Размер самого производного класса должен быть больше нуля.

Это необходимо, помимо прочего, для того, чтобы вы могли обрабатывать массивы объектов и указатели на них. Если бы вашим элементам было разрешено иметь нулевой размер, то они &(array[0])были бы идентичны&(array[42]) , что вызовет всевозможные хаос в ваших циклах обработки.

Причина, по которой это может быть не машинное слово, заключается в том, что в нем нет элементов, которые фактически требуют его выравнивания по границе слова (например, целому числу). Например, если вы разместите char x; int y;внутри класса, мой GCC синхронизирует его на восьми байтах (поскольку второй int должен быть выровнен в этой реализации).


Причина «ненулевого» в том, что разные объекты должны иметь разные адреса. Представьте себе массив объектов нулевого размера. Как бы вы его проиндексировали? В некоторых случаях компилятору разрешается оптимизировать это, хотя (оптимизация пустого базового класса)
jalf

@jalf: "Как бы вы его проиндексировали?" Так же, как я сделал бы в C (например, для массива объектов структуры) ??
Lazer

1
@eSKay - Вы бы не смогли, если бы у них был нулевой размер. Все они будут в нулевом элементе
Брайан Нил,

1
@BrianNeal, и это не проблема, потому что у них нет состояния, чтобы отличаться. Проблемы возникают только тогда, когда вы рассматриваете указатели.
gha.st

2
Или взяв размер указанного массива, чтобы вычислить его длину.
gha.st

6

Есть исключение: массивы нулевой длины.

#include <iostream>

class CompletlyEmpty {
  char NO_DATA[0];
};

int main(int argc, const char** argv) {
  std::cout << sizeof(CompletlyEmpty) << '\n';
}

Почему никто не прокомментировал этот ответ? Мне это кажется очень любопытным.
Peregring-lk

Если вы создаете два объекта "CompletelyEmpty" (например, 'a' и 'b'), sizeof сообщает, что они имеют длину 0 байт, но его адреса разные ('& a == & b' оценивает false). И это должно быть невозможно ... (с использованием g ++ 4.7.2).
Peregring-lk

1
Но если вы создадите массив этих объектов, скажем c, &c[M] == &c[N]для каждого Mand N(clang ++ 4.1).
Константин Никитин

5
C и C ++ не допускают массивы нулевой длины. Ваш компилятор мог бы, но это неверно.
Конрад Боровски

да, размер самого класса равен нулю, но экземпляр этого класса по-прежнему имеет размер 1 байт.
ZeR0 05

5

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



3

Это может помочь вам :-) http://bytes.com/topic/c/insights/660463-sizeof-empty-class-structure-1-a

Размер пустого класса или структуры равен 1.

Причина, по которой это происходит, сводится к правильной реализации стандарта. Одна из вещей, о которой говорится в стандарте C ++, заключается в том, что «ни один объект не должен иметь тот же адрес в памяти, что и любая другая переменная» .... Каков самый простой способ гарантировать это? Убедитесь, что все типы имеют ненулевой размер. Чтобы добиться этого, компилятор добавляет фиктивный байт к структурам и классам, которые не имеют элементов данных и виртуальных функций, так что они имеют размер 1, а не размер 0, и тогда они гарантированно имеют уникальный адрес памяти.


2

Выделение 1 байта для пустого класса зависит от компилятора. Компиляторам необходимо убедиться, что объекты находятся в разных местах памяти, и им необходимо выделить объекту ненулевой размер памяти. Слушайте заметки по этой теме здесь: http://listenvoice.com/listenVoiceNote.aspx?id=27

Несмотря на то, что компиляторы выделяют ненулевой размер пустому классу, они также выполняют оптимизацию, когда новые классы являются производными от пустых классов. Послушайте об оптимизации пустой базы в вопросах собеседования ListenVoice по программированию на C ++.


2

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


0

пустой класс - этот класс не содержит никакого содержимого.

любой непустой класс будет представлен своим содержимым в памяти.

как теперь пустой класс будет представлен в памяти? поскольку у него нет содержимого, нет способа показать его существование в памяти, но класс присутствует, его присутствие в памяти обязательно показать. Чтобы показать наличие пустого класса в памяти, требуется 1 байт.


0

Я думаю, это так, потому что 1 байт - это наименьшая единица памяти, которую можно использовать в качестве заполнителя, и он не может дать нулевой размер, так как невозможно создать массив объектов ..

и то, что вы сказали: «Это было немного удивительно для меня, так как я ожидал, что оно будет размером с машинное слово (32 бита или 4 байта)». будет верно для ссылочной переменной (слова macine) типа empty (), а не для размера самого класса (который является абстрактным типом данных),


0

Думаю, этот вопрос представляет только теоретический интерес, но не имеет значения на практике.

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

Более того, если класс пуст (это означает, что он - теоретически - не требует никакой памяти для каждого экземпляра, т. Е. Он не имеет каких-либо нестатических элементов данных или виртуальных функций-членов), тогда все его функции-члены могут также (и должен) быть определен как статический. Таким образом, нет необходимости когда-либо создавать экземпляр этого класса.

Итог: если вы обнаружите, что пишете пустой класс X, просто сделайте все функции-члены статическими. Тогда вам не нужно будет создавать X-объекты, и производные классы никоим образом не будут затронуты.


0
#include<iostream>
using namespace std;


    class Empty { };
    int main()
    {
        Empty* e1 = new Empty;
        Empty* e2 = new Empty;

        if (e1 == e2)
            cout << "Alas same address of two objects" << endl;
        else
            cout << "Okay it's Fine to have different addresses" << endl;

        return 0;
    }

Вывод: Хорошо иметь разные адреса.

Возвращение размера 1 гарантирует, что два объекта не будут иметь одинаковый адрес.


0

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


-2

Я думаю, что если размер пустого класса равен нулю, значит, его не существует. Чтобы он (класс) существовал, он должен иметь как минимум 1 байт, поскольку этот байт является адресом памяти / ссылки.


-3

Это из-за этого указателя, хотя указатель имеет (целое число) 4 байта, но он относится к одной ячейке памяти (одна единица), которая составляет 1 байт.


5
Извините, но это чушь.
jogojapan

@Narayan das khatri: во-первых, тип указателя зависит от типа данных, это не всегда int, а во-вторых, размер указателя зависит от машины и компилятора на 32-битных машинах - это 4 байта, а для 64-битных машин - 8 байтов.
Кришна Оза
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.