Dlaczego musimy umieszczać prywatnych członków w nagłówkach?

62

Zmienne prywatne są sposobem na ukrycie złożoności i szczegółów implementacji dla użytkownika klasy. Jest to dość fajna funkcja. Ale nie rozumiem, dlaczego w c ++ musimy umieścić je w nagłówku klasy. Widzę dwie irytujące wady tego:

  • Zaśmieca nagłówek użytkownika
  • Wymusza ponowną kompilację wszystkich bibliotek klienckich przy każdej modyfikacji elementów wewnętrznych

Czy istnieje uzasadnienie koncepcyjne tego wymogu? Czy to tylko po to, aby ułatwić pracę kompilatorowi?

Simon Bergot
źródło
możesz zadeklarować pustą strukturę w nagłówku, ale wtedy możesz używać wskaźników do takiej struktury tylko wtedy, gdy go używasz (i nie możesz jej przypisać)
maniak ratchet
3
@ratchetfreak: Nie, pusty ( struct foo{};) jest niedozwolony, ale deklaracje forward ( struct foo;) są.
MSalters
@MSalters to miałem na myśli
maniak zapadkowy
1
Dodaję wadę: * Zapisywanie nagłówków funkcji prywatnych w pliku .h to ogromna strata czasu. (na chwilę zapominając o lekcjach dla przyjaciół)
Jonny

Odpowiedzi:

68

Wynika to z faktu, że kompilator C ++ musi znać rzeczywisty rozmiar klasy, aby przydzielić odpowiednią ilość pamięci przy tworzeniu. Rozmiar obejmuje wszystkich członków, również prywatnych.

Jednym ze sposobów uniknięcia tego jest użycie idiomu Pimpl , wyjaśnionego przez Herb Suttera w jego serii Guru Tygodnia nr 24 i 28 .

Aktualizacja

Rzeczywiście, to (lub bardziej ogólnie, rozróżnienie pliku nagłówka / pliku źródłowego #include) jest główną przeszkodą w C ++, odziedziczonym po C. W czasach, gdy C ++ C został stworzony, nie było jeszcze doświadczenia z programowaniem na dużą skalę, gdzie to zaczyna powodować prawdziwe problemy. Doświadczenia zdobyte od tego czasu były uwzględniane przez projektantów nowszych języków, ale C ++ jest związany wymogami dotyczącymi kompatybilności wstecznej, co sprawia, że ​​naprawdę trudno jest rozwiązać tak fundamentalną kwestię w języku.

Péter Török
źródło
Czy tego rodzaju informacje nie są zawarte tylko w bibliotece klas? Czy służy do łączenia?
Simon Bergot,
@ Simon, co rozumiesz przez „bibliotekę klas”?
Péter Török
Mam na myśli kolekcję plików obiektowych zawierających definicję klasy i metody
Simon Bergot,
7
Kiedy stworzono C ++, AT & T / Bell Labs (wówczas pracodawca Stroustrups) z pewnością miał doświadczenie w rozwoju C na dużą skalę. Ich oprogramowanie do przełączania telefonów 5ESS było wówczas prawdopodobnie największym pojedynczym programem C na świecie. Wczesne pomysły na temat OO są już widoczne w tej bazie kodu, a Cfront naśladował te techniki. Jednak pojęcie privatejest bardziej nowoczesne.
MSalters
1
W C wystarczy umieścić alokator w funkcji bibliotecznej; klient w ogóle nie byłby w stanie przydzielić takiej struktury. Zwiększa to nieco narzut, ale sprawia, że ​​migracja kodu pomiędzy wersjami jest banalna, dlatego często warto. Jednak zwykle prowadzi to do stylu kodu, który bardzo różni się od tego, który można zobaczyć w C ++.
Donal Fellows
15

Definicja klasy musi być wystarczająca, aby kompilator tworzył identyczny układ w pamięci, niezależnie od tego, gdzie użyto się obiektu klasy. Na przykład biorąc pod uwagę coś takiego:

class X { 
    int a;
public:
    int b;
};

Kompilator zwykle ma aprzesunięcie 0 i bprzesunięcie 4. Jeśli kompilator uzna to za po prostu:

class X { 
public:
    int b;
};

„Myślałby”, że bpowinien mieć przesunięcie 0 zamiast przesunięcia 4. Gdy kod korzystający z tej definicji zostanie przypisany b, kod korzystający z pierwszej definicji zobaczy amodyfikację i odwrotnie.

Zwykły sposób na zminimalizowanie efektów wprowadzania zmian w prywatnych częściach klasy jest zwykle nazywany idiomem pimpl (o którym jestem pewien, że Google może podać wiele informacji).

Jerry Coffin
źródło
1
Pytam o decyzję projektową. Oczywiście musisz umieścić gdzieś deklarację członka prywatnego, aby język działał. Ale dlaczego powinien być w nagłówku, a nie w bardziej prywatnym miejscu?
Simon Bergot,
7
@ Simon: Nagłówek to wszystko, co widzi kompilator, aby powiedzieć mu, jak wygląda klasa / struct. Dyskutowano o dodaniu do C ++ czegoś w rodzaju modułów, które mogłyby bardziej ukryć tego rodzaju dane, ale jak dotąd nie zostały zatwierdzone (choć nie zostały całkowicie usunięte).
Jerry Coffin
3
Mimo to trywialną zasadą byłoby przydzielanie takich prywatnych członków „zdefiniowanych w formacie .cpp” na końcu. Oznacza to, że przesunięcia członków publicznych i „normalnych” członków prywatnych nie będą od nich zależne. IMO prawdziwym powodem jest to, że nie możesz dziedziczyć po takiej klasie, ponieważ pochodna część musi podążać nawet za tymi prywatnymi członkami.
MSalters
3

Najprawdopodobniej jest kilka powodów. Chociaż członkowie prywatni nie mogą uzyskać dostępu do większości innych klas, nadal mogą być dostępne dla klas przyjaciół. Tak więc przynajmniej w tym przypadku mogą być potrzebne w nagłówku, aby klasa znajomych mogła zobaczyć, że istnieją.

Ponowna kompilacja plików zależnych może zależeć od struktury dołączeń. Dołączenie plików .h do pliku .cpp zamiast innego nagłówka może w niektórych przypadkach zapobiec długim łańcuchom ponownej kompilacji.

Thorsten Müller
źródło