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?


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.


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).


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.

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.