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:
- kod wbudowany
- szablony
- stałe (zwykle te, których chcesz używać wewnątrz przełączników ...)
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).
- Deklaracje forward
- deklaracje / definicje funkcji / struktur / klas / szablonów
- implementacja kodu wbudowanego i szablonu
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ć:
- nagłówek deklaracji do przodu
- nagłówek deklaracji / definicji
- nagłówek implementacji
- źródło implementacji
Wyobraźmy sobie, że mamy szablon MyObject. Moglibyśmy mieć:
template<typename T>
class MyObject ;
.
#include <MyObject_forward.hpp>
template<typename T>
class MyObject
{
public :
MyObject() ;
} ;
void doSomething() ;
.
#include <MyObject_declaration.hpp>
template<typename T>
MyObject<T>::MyObject()
{
doSomething() ;
}
.
#include <MyObject_implementation.hpp>
void doSomething()
{
} ;
Ł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 ...
^ _ ^