2014-12-02 16 views
7

Używam #pragma once (lub korzystasz z załączników à #ifndef...) zasadniczo w każdym pliku nagłówkowym w moich projektach C++. Czy jest to przypadek, czy też jest to coś, co można znaleźć na przykład w większości projektów typu open source (aby uniknąć odpowiedzi, które opierają się tylko na osobistym doświadczeniu projektu). Jeśli tak, dlaczego nie jest na odwrót: jeśli chcę, aby plik nagłówkowy był dołączany kilka razy, używam tego samego specjalnego polecenia przed procesorem, a jeśli nie, to zostawiam ten plik bez zmian.Dlaczego nie ma osłony w C++ domyślnie?

+8

'#pragma once' nie jest standardem; '# ifndef/# define/# endif' to. –

+3

@ T.C. Ok. Ale pytanie pozostaje w zasadzie takie samo. – NOhs

+0

Możesz rzucić okiem na '' (i ''). Dwa nagłówki, które zostałyby uszkodzone, gdyby '#pragma once' lub podobne były automatyczne. (Chociaż nadal byłoby to sprzeczne z prostotą i prostotą, nie czyniąc tego w sposób niezwykły niemożliwym, to znaczy, że można dodać '#pragma redo' lub coś podobnego.) – Deduplicator

Odpowiedz

7

Zachowanie kompilatora C++ określa sposób obsługi każdej jednostki tłumaczeniowej. Jednostka tłumaczeniowa jest pojedynczym plikiem po uruchomieniu preprocesora. Fakt, że mamy konwencję zbierania deklaracji w niektórych plikach i nazywania ich plikami "nagłówkowymi" nic nie znaczy dla kompilatora lub standardu C++.

Po prostu standard nie przewiduje "plików nagłówkowych", więc nie może automatycznie uwzględniać pilnowania plików nagłówkowych. Standard przewiduje tylko dyrektywę preprocesora #include, a reszta to tylko konwencja. Nic nie powstrzyma cię od deklarowania wszystkiego i nie używania plików nagłówkowych (z wyjątkiem szkód dla tych, którzy powinni zachować ten kod ...).

Więc pliki nagłówkowe nie są specjalne i nie ma sposobu, aby powiedzieć "to plik nagłówka, strzeż go", ale dlaczego nie możemy strzec wszystkiego, co dostaje #include "d? Ponieważ #include jest zarówno bardziej, jak i mniej wydajnie niż system modułowy, tak jak inne języki. #include powoduje, że preprocesor wkleja się do innych plików, niekoniecznie do plików nagłówkowych. Czasami może to być przydatne, jeśli masz takie same deklaracje użycia i typedef w grupie różnych przestrzeni nazw w różnych plikach. Możesz je zebrać w pliku, a następnie w kilku miejscach. Nie chcesz, aby automatyczne włączanie ochrony uniemożliwiało ci to.

Stosowanie #ifndef i #define do warunkowego uwzględniania nagłówków jest również jedynie konwencją. Standard nie ma pojęcia "włącz straży". (Współczesne kompilatory jednak faktycznie są świadomi zawierać strażników. Uznając to strażnicy mogą umożliwić szybszą kompilację, ale to nie ma nic wspólnego z odpowiednim wdrażaniu standardu.)

Pierwsze pedantyczne

Norma ma hojnie używać słowa "header", szczególnie w odniesieniu do standardowych bibliotek C i C++. Jednak zachowanie #include jest zdefiniowane pod § 16.2 *Source file* inclusion (emph. Mine) i nie przyznaje żadnych specjalnych uprawnień do plików nagłówkowych.

Dołożono starań, aby uzyskać odpowiedni system modułów w standardzie C++.

+1

Istnieje "nagłówek" i "plik źródłowy". "nagłówki" nie muszą być rzeczywistymi plikami. –

+0

Naprawdę? Powiedz. Czy to dlatego, że standard pozwala, aby rzeczy w <> były dostarczane przez jakieś zdefiniowane sposoby implementacji? – Praxeolitic

+4

Prawo (lub nieokreślone, a następnie określona implementacja). Wszystko, co jest wymagane, to napisanie '#include ' powoduje wyświetlenie odpowiednich deklaracji i definicji. Kompilator może mieć specjalną obsługę, aby rozpoznać standardowe nazwy nagłówków i mieć na nich swoją zawartość, lub może pobrać fragment danych AST z bazy danych obiektów ...lub może tylko sprawić, że preprocesor będzie tekstowo dołączał plik (co w praktyce robią wszystkie popularne implementacje). –

4

Ponieważ histeryczne rodzynki.

Procesor C++ jest prawie identyczny z preprocesorem C, który został zaprojektowany ponad 40 lat temu. Kompilatory były wtedy znacznie prostsze. Preprocesor był jeszcze prostszy, po prostu głupi procesor makro, a nawet kompilator. Chociaż standard C++ nie określa sposobu działania dla standardowych nagłówków, koncepcyjnie #include jest wciąż taki sam jak był 40+ lat temu: powoduje, że preprocesor wstawia zawartość nazwanego pliku do pliku włączającego.

W prostej bazie kodu C z lat 70. bez wielu współzależności i podmodułów może nie być potrzeby stosowania osłon. Wczesne przed-standardowe C nie używało prototypów funkcyjnych, do czego większość z tych plików jest używana w dzisiejszych czasach. Jeśli dwukrotne dodanie nagłówka spowodowało błąd, prawdopodobnie mógłbyś ponownie ustawić kod, aby zapobiec jego dwukrotnemu dołączeniu.

W miarę jak wzrastała i stawała się coraz bardziej skomplikowana, zakładam, że problem przypadkowego włączenia nagłówka dwukrotnie (prawdopodobnie pośrednio, poprzez inne nagłówki) stał się bardziej powszechny. Jednym z rozwiązań byłoby zmodyfikowanie preprocesora, aby uczynić go bardziej inteligentnym, ale wymagałoby to od wszystkich użytkowników korzystania z nowego preprocesora i spowodowałoby, że będzie on większy i wolniejszy. Zamiast tego opracowano konwencję, która rozwiązuje problem, wykorzystując istniejące funkcje preprocesora (#define i #ifndef), nie zapobiegając dwukrotnemu dołączeniu nagłówka, lecz po prostu nie czyniąc tego, aby dwukrotnie umieścić nagłówek, ponieważ nie ma żadnego efektu po pierwszym czas jest włączony.

Z biegiem czasu konwencja stała się szerzej stosowana i obecnie jest prawie uniwersalna, z wyjątkiem rzadkich przykładów nagłówków, które zostały zaprojektowane w taki sposób, aby można je było dwukrotnie uwzględnić i które zostały poprawnie napisane w ten sposób (np. <assert.h>).

Później nadal, #pragma once został wprowadzony przez niektóre kompilatory jako alternatywny, nieprzenośny sposób, aby uzyskać ten sam efekt, co obejmuje strażników, ale wtedy było wiele tysięcy kopii różnych preprocesorów C w użyciu wokół słowa i obejmują strażnicy stali się normą.

Obecne zachowanie jest prawie na pewno spowodowane względami historycznymi. Współczesne języki pisane dzisiaj, w dzisiejszych niewiarygodnie potężnych komputerach, nie używałyby czegoś takiego jak preprocesor C, jeśli zaprojektowano go od zera. Ale C nie zostało zaprojektowane w XXI wieku. Sądzę, że konwencja strażników została stworzona powoli w czasie i nie wymagała żadnych zmian w istniejącym oprogramowaniu, aby to działało. Zmiana teraz złamie nieznane ilości kodu C i C++, które opierają się na bieżącym zachowaniu i prawdopodobnie nie jest opcją, ponieważ zgodność wsteczna jest ważna zarówno dla C jak i C++.

0

Muszę zgodzić się, że głównym czynnikiem jest historyczny, że czasami widzisz kod, który polega na tym, że nie są tam. MAME jest jednym z przykładów: tworzy złożone struktury danych (lub przynajmniej ostatni raz, kiedy wyglądałem, jakiś czas temu) z czytelnego pliku opartego na makrach, włączając go wielokrotnie z makrami zdefiniowanymi inaczej. Jeśli włączono osłony automatyczne, natknąłeś się na kod, który wymagałby sposobu na ich wyłączenie.

Powiązane problemy