W jakiej kolejności należy podać pliki, tj. Jakie są powody umieszczania jednego nagłówka przed drugim?
Na przykład, czy pliki systemowe, STL i Boost idą przed czy po lokalnych plikach dołączanych?
W jakiej kolejności należy podać pliki, tj. Jakie są powody umieszczania jednego nagłówka przed drugim?
Na przykład, czy pliki systemowe, STL i Boost idą przed czy po lokalnych plikach dołączanych?
Odpowiedzi:
Nie sądzę, aby istniało zalecane zamówienie, o ile się kompiluje! Irytujące jest to, że niektóre nagłówki wymagają najpierw dołączenia innych nagłówków ... To jest problem z samymi nagłówkami, a nie z kolejnością dołączania.
Moje osobiste preferencje to przejście z lokalnego na globalny, każdy podsekcja w kolejności alfabetycznej, tj .:
Moim uzasadnieniem dla 1. jest to, że powinno to udowodnić, że każdy nagłówek (dla którego jest procesor) może być #include
d bez wymagań wstępnych (terminus technicus: nagłówek jest „samowystarczalny”). A reszta wydaje się płynąć stamtąd logicznie.
Należy pamiętać, że nagłówki nie powinny być zależne od włączenia innych nagłówków. Jednym ze sposobów zapewnienia tego jest dołączenie nagłówków przed innymi nagłówkami.
Wspomina o tym w szczególności „Thinking in C ++”, nawiązując do „Lakos C ++ Software Design”:
Można uniknąć ukrytych błędów użytkowania, upewniając się, że plik .h komponentu analizuje się sam - bez deklaracji lub definicji dostarczonych zewnętrznie ... Dołączenie pliku .h jako pierwszej linii pliku .c gwarantuje, że żaden kluczowy fragment informacji właściwych dla fizycznego interfejsu komponentu brakuje w pliku .h (lub, jeśli tak, to dowiesz się o tym, jak tylko spróbujesz skompilować plik .c).
To znaczy, uwzględnij w następującej kolejności:
Jeśli któryś z nagłówków ma problem z dołączeniem do tej kolejności, napraw je (jeśli twoje) lub nie używaj ich. Biblioteki bojkotu, które nie piszą czystych nagłówków.
C ++ przewodnik redakcyjny Google twierdzi, prawie na odwrót, ze naprawdę nie ma uzasadnienia w ogóle; Osobiście wolę podejście Lakos.
Przestrzegam dwóch prostych zasad, które pozwalają uniknąć ogromnej większości problemów:
Przestrzegam również wytycznych:
Innymi słowy:
#include <stdio.h>
#include <string.h>
#include "btree.h"
#include "collect_hash.h"
#include "collect_arraylist.h"
#include "globals.h"
Chociaż będąc wytycznymi, jest to subiektywna sprawa. Z drugiej strony reguły, egzekwuję sztywno, nawet do tego stopnia, że udostępniam pliki nagłówkowe „wrapper” z włączonymi strażnikami i pogrupowane, jeśli jakiś wstrętny zewnętrzny programista nie podzieli się moją wizją :-)
Aby dodać własną cegłę do ściany.
Więc zwykle tak wyglądam:
// myproject/src/example.cpp
#include "myproject/example.h"
#include <algorithm>
#include <set>
#include <vector>
#include <3rdparty/foo.h>
#include <3rdparty/bar.h>
#include "myproject/another.h"
#include "myproject/specific/bla.h"
#include "detail/impl.h"
Każda grupa oddzielona pustą linią od następnej:
Zauważ też, że oprócz nagłówków systemowych każdy plik znajduje się w folderze o nazwie jego przestrzeni nazw, tylko dlatego, że łatwiej jest go wyśledzić w ten sposób.
#define
tym, że psują inny kod) i zapobiegają niejawnym zależnościom. Na przykład, jeśli nasz podstawowy plik nagłówkowy kodu foo.h
naprawdę zależy, <map>
ale wszędzie, gdzie był używany w .cc
plikach, <map>
zdarzyło się, że już został dołączony, prawdopodobnie nie zauważymy. Aż ktoś próbował dołączyć foo.h
bez uprzedniego włączenia <map>
. A potem byliby zirytowani.
.h
ma co najmniej jeden, .cpp
który go obejmuje jako pierwszy (w moim osobistym kodzie powiązany test jednostkowy obejmuje go pierwszy, a kod źródłowy obejmuje go w odpowiedniej grupie ). Jeśli chodzi o brak wpływu, jeśli którykolwiek z nagłówków zawiera, <map>
to i tak wszystkie nagłówki później zostaną zmienione , więc wydaje mi się, że przegrywam bitwę.
Header corresponding to this cpp file first (sanity check)
. Czy jest coś szczególnego, jeśli #include "myproject/example.h"
zostanie przeniesiony na koniec wszystkich dołączeń?
Polecam:
I oczywiście, w miarę możliwości, kolejność alfabetyczna w każdej sekcji.
Zawsze używaj deklaracji przesyłania dalej, aby uniknąć niepotrzebnych #include
znaków w plikach nagłówka.
Jestem prawie pewien, że nie jest to zalecana praktyka nigdzie w zdrowym świecie, ale lubię, aby system zawierał według długości nazwy pliku, posortowanej leksykalnie w tej samej długości. Tak jak:
#include <set>
#include <vector>
#include <algorithm>
#include <functional>
Myślę, że dobrym pomysłem jest dołączanie własnych nagłówków przed innymi ludźmi, aby uniknąć wstydu związania z kolejnością dołączania.
windows.h
.
To nie jest subiektywne. Upewnij się, że nagłówki nie polegają na #include
bw określonej kolejności. Możesz być pewien, że nie ma znaczenia, w jakiej kolejności dołączasz nagłówki STL lub Boost.
Najpierw dołącz nagłówek odpowiadający .cpp ... innymi słowy, source1.cpp
powinien zawierać source1.h
przed dołączeniem czegokolwiek innego. Jedyny wyjątek, jaki mogę wymyślić, to użycie MSVC ze wstępnie skompilowanymi nagłówkami, w którym to przypadku musisz dołączyćstdafx.h
wszystko inne.
Uzasadnienie: Uwzględnienie source1.h
wcześniejszych plików gwarantuje, że może on działać samodzielnie bez swoich zależności. Jeśli source1.h
później przyjmie zależność, kompilator natychmiast powiadomi Cię o dodaniu wymaganych deklaracji przesyłania dalej source1.h
. To z kolei zapewnia, że nagłówki mogą być uwzględniane w dowolnej kolejności przez osoby pozostające na ich utrzymaniu.
Przykład:
źródło1.h
class Class1 {
Class2 c2; // a dependency which has not been forward declared
};
source1.cpp
#include "source1.h" // now compiler will alert you saying that Class2 is undefined
// so you can forward declare Class2 within source1.h
...
Użytkownicy MSVC: Zdecydowanie polecam korzystanie ze wstępnie skompilowanych nagłówków. Więc przenieś wszystkie #include
dyrektywy dla standardowych nagłówków (i innych nagłówków, które nigdy się nie zmienią) na stdafx.h
.
Uwzględnij od najbardziej specyficznego do najmniej określonego, zaczynając od odpowiedniego pliku .hpp dla pliku .cpp, jeśli taki istnieje. W ten sposób zostaną ujawnione wszelkie ukryte zależności w plikach nagłówkowych, które nie są samowystarczalne.
Komplikuje to użycie wstępnie skompilowanych nagłówków. Jednym ze sposobów obejścia tego jest to, że nie czyniąc specyficznego dla kompilatora projektu, można użyć jednego z nagłówków projektu jako wstępnie skompilowanego pliku dołączanego nagłówka.
To trudne pytanie w świecie C / C ++ z tak wieloma elementami poza standardem.
Myślę, że kolejność plików nagłówkowych nie stanowi poważnego problemu, o ile się kompiluje, jak powiedział Squelart.
Moje pomysły są następujące: jeśli nie ma konfliktu symboli we wszystkich tych nagłówkach, każda kolejność jest OK, a problem zależności nagłówka można rozwiązać później, dodając #include wiersze do wadliwego .h.
Prawdziwy kłopot powstaje, gdy jakiś nagłówek zmienia swoje działanie (sprawdzając warunki # if) zgodnie z nagłówkami powyżej.
Na przykład w pliku stddef.h w VS2005 znajduje się:
#ifdef _WIN64
#define offsetof(s,m) (size_t)( (ptrdiff_t)&(((s *)0)->m) )
#else
#define offsetof(s,m) (size_t)&(((s *)0)->m)
#endif
Teraz problem: jeśli mam niestandardowy nagłówek („custom.h”), który musi być używany z wieloma kompilatorami, w tym starszymi, które nie zawierają offsetof
nagłówków systemowych, powinienem napisać w moim nagłówku:
#ifndef offsetof
#define offsetof(s,m) (size_t)&(((s *)0)->m)
#endif
I pamiętaj, aby poinformować użytkownika #include "custom.h"
po wszystkich nagłówkach systemowych, w przeciwnym razie wiersz offsetof
w pliku stddef.h spowoduje błąd redefinicji makra.
Modlimy się, aby nie spotkać się z takimi przypadkami w naszej karierze.