C ++: Jaki jest rozmiar obiektu pustej klasy?


111

Zastanawiałem się, jaki może być rozmiar obiektu z pustej klasy . Z pewnością nie może to być 0 bajtów, ponieważ powinno być możliwe odwoływanie się do niego i wskazywanie go jak każdy inny obiekt. Ale jak duży jest taki obiekt?

Użyłem tego małego programu:

#include <iostream>
using namespace std;

class Empty {};

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

Wynik, który otrzymałem na kompilatorach Visual C ++ i Cygwin-g ++, wynosił 1 bajt ! Było to dla mnie trochę zaskakujące, ponieważ spodziewałem się, że będzie miał rozmiar słowa maszynowego (32 bity lub 4 bajty).

Czy ktoś może wyjaśnić, dlaczego rozmiar 1 bajtu? Dlaczego nie 4 bajty? Czy to zależy także od kompilatora czy maszyny? Czy ktoś może podać bardziej przekonujący powód, dla którego pusty obiekt klasy nie będzie miał rozmiaru 0 bajtów?


Nie widzę powodu, dla którego nie mogłoby to być zero. Ale nadając mu rozmiar, inne rzeczy są łatwiejsze w kompilatorze. Jeśli masz tablicę tych rzeczy, każdy element potrzebuje unikalnego adresu. Rozmiar 1 ułatwia to.
Martin York,

2
Może mieć rozmiar zerowy, jeśli jest podobiektem klasy bazowej.
Johannes Schaub - litb

Odpowiedzi:


129

Cytując często zadawane pytania dotyczące stylu i techniki Bjarne Stroustrupa , powodem, dla którego rozmiar jest różny od zera, jest „Aby upewnić się, że adresy dwóch różnych obiektów będą różne”. Rozmiar może wynosić 1, ponieważ wyrównanie nie ma tutaj znaczenia, ponieważ nie ma na co właściwie patrzeć.


55
Aha, co do cholery mógłby wiedzieć o C ++? :-)
paxdiablo

18
@Lazer, ponieważ w C. nie ma pustych struktur
aib

7
@nurabha Punkty ten wskaźnik do obiektu. Nie jest przechowywany w obiekcie. Jeśli jednak istnieją funkcje wirtualne, obiekt zawiera wskaźnik do tabeli vtable.
tbleher

4
@krish_oza: źle myślisz. Kiedy alokujesz zmienną na stosie, nie może ona zajmować zerowych bajtów (ponieważ różne zmienne wymagają różnych adresów), stąd minimalny rozmiar 1. Potem wszystko zależy od kompilatora. Podobnie z new; kiedy miejsce jest przydzielane, inna alokacja musi mieć inny adres. I tak dalej. W pustej klasie niekoniecznie występuje żaden wskaźnik; mogą istnieć wskaźniki do zmiennych (lub referencje lub alokacje lokalne / stosu), ale nie mają one rozmiaru zerowego i niekoniecznie są rozmiarem wskaźnika.
Jonathan Leffler

3
@Destructor, nie mądrzejszy, ale wiem, co to jest buźka :-) Może zechcesz ponownie przeczytać komentarz w tym świetle i zdać sobie sprawę, że to był humor.
paxdiablo

30

Standard stwierdza, że ​​wszystkie najbardziej pochodne obiekty mają sizeof ()> = 1:

O ile nie jest to pole bitowe (class.bit), większość pochodnych obiektów ma rozmiar niezerowy i zajmuje jeden lub więcej bajtów pamięci. Podobiekty klasy bazowej mogą mieć zerowy rozmiar. ISO / IEC FDIS 14882: 1998 (E) intro. Obiekt


1
Trudno mi w to uwierzyć. Standard robi wszystko, co w jego mocy, aby implementacje miały wolną rękę do wykonania dobrej roboty optymalizacji, wiązanie rąk implementującego w ten sposób nie brzmi tak, jak normalnie robi to norma (mogę się mylić)
Martin York,

4
@eSKay - Jest to wymagane, aby różne obiekty miały różne adresy. Nie możesz mieć mapy wskaźników do obiektów, na przykład, jeśli różne instancje miały ten sam adres.
Brian Neal

14

To naprawdę szczegół implementacji. Kiedyś myślałem, że może to być zero bajtów lub tysiąc bajtów, że nie ma to wpływu na specyfikację języka. Ale po spojrzeniu na standard (sekcja 5.3.3), sizeofjest definiowany jako zawsze zwracający jeden lub więcej, bez względu na wszystko.

Rozmiar najbardziej pochodnej klasy powinien być większy niż zero.

Jest to wymagane między innymi do obsługi tablic obiektów i wskaźników do nich. Gdyby twoje elementy mogły mieć zerową wielkość, &(array[0])byłyby identyczne z &(array[42]), co spowoduje różnego rodzaju spustoszenie w twoich pętlach przetwarzania.

Przyczyną, dla której może to nie być słowo maszynowe, jest to, że nie ma w nim żadnych elementów, które w rzeczywistości wymagają wyrównania na granicy słowa (na przykład liczby całkowitej). Na przykład, jeśli umieścisz char x; int y;wewnątrz klasy, moje GCC będzie ją ustawiało na ośmiu bajtach (ponieważ druga int musi być wyrównana w tej implementacji).


Powodem braku zera jest to, że różne obiekty powinny mieć różne adresy. Wyobraź sobie tablicę obiektów o zerowej wielkości. Jak byś to zindeksował? W niektórych przypadkach kompilator może to jednak zoptymalizować (pusta optymalizacja klasy bazowej)
jalf

@jalf: „Jak byś to zindeksował?” To samo, co zrobiłbym w C (na przykład dla tablicy obiektów struktury)?
Lazer

1
@eSKay - Nie mogłeś, gdyby miały rozmiar 0. Wszyscy byliby w elemencie 0.
Brian Neal

1
@BrianNeal, co nie stanowi problemu, ponieważ nie mają stanu, aby się wyróżnić. Problemy pojawiają się tylko wtedy, gdy weźmiesz pod uwagę wskazówki.
gha.st

2
Lub biorąc rozmiar wspomnianej tablicy, aby obliczyć jej długość.
gha.st

6

Jest wyjątek: tablice o długości 0

#include <iostream>

class CompletlyEmpty {
  char NO_DATA[0];
};

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

Dlaczego nikt nie skomentował tej odpowiedzi? Wydaje mi się to bardzo ciekawe.
Peregring-lk

Jeśli utworzysz dwa obiekty „CompletelyEmpty” (na przykład „a” i „b”), sizeof mówi, że mają one długość 0 bajtów, ale jego adresy są różne („& a == & b” daje wartość fałsz). A to powinno być niemożliwe ... (używając g ++ 4.7.2).
Peregring-lk

1
Ale jeśli utworzysz tablicę tych obiektów, powiedzmy c, &c[M] == &c[N]dla każdego Mi N(clang ++ 4.1).
Konstantin Nikitin

5
C i C ++ nie zezwalają na tablice o zerowej długości. Twój kompilator mógłby, ale jest nieprawidłowy.
Konrad Borowski

tak, sam rozmiar klasy wynosi zero, ale instancja tej klasy nadal ma 1 bajt.
ZeR0

5

Mimo że nie jest wymagane przypisywanie jakiejkolwiek pamięci dla pustej klasy, ale aby utworzyć obiekty z pustych klas, kompilator przypisuje minimalną pamięć, którą można przypisać, czyli 1 bajt. W ten sposób kompilator może jednoznacznie rozróżnić dwa obiekty tej samej pustej klasy i będzie mógł przypisać adres obiektu do wskaźnika pustej klasy.



3

To może pomóc :-) http://bytes.com/topic/c/insights/660463-sizeof-empty-class-structure-1-a

Rozmiar pustej klasy lub struktury to 1

Powód, dla którego tak się dzieje, sprowadza się do prawidłowego zaimplementowania standardu, jedną z rzeczy, o których mówi standard C ++, jest to, że „żaden obiekt nie może mieć tego samego adresu w pamięci, co każda inna zmienna”… Jaki jest najłatwiejszy sposób, aby to zapewnić? Upewnij się, że wszystkie typy mają niezerowy rozmiar. Aby to osiągnąć, kompilator dodaje bajt fikcyjny do struktur i klas, które nie mają elementów składowych danych ani funkcji wirtualnych, tak aby miały rozmiar 1, a nie rozmiar 0, a następnie mają zagwarantowany unikalny adres pamięci.


2

Przydział 1 bajtu dla pustej klasy zależy od kompilatora. Kompilatory muszą upewnić się, że obiekty znajdują się w różnych lokalizacjach pamięci i muszą przydzielić niezerowy rozmiar pamięci do obiektu. Posłuchaj uwag na ten temat tutaj: http://listenvoice.com/listenVoiceNote.aspx?id=27

Mimo że kompilatory przydzielają niezerowy rozmiar do pustej klasy, wykonują również optymalizacje, gdy nowe klasy pochodzą z pustych klas. Posłuchaj o optymalizacji pustej bazy w pytaniach o programowanie w C ++ zadawanych przez ListenVoice.


2

powodem istnienia klasy bez elementów danych, ale o rozmiarze 1 bajtu jest to, że ten * silny tekst * musi być przechowywany w pamięci, aby odwołanie lub wskaźnik mogły wskazywać na obiekt tej klasy


0

pusta klasa - ta klasa nie zawiera żadnej treści.

każda klasa, która nie jest pusta, będzie reprezentowana przez swoją zawartość w pamięci.

teraz jak pusta klasa będzie reprezentowana w pamięci? ponieważ nie ma treści, nie ma sposobu, aby pokazać swoje istnienie w pamięci, ale klasa jest obecna, obowiązkowe jest wykazanie jej obecności w pamięci. Aby pokazać pustą klasę w pamięci, wymagany jest 1 bajt.


0

Myślę, że tak jest, ponieważ 1 bajt jest najmniejszą jednostką pamięci, która może być używana jako symbol zastępczy i nie może dać zerowego rozmiaru, ponieważ nie będzie możliwe utworzenie tablicy obiektów.

i to, co powiedziałeś: „To było dla mnie trochę zaskakujące, ponieważ spodziewałem się, że będzie to rozmiar słowa maszynowego (32 bity lub 4 bajty)”. będzie prawdziwe dla zmiennej referencyjnej (słowa macine) typu empty (), a nie rozmiaru samej klasy (która jest abstrakcyjnym typem danych),


0

Myślę, że to pytanie jest interesujące tylko teoretycznie, ale nie ma znaczenia w praktyce.

Jak już zauważyli inni, wyprowadzanie z pustej klasy nie powoduje żadnej szkody, ponieważ nie zużywa dodatkowej pamięci dla części klasy bazowej.

Co więcej, jeśli klasa jest pusta (co oznacza, że ​​- teoretycznie - nie potrzebuje żadnej pamięci na instancję, tj. Nie ma żadnych niestatycznych składowych danych ani wirtualnych funkcji składowych), to wszystkie jej funkcje składowe mogą równie dobrze (i powinien) być zdefiniowany jako statyczny. Nie ma więc potrzeby tworzenia instancji tej klasy.

Konkluzja: jeśli napiszesz pustą klasę X, po prostu ustaw wszystkie funkcje składowe jako statyczne. Nie będziesz wtedy musiał tworzyć obiektów X, a klasy pochodne nie będą miały żadnego wpływu.


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;
    }

Wyjście: OK, dobrze jest mieć różne adresy

Zwrócenie rozmiaru 1 gwarantuje, że dwa obiekty nie będą miały tego samego adresu.


0

Jest niezerowe, aby zapewnić, że dwa różne obiekty będą miały różne adresy. Różne obiekty powinny mieć różne adresy, więc rozmiar pustej klasy zawsze wynosi 1 bajt.


-2

Myślę, że jeśli rozmiar pustej klasy wynosi zero, to znaczy, że nie istnieje. Aby to (klasa) istniało, wymaga co najmniej 1 bajtu, ponieważ ten bajt jest adresem pamięci / odniesienia.


-3

Dzieje się tak z powodu tego wskaźnika, chociaż wskaźnik ma (liczbę całkowitą) 4 bajty, ale odnosi się do jednej lokalizacji pamięci (jednej jednostki), która ma 1 bajt.


5
Przepraszam, ale to nonsens.
jogojapan

@Narayan das khatri: po pierwsze, typ wskaźnika zależy od typu danych, to nie zawsze, a po drugie rozmiar wskaźnika zależy od maszyny i kompilatora na maszynach 32-bitowych to 4 bajty, a dla maszyn 64-bitowych 8 bajtów.
Krishna Oza
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.