Nowy w C ++! Czytałem więc: http://www.learncpp.com/cpp-tutorial/110-a-first-look-at-the-preprocessor/
Nagłówki
Ponieważ pliki nagłówkowe mogą zawierać inne pliki nagłówkowe, możliwe jest, że plik nagłówkowy zostanie dołączony wiele razy.
Tworzymy więc wytyczne preprocesora, aby tego uniknąć. Ale nie jestem pewien - dlaczego kompilator nie może po prostu ... nie importować tego samego dwa razy?
Biorąc pod uwagę, że osłony nagłówków są opcjonalne (ale najwyraźniej to dobra praktyka), prawie każę myśleć, że istnieją sytuacje, w których chcesz zaimportować coś dwukrotnie. Chociaż w ogóle nie mogę wymyślić żadnego takiego scenariusza. Jakieś pomysły?
#pragma once
kompilator, który dołącza ten plik tylko raz.Odpowiedzi:
Mogą, jak pokazują nowe języki, które to robią.
Ale decyzja projektowa została podjęta wiele lat temu (kiedy kompilator C był wieloma niezależnymi etapami) i teraz, aby zachować zgodność, preprocesor musi działać w określony sposób, aby upewnić się, że stary kod kompiluje się zgodnie z oczekiwaniami.
Ponieważ C ++ dziedziczy sposób przetwarzania plików nagłówkowych po C, zachował te same techniki. Wspieramy starą decyzję projektową. Jednak zmiana sposobu działania jest zbyt ryzykowna, ponieważ wiele kodów może potencjalnie ulec uszkodzeniu. Więc teraz musimy nauczyć nowych użytkowników języka, jak korzystać ze strażników.
Istnieje kilka sztuczek z plikami nagłówkowymi, jeśli celowo dołączasz je wiele razy (w rzeczywistości zapewnia to przydatną funkcję). Chociaż, jeśli przeprojektowaliśmy paradygmat od zera, moglibyśmy uczynić z niego domyślny sposób dołączania plików.
źródło
W przeciwnym razie nie byłby tak wyrazisty, biorąc pod uwagę, że zdecydowali się zachować zgodność z C, a tym samym kontynuować preprocesor zamiast tradycyjnego systemu pakowania.
Jedna rzecz, która przychodzi mi na myśl, to to, że miałem projekt, który był API. Miałem dwa pliki nagłówkowe
x86lib.h
ix86lib_internal.h
. Ponieważ wewnętrzny był ogromny, posegregowałem „publiczne” bity do x86lib.h, aby użytkownicy nie musieli poświęcać dodatkowego czasu na kompilację.To wprowadziło zabawny problem z zależnościami, więc skończyłem z przepływem, który wyglądał tak w x86lib_internal
Nie powiedziałbym, że to najlepszy sposób, aby to zrobić, ale osiągnął to, co chciałem.
źródło
Jedną trudnością związaną z automatycznym wykluczaniem duplikatów nagłówków jest to, że standard C relatywnie milczy na temat tego, co oznaczają nazwy plików. Załóżmy na przykład, że kompilowany plik główny zawiera dyrektywy
#include "f1.h"
i#include "f2.h"
, a pliki znalezione dla tych dyrektyw zawierają oba#include "f3.h"
. Jeślif1.h
if2.h
znajdują się w różnych katalogach, ale zostały znalezione podczas wyszukiwania ścieżek dołączania, wówczas nie byłoby jasne, czy#include
dyrektywy w tych plikach miały ładować ten samf3.h
plik lub różne.Sytuacja staje się jeszcze gorsza, jeśli dodaje się możliwości dołączania plików, w tym ścieżek względnych. W niektórych przypadkach, w których pliki nagłówkowe używają ścieżek względnych dla zagnieżdżonych dyrektyw zawierających, i gdy chce się uniknąć wprowadzania jakichkolwiek zmian w dostarczonych plikach nagłówkowych, może być konieczne zduplikowanie pliku nagłówkowego w wielu miejscach w strukturze katalogów projektu. Mimo że istnieje wiele fizycznych kopii tego pliku nagłówka, należy je traktować semantycznie, jakby były pojedynczym plikiem.
Jeśli
#pragma once
dyrektywa zezwala na podążanie za identyfikatoremonce
, z semantyką, że kompilator powinien pominąć plik, jeśli identyfikator odpowiada jednemu z wcześniej napotkanej#pragma once
dyrektywy, to semantyka byłaby jednoznaczna; kompilator, który mógłby stwierdzić, że#include
dyrektywa załaduje ten sam#pragma once
plik ze znacznikami jak wcześniejszy, może zaoszczędzić trochę czasu, pomijając plik bez ponownego otwierania go, ale takie wykrycie nie będzie semantycznie ważne, ponieważ plik zostanie pominięty, czy lub nie nazwa pliku została rozpoznana jako pasująca. Nie znam jednak żadnych kompilatorów działających w ten sposób. Posiadanie kompilatora obserwuje, czy plik pasuje do wzorca#ifndef someIdentifier / #define someIdentifier / #endif [for that ifndef] / nothing following
i traktuje taką rzecz jako równoważną powyższemu#pragma once someIdentifier
ifsomeIdentifier
pozostaje zdefiniowany, jest zasadniczo tak dobry.źródło