Podczas dzielenia kodu na wiele plików, co dokładnie powinno znaleźć się w pliku .h, a co w pliku .cpp?
c++
header-files
Enrico Tuvera Jr
źródło
źródło
.hpp
pliku, podczas gdy deklaracje C trafiają do.h
pliku. Jest to bardzo przydatne podczas mieszania kodu C i C ++ (na przykład starsze moduły w C).Odpowiedzi:
Pliki nagłówkowe (
.h
) mają na celu dostarczenie informacji, które będą potrzebne w wielu plikach. Rzeczy takie jak deklaracje klas, prototypy funkcji i wyliczenia zwykle znajdują się w plikach nagłówkowych. Jednym słowem „definicje”.Pliki kodu (
.cpp
) są przeznaczone do dostarczania informacji o implementacji, które muszą być znane tylko w jednym pliku. Ogólnie rzecz biorąc, treści funkcji i zmienne wewnętrzne, do których inne moduły nie powinny / nigdy nie będą miały dostępu, należą do.cpp
plików. Jednym słowem „realizacje”.Najprostszym pytaniem, które należy sobie zadać, aby określić, co należy do miejsca, jest „czy jeśli to zmienię, będę musiał zmienić kod w innych plikach, aby ponownie się skompilować?” Jeśli odpowiedź brzmi „tak”, prawdopodobnie należy ona do pliku nagłówkowego; jeśli odpowiedź brzmi „nie”, prawdopodobnie należy ona do pliku kodu.
źródło
export
). Jedynym sposobem obejścia nr 1 jest PIMPL. # 2 byłby możliwy, gdybyexport
był obsługiwany i może być możliwy przy użyciu języka c ++ 0x iextern
szablonów. IMO, pliki nagłówkowe w języku c ++ tracą wiele ze swojej użyteczności.Faktem jest, że w C ++ jest to nieco bardziej skomplikowane niż organizacja nagłówka / źródła C.
Co widzi kompilator?
Kompilator widzi jeden duży plik źródłowy (.cpp) z poprawnie dołączonymi nagłówkami. Plik źródłowy to jednostka kompilacji, która zostanie wkompilowana w plik obiektowy.
Dlaczego więc potrzebne są nagłówki?
Ponieważ jedna jednostka kompilacji może potrzebować informacji o implementacji w innej jednostce kompilacji. Można więc napisać na przykład implementację funkcji w jednym źródle, a deklarację tej funkcji w innym źródle, aby jej użyć.
W takim przypadku są dwie kopie tych samych informacji. Co jest złe ...
Rozwiązaniem jest udostępnienie kilku szczegółów. Podczas gdy implementacja powinna pozostać w źródle, deklaracja współdzielonych symboli, takich jak funkcje lub definicja struktur, klas, wyliczeń itp., Może wymagać udostępnienia.
Nagłówki służą do umieszczania tych udostępnionych szczegółów.
Przenieś do nagłówka deklaracje tego, co należy udostępniać między wieloma źródłami
Nic więcej?
W C ++ jest kilka innych rzeczy, które można umieścić w nagłówku, ponieważ one również muszą być udostępnione:
Przejdź do nagłówka WSZYSTKO, co ma być udostępnione, w tym udostępnione implementacje
Czy to oznacza, że w nagłówkach mogą znajdować się źródła?
Tak. W rzeczywistości istnieje wiele różnych rzeczy, które mogą znajdować się w „nagłówku” (tj. Współdzielone między źródłami).
Staje się to skomplikowane, aw niektórych przypadkach (zależności cykliczne między symbolami) niemożliwe jest przechowywanie go w jednym nagłówku.
Nagłówki można podzielić na trzy części
Oznacza to, że w skrajnym przypadku możesz mieć:
Wyobraźmy sobie, że mamy szablon MyObject. Moglibyśmy mieć:
// - - - - MyObject_forward.hpp - - - - // This header is included by the code which need to know MyObject // does exist, but nothing more. template<typename T> class MyObject ;
.
// - - - - MyObject_declaration.hpp - - - - // This header is included by the code which need to know how // MyObject is defined, but nothing more. #include <MyObject_forward.hpp> template<typename T> class MyObject { public : MyObject() ; // Etc. } ; void doSomething() ;
.
// - - - - MyObject_implementation.hpp - - - - // This header is included by the code which need to see // the implementation of the methods/functions of MyObject, // but nothing more. #include <MyObject_declaration.hpp> template<typename T> MyObject<T>::MyObject() { doSomething() ; } // etc.
.
// - - - - MyObject_source.cpp - - - - // This source will have implementation that does not need to // be shared, which, for templated code, usually means nothing... #include <MyObject_implementation.hpp> void doSomething() { // etc. } ; // etc.
Łał!
W „prawdziwym życiu” jest to zwykle mniej skomplikowane. Większość kodu ma tylko prostą organizację nagłówka / źródła, z pewnym kodem wbudowanym w źródle.
Ale w innych przypadkach (obiekty oparte na szablonach znające się nawzajem) musiałem mieć dla każdego obiektu oddzielne nagłówki deklaracji i implementacji, z pustym źródłem zawierającym te nagłówki, aby pomóc mi zobaczyć niektóre błędy kompilacji.
Innym powodem podzielenia nagłówków na oddzielne nagłówki może być przyspieszenie kompilacji, ograniczenie liczby analizowanych symboli do ściśle niezbędnego poziomu i uniknięcie niepotrzebnej rekompilacji źródła, które dba tylko o deklarację do przodu, gdy zmieniła się implementacja metody wbudowanej.
Wniosek
Powinieneś uczynić swoją organizację kodu zarówno jak najprostszą, jak to tylko możliwe, i jak najbardziej modułową. Umieść jak najwięcej w pliku źródłowym. Ujawniaj w nagłówkach tylko to, co należy udostępnić.
Ale w dniu, w którym będziesz mieć cykliczne zależności między obiektami szablonowymi, nie zdziw się, jeśli organizacja Twojego kodu stanie się nieco bardziej „interesująca” niż zwykła organizacja nagłówka / źródła ...
^ _ ^
źródło
oprócz wszystkich innych odpowiedzi powiem ci, czego NIE umieszczasz w pliku nagłówkowym:
using
deklaracja (najczęściej spotykanausing namespace std;
) nie powinna pojawiać się w pliku nagłówkowym, ponieważ zanieczyszczają przestrzeń nazw pliku źródłowego, w którym jest zawarta .źródło
using
do umieszczania rzeczy w globalnej przestrzeni nazw w nagłówku.static inline
używałbyś w C99, ponieważ ma to coś wspólnego z tym, co dzieje się, gdy łączysz wewnętrzne powiązania z szablonami. Przestrzenie nazw Anon umożliwiają „ukrywanie” funkcji przy zachowaniu powiązań zewnętrznych.To, co kompiluje się do niczego (zerowy ślad binarny), trafia do pliku nagłówkowego.
Zmienne nie kompilują się do niczego, ale deklaracje typów tak robią (ponieważ opisują one tylko zachowanie zmiennych).
funkcje nie, ale funkcje wbudowane (lub makra), ponieważ tworzą kod tylko wtedy, gdy są wywoływane.
szablony nie są kodem, są jedynie receptą na tworzenie kodu. więc są również umieszczane w plikach h.
źródło
Ogólnie deklaracje umieszcza się w pliku nagłówkowym, a definicje w pliku implementacji (.cpp). Wyjątkiem są szablony, w przypadku których definicja musi również znajdować się w nagłówku.
To i podobne pytanie jest często zadawane w SO - zobacz Dlaczego pliki nagłówkowe i pliki .cpp są w C ++? i pliki nagłówkowe C ++, na przykład separacja kodu.
źródło
Deklaracje klas i funkcji, a także dokumentacja i definicje funkcji / metod wbudowanych (chociaż niektórzy wolą umieścić je w oddzielnych plikach .inl).
źródło
Głównie plik nagłówkowy zawiera szkielet lub deklarację klasy (nie zmienia się często)
a plik cpp zawiera implementację klasy (często się zmienia).
źródło
plik nagłówkowy (.h) powinien być przeznaczony na deklaracje klas, struktur i ich metod, prototypów itp. Implementacja tych obiektów odbywa się w cpp.
w .h
class Foo { int j; Foo(); Foo(int) void DoSomething(); }
źródło
Spodziewałbym się zobaczyć:
prawdziwa odpowiedź brzmi jednak, czego nie należy umieszczać:
źródło
Nagłówek Definiuje coś ale nic nie mówi o implementacji. (Z wyłączeniem szablonów w tym „metaforze”.
Mając to na uwadze, musisz podzielić „definicje” na podgrupy, w tym przypadku istnieją dwa rodzaje definicji.
Teraz oczywiście mówię o pierwszej podgrupie.
Nagłówek jest po to, aby zdefiniować układ Twojej struktury, aby pomóc reszcie oprogramowania w użyciu implementacji. Możesz chcieć to potraktować jako „abstrakcję” swojej implementacji, co jest grzecznie powiedziane, ale myślę, że w tym przypadku pasuje całkiem dobrze.
Jak powiedzieli i pokazali poprzednie postery, deklarują prywatne i publiczne obszary użytkowania oraz ich nagłówki, obejmuje to również zmienne prywatne i publiczne. Nie chcę tutaj zajmować się projektowaniem kodu, ale możesz rozważyć, co umieścisz w swoich nagłówkach, ponieważ jest to warstwa między użytkownikiem końcowym a implementacją.
źródło
źródło
Nagłówek (.h)
Treść (.cpp)
Z reguły umieszcza się „współdzieloną” część modułu w .h (część, którą inne moduły muszą widzieć), a „nieudostępnioną” część w .cpp
PD: Tak, dołączyłem zmienne globalne. Używałem ich już kilka razy i ważne jest, aby nie definiować ich w nagłówkach, w przeciwnym razie otrzymasz wiele modułów, z których każdy będzie definiował własną zmienną.
EDYCJA: Zmodyfikowano po komentarzu Davida
źródło