Gdzie umieścić instrukcje dołączenia, nagłówek lub źródło?

107

Czy powinienem umieścić dołączenia w pliku nagłówkowym, czy w pliku źródłowym? Jeśli plik nagłówkowy zawiera instrukcje include, to jeśli dołączę ten plik nagłówkowy do mojego źródła, to czy mój plik źródłowy będzie zawierał wszystkie dołączone pliki, które były w moim nagłówku? A może powinienem po prostu dołączyć je do mojego pliku źródłowego?

Mohit Deshpande
źródło
2
Wiele poprzednich duplikatów na SO, np. Gdzie należy umieścić „include” w C ++
Paul R

Odpowiedzi:

141

Umieść w nagłówku tylko wtedy, gdy sam nagłówek ich potrzebuje.

Przykłady:

  • Twoja funkcja zwraca typ size_t. Następnie #include <stddef.h>w pliku nagłówkowym .
  • Twoja funkcja używa strlen. Następnie #include <string.h>w pliku źródłowym .
schot
źródło
2
Co jeśli moja funkcja przyjmuje argument typu size_t?
andrybak
To samo pytanie rozwijające się do C ++: co jeśli moja struktura / klasa ma pole / element członkowski typu size_tlub std::string?
andrybak
10
Jakie jest uzasadnienie?
Patrizio Bertoni
Mam sytuację przewodową, klasa A C ++ ma obiekt innej klasy B i nie mogę użyć deklaracji do przodu B i zakończenia, w tym nagłówka B w nagłówku A. (używanie wskaźnika nie ma tego problemu)
shuva
@andrybak Twoje pliki źródłowe powinny zawierać plik nagłówkowy, więc każdy plik zawierający nagłówek otrzyma również źródło.
Jeremy Trifilo
27

Przez lata było sporo nieporozumień w tej sprawie. Kiedyś było tradycyjne, że nagłówek tylko deklarował, co było w jakimkolwiek module, z którym był powiązany, więc wiele nagłówków miało określone wymagania, że ​​masz #includeokreślony zestaw nagłówków (w określonej kolejności). Niektórzy niezwykle tradycyjni programiści C nadal stosują ten model (przynajmniej w niektórych przypadkach religijnie).

Niedawno nastąpił ruch w kierunku samodzielnego tworzenia większości nagłówków. Jeśli ten nagłówek wymaga czegoś innego, sam nagłówek to obsługuje, zapewniając, że wszystko, czego potrzebuje, jest uwzględnione (we właściwej kolejności, jeśli występują problemy z zamówieniem). Osobiście wolę to - zwłaszcza gdy kolejność nagłówków może być ważna, rozwiązuje to problem raz, zamiast wymagać od wszystkich, którzy go używają, ponownego rozwiązania problemu.

Zauważ, że większość nagłówków powinna zawierać tylko deklaracje. Oznacza to, że dodanie niepotrzebnego nagłówka nie powinno (normalnie) mieć żadnego wpływu na końcowy plik wykonywalny. Najgorsze jest to, że trochę spowalnia kompilację.

Jerry Coffin
źródło
2
Jeśli wszystkie nagłówki są zapisane w drugim stylu, nie powinno być żadnych problemów z porządkiem. Problemy z porządkiem w nagłówkach zwykle oznaczają, że nie umieściłeś w nagłówku wszystkiego, czego potrzebujesz.
Do widzenia SE
12

Twoje #includepliki powinny składać się z plików nagłówkowych, a każdy plik (źródłowy lub nagłówkowy) powinien #includezawierać pliki nagłówkowe, których potrzebuje. Pliki nagłówkowe powinny mieć #includeminimalne wymagane pliki nagłówkowe, a pliki źródłowe również, chociaż nie jest to tak ważne dla plików źródłowych.

Plik źródłowy będzie mieć nagłówki to #includeS i nagłówki they #include, i tak dalej, aż do maksymalnej głębokości zagnieżdżenia. Dlatego nie chcesz, #includeaby w plikach nagłówkowych były niepotrzebne pliki: mogą one spowodować, że plik źródłowy będzie zawierał wiele plików nagłówkowych, których może nie potrzebować, spowalniając kompilację.

Oznacza to, że jest całkowicie możliwe, że pliki nagłówkowe mogą być dołączane dwukrotnie i może to być problem. Tradycyjną metodą jest umieszczanie „włączonych strażników” w plikach nagłówkowych, na przykład w pliku foo.h:

#ifndef INCLUDE_FOO_H
#define INCLUDE_FOO_H
/* everything in header goes here */
#endif
David Thornley
źródło
Wiem, że ta odpowiedź jest bardzo stara, ale od tego czasu dodali raz #pragma, więc nie musisz uwzględniać #ifndef podczas deklarowania #includes. Opublikowałem tę odpowiedź, ponieważ starsze, ale bardziej popularne / cieszące się uznaniem wątki zwykle znajdują się na szczycie wyszukiwań w Google
Dogunbound hounds
6

Podejście, do którego ewoluowałem przez ponad dwadzieścia lat, jest takie;

Rozważmy bibliotekę.

Istnieje wiele plików C, jeden wewnętrzny plik H i jeden zewnętrzny plik H. Pliki C zawierają wewnętrzny plik H. Wewnętrzny plik H zawiera zewnętrzny plik H.

Widzisz, że z kompilatorów POV, gdy kompiluje plik C, istnieje hierarchia;

zewnętrzne -> wewnętrzne -> kod C.

To jest właściwa kolejność, ponieważ to, co jest zewnętrzne, jest wszystkim, czego strona trzecia potrzebuje do korzystania z biblioteki. To, co jest wewnętrzne, jest wymagane do skompilowania kodu C.


źródło
4

Jeśli plik nagłówku #includesheader files B i C, a następnie każdy plik źródłowy, że #includesA będzie również dostać B i C #included. Preprocesor dosłownie dokonuje podstawienia tekstu: gdziekolwiek znajdzie tekst, który mówi, #include <foo.h>że zastępuje go tekstem z foo.hpliku.

Istnieją różne opinie na temat tego, czy należy umieszczać #includesnagłówki, czy pliki źródłowe. Osobiście wolę #includesdomyślnie umieszczać all w pliku źródłowym, ale wszystkie pliki nagłówkowe, które nie mogą się skompilować bez innych wstępnie wymaganych nagłówków, powinny #includesame je zawierać .

Każdy plik nagłówkowy powinien zawierać zabezpieczenie włączające, aby zapobiec wielokrotnemu dołączaniu.

Vicky
źródło
4

W niektórych środowiskach kompilacja będzie najszybsza, jeśli zawiera się tylko potrzebne pliki nagłówkowe. W innych środowiskach kompilacja zostanie zoptymalizowana, jeśli wszystkie pliki źródłowe mogą używać tej samej podstawowej kolekcji nagłówków (niektóre pliki mogą mieć dodatkowe nagłówki poza wspólnym podzbiorem). Idealnie, nagłówki powinny być skonstruowane tak, aby wiele operacji #include nie miało żadnego efektu. Dobrze może być otoczenie instrukcji #include sprawdzeniami ochrony włączania pliku, który ma być dołączony, chociaż tworzy to zależność od formatu tej ochrony. Ponadto, w zależności od zachowania systemu w buforowaniu plików, niepotrzebne #include, którego cel zostanie całkowicie # ifdef'ed usunięte, może nie zająć dużo czasu.

Inną rzeczą do rozważenia jest to, że jeśli funkcja przyjmuje wskaźnik do struktury, można zapisać prototyp jako

void foo (struct BAR_s * bar);

bez definicji BAR_s, które muszą znajdować się w zakresie. Bardzo przydatne podejście do unikania niepotrzebnych dołączeń.

PS - w wielu moich projektach będzie plik, którego oczekuje się, że każdy moduł będzie #include, zawierający takie rzeczy jak typedef dla rozmiarów całkowitych oraz kilka typowych struktur i unii [np.

typedef union {
  unsigned long l;
  unsigned short lw [2];
  unsigned char lb [4];
} U_QUAD;

(Tak, wiem, że miałbym kłopoty, gdybym przeniósł się na architekturę big-endian, ale ponieważ mój kompilator nie zezwala na anonimowe struktury w uniach, używanie nazwanych identyfikatorów dla bajtów w unii wymagałoby dostępu do nich jako theUnion.b.b1 itp., co wydaje się dość denerwujące.

supercat
źródło
3

Twórz wszystkie swoje pliki tak, aby można je było budować tylko przy użyciu tego, co zawierają. Jeśli nie potrzebujesz dołączenia do nagłówka, usuń je. W dużym projekcie, jeśli nie utrzymujesz tej dyscypliny, narażasz się na zerwanie całej kompilacji, gdy ktoś usunie dołączenie z pliku nagłówkowego, który jest używany przez konsumenta tego pliku, a nawet przez nagłówek.

ponownie odtwarzać
źródło
1

Twój plik źródłowy będzie zawierał instrukcje include, jeśli umieścisz go w nagłówku. Jednak w niektórych przypadkach lepiej byłoby umieścić je w pliku źródłowym.

Pamiętaj, że jeśli umieścisz ten nagłówek w jakimkolwiek innym źródle, otrzymają one również dołączenia z nagłówka, a to nie zawsze jest pożądane. Powinieneś uwzględniać tylko te rzeczy, w których jest używany.

Kurz rozpryskowy
źródło
1

W nagłówku należy umieszczać tylko te pliki, które trzeba zadeklarować stałe i deklaracje funkcji. Z technicznego punktu widzenia dołączenia te będą również zawarte w pliku źródłowym, ale dla zachowania przejrzystości w każdym pliku należy umieszczać tylko te pliki, których faktycznie potrzebujesz. Powinieneś także chronić je w swoim nagłówku przed wielokrotnym włączaniem w ten sposób:

#ifndef NAME_OF_HEADER_H
#define NAME_OF_HEADER_H

...definition of header file...

#endif

Zapobiega to wielokrotnemu dołączaniu nagłówka, co powoduje błąd kompilatora.

JRam930
źródło