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?
107
Odpowiedzi:
Umieść w nagłówku tylko wtedy, gdy sam nagłówek ich potrzebuje.
Przykłady:
size_t
. Następnie#include <stddef.h>
w pliku nagłówkowym .strlen
. Następnie#include <string.h>
w pliku źródłowym .źródło
size_t
?size_t
lubstd::string
?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
#include
okreś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ę.
źródło
Twoje
#include
pliki powinny składać się z plików nagłówkowych, a każdy plik (źródłowy lub nagłówkowy) powinien#include
zawierać pliki nagłówkowe, których potrzebuje. Pliki nagłówkowe powinny mieć#include
minimalne 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
#include
S i nagłówki they#include
, i tak dalej, aż do maksymalnej głębokości zagnieżdżenia. Dlatego nie chcesz,#include
aby 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:
źródło
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
Jeśli plik nagłówku
#includes
header files B i C, a następnie każdy plik źródłowy, że#includes
A 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 zfoo.h
pliku.Istnieją różne opinie na temat tego, czy należy umieszczać
#includes
nagłówki, czy pliki źródłowe. Osobiście wolę#includes
domyś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#include
same je zawierać .Każdy plik nagłówkowy powinien zawierać zabezpieczenie włączające, aby zapobiec wielokrotnemu dołączaniu.
źródło
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
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.
(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.
źródło
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.
źródło
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.
źródło
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:
Zapobiega to wielokrotnemu dołączaniu nagłówka, co powoduje błąd kompilatora.
źródło