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 про стиль та техніку Bjarne Stroustrup C ++ , причина розміру не дорівнює нулю - "Для того, щоб адреси двох різних об'єктів були різними". І розмір може бути 1, тому що вирівнювання тут не має значення, оскільки насправді немає на що подивитися.


55
Ба, що б він знав про C ++? :-)
paxdiablo

18
@Lazer, оскільки в С. немає порожніх структур
аїб

7
@nurabha Цей вказівник вказує на об’єкт. Він не зберігається в об'єкті. Якщо ж існують віртуальні функції, об’єкт містить вказівник на vtable.
tbleher

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

3
@ Деструктор, не розумніший, але я знаю, що таке смайлик :-) Ви можете перечитати коментар у такому світлі і зрозуміти, що це гумор.
paxdiablo

30

Стандарт стверджує, що всі більшість похідних об'єктів мають розмір ()> = 1:

Якщо це не бітове поле (class.bit), найбільш похідний об'єкт повинен мати ненульовий розмір і повинен займати один або кілька байтів сховища. Суб'єкти базового класу можуть мати нульовий розмір. ISO / IEC FDIS 14882: 1998 (E) вступ.об'єкт


1
Мені важко повірити. Стандарт виходить із шляху, щоб переконатися, що у реалізацій є вільна рука, щоб зробити хорошу роботу з оптимізації, зв’язуючи руки реалізатора, як це не звучить як те, що звичайно робить стандарт (я можу помилятися)
Мартін Йорк

4
@eSKay - Це потрібно, щоб різні об'єкти отримували різні адреси. Ви не могли мати карту покажчиків на об’єкти, наприклад, якщо різні екземпляри мали однакову адресу.
Брайан Ніл

14

Це справді деталь реалізації. Колись давно я думав, що це може бути нульовим байтом або тисячею байт, що це не має відношення до мовної специфікації. Але, переглянувши стандарт (розділ 5.3.3), sizeofвизначається як завжди повертається один або більше, незалежно від того.

Розмір найбільш похідного класу повинен бути більшим за нуль.

Це потрібно для, крім іншого, дозволяє обробляти масиви об'єктів та покажчики на них. Якщо вашим елементам було дозволено нульовий розмір, то це &(array[0])було б ідентично &(array[42]), що спричинить всі види хаосу для ваших циклів обробки.

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


Причина "не нуля" полягає в тому, що різні об'єкти повинні мати різні адреси. Уявіть масив об’єктів нульового розміру. Як би ви його індексували? У деяких випадках компілятору дозволяється оптимізувати це, хоча (порожня оптимізація базового класу)
jalf

@jalf: "Як би ви проіндексували це?" Так само, як і в C (для масиву структур, наприклад) ??
Лазер

1
@eSKay - Ви б не могли, якби вони мали розмір 0. Усі вони були б у стихії 0.
Брайан Ніл

1
@BrianNeal це не проблема, оскільки вони не мають держави, щоб диференціювати себе. Проблеми виникають лише тоді, коли ви враховуєте покажчики.
gha.st

2
Або взявши розмір згаданого масиву для обчислення його довжини.
gha.st

6

Є виняток: масиви 0 довжини

#include <iostream>

class CompletlyEmpty {
  char NO_DATA[0];
};

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

Чому ніхто не коментував цю відповідь? Мені це здається дуже цікавим.
Peregring-lk

Якщо ви створюєте два об'єкти "CompleteEmpty" (наприклад, "a" і "b"), розмірofof говорить, що вони мають довжину 0 байт, але його адреси є різними ("& a == & b" оцінює помилкове). І це повинно бути неможливо ... (використовуючи g ++ 4.7.2).
Peregring-lk

1
Але якщо ви створюєте масив з цих об'єктів, скажімо c, &c[M] == &c[N]для кожного Mі N(clang ++ 4.1).
Костянтин Нікітін

5
C і C ++ не дозволяє мати масиви нульової довжини. Ваш компілятор міг, але це неправильно.
Конрад Боровський

так, розмір самого класу дорівнює нулю, але екземпляр цього класу все ще становить 1 байт.
ZeR0

5

Навіть незважаючи на те, що для порожнього класу не потрібно призначати жодної пам'яті, але для того, щоб робити об'єкти порожніх класів, компілятор призначає мінімальний об'єм пам'яті, який може бути призначений, а це 1 байт. Таким чином компілятор може однозначно розрізнити два об'єкти одного і того ж порожнього класу та зможе призначити адресу об’єкта вказівнику порожнього класу.



3

Це може допомогти u :-) 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

Хоча компілятори виділяють ненульовий розмір порожньому класу, вони також роблять оптимізацію, коли нові класи виводяться з порожніх класів. Послухайте про оптимізацію порожньої бази на питаннях інтерв'ю для програмування Listen + Voice.


2

Причина для класу, який не має членів даних, але має байт розміру 1, полягає в тому, що цей * сильний текст * повинен зберігатися в пам'яті, щоб посилання або вказівник могли вказувати на об'єкт цього класу


0

порожній клас -це клас не містить жодного вмісту.

будь-який клас, який не порожній, буде представлений його вмістом у пам'яті.

тепер, як порожній клас буде представлений в пам'яті? оскільки у нього немає вмісту, який не може показати своє існування в пам'яті, але клас присутній, обов'язково потрібно показати його присутність у пам'яті. Для відображення порожньої присутності класу в пам'яті потрібно 1 байт.


0

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

і те, що ви сказали: "Це мене трохи здивувало, оскільки я очікував, що воно буде розміром із машинного слова (32 біти або 4 байти)". буде вірно для довідкової змінної (macine слова) типу порожній (), а не розміру самого класу (який є абстрактним типом даних),


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: по-перше, тип вказівника залежить від типу даних його не завжди, а по-друге, розмір вказівника залежить від машини та компілятора на 32-бітних машинах, це 4 байти, а для 64-бітних машин - 8 байт.
Крішна Оза
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.