Po pierwsze, aby odpowiedzieć na pierwsze zapytanie:
Kiedy zobaczysz to w pliku .h :
#ifndef FILE_H
#define FILE_H
/* ... Declarations etc here ... */
#endif
Jest to technika preprocesora, która zapobiega wielokrotnemu dołączaniu pliku nagłówkowego, co może być problematyczne z różnych powodów. Podczas kompilacji projektu kompilowany jest każdy plik .cpp (zwykle). Mówiąc prościej, oznacza to, że kompilator weźmie plik .cpp , otworzy wszystkie pliki #includedprzez niego, połączy je wszystkie w jeden ogromny plik tekstowy, a następnie przeprowadzi analizę składni, a na koniec przekonwertuje go na jakiś kod pośredni, zoptymalizuje / wykona inne zadań, a na koniec wygeneruj dane wyjściowe zestawu dla architektury docelowej. Z tego powodu, jeśli plik jest #includedwielokrotnie pod jednym .cppplik, kompilator dwukrotnie dopisze zawartość pliku, więc jeśli w tym pliku znajdują się definicje, pojawi się błąd kompilatora informujący o przedefiniowaniu zmiennej. Gdy plik jest przetwarzany przez krok preprocesora w procesie kompilacji, przy pierwszym osiągnięciu jego zawartości pierwsze dwa wiersze sprawdzają, czy FILE_Hzostał on zdefiniowany dla preprocesora. Jeśli nie, zdefiniuje FILE_Hi będzie kontynuował przetwarzanie kodu między nim a #endifdyrektywą. Następnym razem, gdy zawartość tego pliku zostanie zobaczona przez preprocesor, sprawdzenie FILE_Hbędzie fałszywe, więc natychmiast skanuje w dół do #endifi kontynuuje po nim. Zapobiega to błędom redefinicji.
Aby zająć się drugim problemem:
W programowaniu C ++ jako ogólną praktykę rozdzielamy programowanie na dwa typy plików. Jeden ma rozszerzenie .h i nazywamy go „plikiem nagłówkowym”. Zwykle dostarczają deklaracji funkcji, klas, struktur, zmiennych globalnych, typów definicji, wstępnie przetwarzających makr i definicji, itp. Zasadniczo, po prostu dostarczają informacji o kodzie. Następnie mamy rozszerzenie .cpp, które nazywamy „plikiem kodu”. Zapewni to definicje tych funkcji, składowych klas, dowolnych składowych struktury, które potrzebują definicji, zmiennych globalnych itp. Zatem plik .h deklaruje kod, a plik .cpp implementuje tę deklarację. Z tego powodu zazwyczaj podczas kompilacji kompilujemy każdy plik .cppplik do obiektu, a następnie połącz te obiekty (ponieważ prawie nigdy nie widać jednego pliku .cpp zawierającego inny plik .cpp ).
Sposób rozwiązywania tych zewnętrznych elementów jest zadaniem konsolidatora. Kiedy kompilator przetwarza main.cpp , pobiera deklaracje kodu w class.cpp , dołączając class.h . Musi tylko wiedzieć, jak wyglądają te funkcje lub zmienne (co daje deklaracja). Więc kompiluje twój plik main.cpp do jakiegoś pliku obiektowego (nazwij go main.obj ). Podobnie class.cpp jest kompilowany do pliku class.objplik. Aby utworzyć ostateczny plik wykonywalny, wywoływany jest konsolidator, który łączy te dwa pliki obiektowe ze sobą. W przypadku wszelkich nierozwiązanych zewnętrznych zmiennych lub funkcji kompilator umieści kod pośredniczący w miejscu, w którym następuje dostęp. Konsolidator następnie pobierze ten kod i wyszuka kod lub zmienną w innym wymienionym pliku obiektowym, a jeśli zostanie znaleziony, łączy kod z dwóch plików obiektowych w plik wyjściowy i zastępuje kod pośredniczący ostateczną lokalizacją funkcji lub zmienna. W ten sposób twój kod w main.cpp może wywoływać funkcje i używać zmiennych w class.cpp JEŚLI I TYLKO JEŚLI ZADEKLAROWANE SĄ W class.h .
Mam nadzieję, że to było pomocne.