Przewodnik: kompilowanie i importowanie jednostek nagłówków w programie Microsoft Visual C++

Ten artykuł dotyczy kompilowania i importowania jednostek nagłówków za pomocą programu Visual Studio 2022. Aby dowiedzieć się, jak zaimportować standardowe nagłówki bibliotek języka C++ jako jednostki nagłówka, zobacz Przewodnik: importowanie bibliotek STL jako jednostek nagłówka. Aby uzyskać jeszcze szybszy i bardziej niezawodny sposób importowania standardowej biblioteki, zobacz Samouczek: importowanie standardowej biblioteki języka C++ przy użyciu modułów.

Jednostki nagłówka są zalecaną alternatywą dla wstępnie skompilowanych plików nagłówków (PCH). Jednostki nagłówka są łatwiejsze do skonfigurowania i użycia, są znacznie mniejsze na dysku, zapewniają podobne korzyści z wydajności i są bardziej elastyczne niż współdzielona PCH.

Aby porównać jednostki nagłówka z innymi sposobami dołączania funkcji do programów, zobacz Porównanie jednostek nagłówków, modułów i wstępnie skompilowanych nagłówków.

Wymagania wstępne

Aby użyć jednostek nagłówka, potrzebujesz programu Visual Studio 2019 16.10 lub nowszego.

Co to jest jednostka nagłówka

Jednostka nagłówka to binarna reprezentacja pliku nagłówka. Jednostka nagłówka .ifc kończy się rozszerzeniem. Ten sam format jest używany dla nazwanych modułów.

Ważną różnicą między jednostką nagłówka a plikiem nagłówka jest to, że jednostka nagłówka nie ma wpływu na definicje makr poza jednostką nagłówka. Oznacza to, że nie można zdefiniować symbolu preprocesora, który powoduje, że jednostka nagłówka zachowuje się inaczej. Po zaimportowaniu jednostki nagłówka jednostka nagłówka jest już skompilowana. Różni się to od sposobu #include traktowania pliku. Uwzględniony plik może mieć wpływ na definicję makr poza plikiem nagłówka, ponieważ plik nagłówka przechodzi przez preprocesor podczas kompilowania pliku źródłowego, który go zawiera.

Jednostki nagłówka można zaimportować w dowolnej kolejności, co nie jest prawdziwe w przypadku plików nagłówków. Kolejność plików nagłówka ma znaczenie, ponieważ definicje makr zdefiniowane w jednym pliku nagłówka mogą mieć wpływ na kolejny plik nagłówka. Definicje makr w jednej jednostce nagłówka nie mogą mieć wpływu na inną jednostkę nagłówka.

Wszystko widoczne z pliku nagłówka jest również widoczne w jednostce nagłówka, w tym makra zdefiniowane w jednostce nagłówka.

Aby można było zaimportować plik nagłówka, należy przetłumaczyć go na jednostkę nagłówka. Zaletą jednostek nagłówków w prekompilowanych plikach nagłówków (PCH) jest to, że mogą być używane w kompilacjach rozproszonych. Jeśli skompilujesz program .ifc i program, który importuje go z tym samym kompilatorem, i jest przeznaczony dla tej samej platformy i architektury, jednostka nagłówkowa utworzona na jednym komputerze może być zużywana na innym. W przeciwieństwie do PCH, gdy jednostka nagłówka ulegnie zmianie, tylko ona i to, co zależy od niego, zostaną ponownie skompilowane. Jednostki nagłówka mogą być do wielkości mniejszej niż .pch.

Jednostki nagłówka nakładają mniej ograniczeń na wymagane podobieństwa kombinacji przełącznika kompilatora używane do tworzenia jednostki nagłówka i kompilowania kodu, który go używa niż PCH. Jednak niektóre kombinacje przełączników i definicje makr mogą powodować naruszenia jednej reguły definicji (ODR) między różnymi jednostkami tłumaczenia.

Na koniec jednostki nagłówka są bardziej elastyczne niż PCH. Za pomocą PCH nie można wybrać, aby wprowadzić tylko jeden z nagłówków w PCH - kompilator przetwarza wszystkie z nich. W przypadku jednostek nagłówka, nawet gdy kompilujesz je razem w bibliotece statycznej, zawartość jednostki nagłówka importowanej do aplikacji jest dostarczana tylko do aplikacji.

Jednostki nagłówka to krok między plikami nagłówków i modułami języka C++20. Zapewniają one niektóre korzyści z modułów. Są one bardziej niezawodne, ponieważ definicje makr zewnętrznych nie wpływają na nie — dzięki czemu można je zaimportować w dowolnej kolejności. Kompilator może przetwarzać je szybciej niż pliki nagłówkowe. Jednak jednostki nagłówków nie mają wszystkich zalet modułów, ponieważ jednostki nagłówka uwidaczniają makra zdefiniowane w nich (moduły nie). W przeciwieństwie do modułów nie ma możliwości ukrycia prywatnej implementacji w jednostce nagłówka. Aby wskazać prywatną implementację z plikami nagłówków, stosowane są różne techniki, takie jak dodawanie wiodących podkreśleń do nazw lub umieszczanie elementów w przestrzeni nazw implementacji. Moduł nie uwidacznia prywatnej implementacji w żadnej formie, więc nie trzeba tego robić.

Rozważ zastąpienie wstępnie skompilowanych nagłówków jednostkami nagłówka. Zyskujesz tę samą szybkość, ale z innymi korzyściami w higienie kodu i elastycznością.

Sposoby kompilowania jednostki nagłówka

Istnieje kilka sposobów kompilowania pliku w jednostce nagłówka:

  • Skompiluj udostępniony projekt jednostki nagłówka. Zalecamy takie podejście, ponieważ zapewnia większą kontrolę nad organizacją i ponowne użycie zaimportowanych jednostek nagłówków. Utwórz projekt biblioteki statycznej zawierający żądane jednostki nagłówka, a następnie odwołaj się do niego, aby zaimportować jednostki nagłówka. Aby zapoznać się z przewodnikiem tego podejścia, zobacz Tworzenie projektu biblioteki statycznej jednostki nagłówka dla jednostek nagłówka.

  • Wybierz poszczególne pliki do tłumaczenia na jednostki nagłówka. Takie podejście zapewnia kontrolę nad tym, co jest traktowane jako jednostka nagłówka. Jest to również przydatne, gdy musisz skompilować plik jako jednostkę nagłówka, ponieważ nie ma domyślnego rozszerzenia (.ixx, .cppm, .h, .hpp), zwykle nie zostanie skompilowane w jednostce nagłówka. To podejście przedstawiono w tym przewodniku. Aby rozpocząć, zobacz Podejście 1: tłumaczenie określonego pliku na jednostkę nagłówka.

  • Automatycznie skanuj pod kątem jednostek nagłówka i kompiluj je. Takie podejście jest wygodne, ale najlepiej nadaje się do mniejszych projektów, ponieważ nie gwarantuje optymalnej przepływności kompilacji. Aby uzyskać szczegółowe informacje na temat tego podejścia, zobacz Podejście 2: automatyczne skanowanie pod kątem jednostek nagłówka.

  • Jak wspomniano we wprowadzeniu, można kompilować i importować pliki nagłówków STL jako jednostki nagłówka i automatycznie traktować #include nagłówki bibliotekI STL bez import ponownego zapisywania kodu. Aby dowiedzieć się, jak to zrobić, odwiedź stronę Przewodnik: importowanie bibliotek STL jako jednostek nagłówka.

Podejście 1. Tłumaczenie określonego pliku na jednostkę nagłówka

W tej sekcji pokazano, jak wybrać określony plik do tłumaczenia na jednostkę nagłówka. Skompiluj plik nagłówka jako jednostkę nagłówka, wykonując następujące kroki w programie Visual Studio:

  1. Utwórz nowy projekt aplikacji konsolowej języka C++.

  2. Zastąp zawartość pliku źródłowego w następujący sposób:

    #include "Pythagorean.h"
    
    int main()
    {
        PrintPythagoreanTriple(2,3);
        return 0;
    }
    
  3. Dodaj plik nagłówka o nazwie Pythagorean.h , a następnie zastąp jego zawartość następującym kodem:

    #ifndef PYTHAGOREAN
    #define PYTHAGOREAN
    
    #include <iostream>
    
    inline void PrintPythagoreanTriple(int a, int b)
    {
        std::cout << "Pythagorean triple a:" << a << " b:" << b << " c:" << a*a + b*b << std::endl;
    }
    #endif
    

Ustawianie właściwości projektu

Aby włączyć jednostki nagłówka, najpierw ustaw język C++ Language Standard na /std:c++20 lub nowszy, wykonując następujące kroki:

  1. W Eksplorator rozwiązań kliknij prawym przyciskiem myszy nazwę projektu i wybierz polecenie Właściwości.
  2. W lewym okienku okna stron właściwości projektu wybierz pozycję Właściwości>konfiguracji Ogólne.
  3. Na liście rozwijanej C++ Language Standard wybierz pozycję ISO C++20 Standard (/std:c++20) lub nowszą. Wybierz przycisk OK , aby zamknąć okno dialogowe.

Skompiluj plik nagłówka jako jednostkę nagłówka:

  1. W Eksplorator rozwiązań wybierz plik, który chcesz skompilować jako jednostkę nagłówka (w tym przypadku Pythagorean.h). Kliknij plik prawym przyciskiem myszy i wybierz polecenie Właściwości.

  2. Ustaw listę rozwijaną Właściwości>konfiguracji Ogólny>typ elementu na kompilator C/C++, a następnie wybierz przycisk OK.

    Screenshot that shows changing the item type to C/C++ compiler.

Podczas kompilowania tego projektu w dalszej części tego przewodnika Pythagorean.h zostanie przetłumaczony na jednostkę nagłówka. Jest on tłumaczony na jednostkę nagłówka, ponieważ typ elementu dla tego pliku nagłówka jest ustawiony na kompilator języka C/C++, a ponieważ domyślną akcją dla .h plików i .hpp ustawionymi w ten sposób jest przetłumaczenie pliku na jednostkę nagłówka.

Uwaga

Nie jest to wymagane w tym przewodniku, ale podano informacje. Aby skompilować plik jako jednostkę nagłówka, która nie ma domyślnego rozszerzenia pliku jednostki nagłówka, na .cpp przykład ustaw właściwości>konfiguracji C/C++>Advanced>Compile JakoKompilowanie jako jednostka nagłówka języka C++ (/exportHeader):Screenshot that shows changing Configuration properties > C/C++ > Advanced > Compile As to Compile as C++ Header Unit (/exportHeader).

Zmienianie kodu w celu zaimportowania jednostki nagłówka

  1. W pliku źródłowym przykładowego projektu zmień wartość #include "Pythagorean.h"import "Pythagorean.h"; na Nie zapomnij o końcowym średnik. Jest to wymagane w przypadku instrukcji import . Ponieważ jest to plik nagłówka w katalogu lokalnym dla projektu, użyliśmy cudzysłowów z instrukcją import : import "file";. We własnych projektach, aby skompilować jednostkę nagłówka z nagłówka systemu, użyj nawiasów kątowych: import <file>;

  2. Skompiluj rozwiązanie, wybierając pozycję Kompiluj>rozwiązanie kompilacji w menu głównym. Uruchom go, aby zobaczyć, że generuje oczekiwane dane wyjściowe: Pythagorean triple a:2 b:3 c:13

We własnych projektach powtórz ten proces, aby skompilować pliki nagłówkowe, które chcesz zaimportować jako jednostki nagłówka.

Jeśli chcesz przekonwertować tylko kilka plików nagłówka na jednostki nagłówka, takie podejście jest dobre. Jeśli jednak masz wiele plików nagłówkowych, które chcesz skompilować, a potencjalna utrata wydajności kompilacji jest przeważona przez wygodę automatycznego obsługi ich przez system kompilacji, zobacz następującą sekcję.

Jeśli interesuje Cię szczególnie importowanie nagłówków bibliotekI STL jako jednostek nagłówków, zobacz Przewodnik: importowanie bibliotek STL jako jednostek nagłówka.

Podejście 2. Automatyczne skanowanie pod kątem jednostek nagłówka i kompilacja

Ponieważ skanowanie wszystkich plików źródłowych w jednostkach nagłówka i tworzenie ich zajmuje trochę czasu, poniższe podejście najlepiej nadaje się do mniejszych projektów. Nie gwarantuje optymalnej przepływności kompilacji.

To podejście łączy dwa ustawienia projektu programu Visual Studio:

  • Skanowanie źródeł dla zależności modułu powoduje, że system kompilacji wywołuje kompilator, aby upewnić się, że wszystkie zaimportowane moduły i jednostki nagłówkowe są kompilowane przed skompilowaniem plików, które są od nich zależne. W połączeniu z funkcją Translate Includes to Import wszystkie pliki nagłówków zawarte w źródle, które są również określone w header-units.json pliku znajdującym się w tym samym katalogu co plik nagłówkowy, są kompilowane w jednostkach nagłówka.
  • Translate Includes to Import traktuje plik nagłówka jako import , jeśli #include odnosi się do pliku nagłówka, który można skompilować jako jednostkę nagłówka (jak określono w header-units.json pliku), a skompilowana jednostka nagłówka jest dostępna dla pliku nagłówka. W przeciwnym razie plik nagłówka jest traktowany jako normalny #include. Plik header-units.json jest używany do automatycznego kompilowania jednostek nagłówka dla każdego #includeelementu bez duplikowania symboli.

Te ustawienia można włączyć we właściwościach projektu. W tym celu kliknij prawym przyciskiem myszy projekt w Eksplorator rozwiązań i wybierz polecenie Właściwości. Następnie wybierz pozycję Właściwości>konfiguracji C/C++>General.

Screenshot that shows the project properties screen with Configuration highlighted and All Configurations selected. Under C/C++ > General, Scan Sources for Module Dependencies is highlighted and set to yes, and Translate Includes to Imports is highlighted and set to Yes (/translateInclude)

Źródła skanowania dla zależności modułu można ustawić dla wszystkich plików w projekcie we właściwościach projektu, jak pokazano tutaj, lub dla poszczególnych plików we właściwościach pliku. Moduły i jednostki nagłówka są zawsze skanowane. Ustaw tę opcję, gdy masz .cpp plik, który importuje jednostki nagłówka, które mają zostać skompilowane automatycznie i może jeszcze nie zostać skompilowane.

Te ustawienia współpracują ze sobą, aby automatycznie kompilować i importować jednostki nagłówka w następujących warunkach:

  • Skanuj źródła dla zależności modułu skanuje źródła plików i ich zależności, które mogą być traktowane jako jednostki nagłówka. Pliki, które mają rozszerzenie .ixx, i pliki, które mają ich właściwości>Plik C/C++Compile As właściwości ustawioną na Wartość Kompiluj jako jednostkę nagłówka języka C++> (/export), są zawsze skanowane niezależnie od tego ustawienia. Kompilator szuka import również instrukcji do identyfikowania zależności jednostek nagłówka. Jeśli /translateInclude zostanie określony, kompilator skanuje #include również pod kątem header-units.json dyrektyw, które są również określone w pliku w celu traktowania jako jednostek nagłówka. Wykres zależności jest kompilowany ze wszystkich modułów i jednostek nagłówków w projekcie.
  • Translate Includes to Import Gdy kompilator napotka instrukcję #include , a odpowiedni plik jednostki nagłówka (.ifc) istnieje dla określonego pliku nagłówka, kompilator importuje jednostkę nagłówka zamiast traktować plik nagłówka #includejako . W połączeniu ze skanowaniem pod kątem zależności kompilator znajduje wszystkie pliki nagłówkowe, które można skompilować w jednostkach nagłówka. Lista dozwolonych jest konsultowana przez kompilator, aby zdecydować, które pliki nagłówkowe mogą być kompilowane w jednostkach nagłówka. Ta lista jest przechowywana w header-units.json pliku, który musi znajdować się w tym samym katalogu co dołączony plik. Przykład pliku można zobaczyć header-units.json w katalogu instalacyjnym programu Visual Studio. Na przykład jest używany przez kompilator do określenia, %ProgramFiles%\Microsoft Visual Studio\2022\Enterprise\VC\Tools\MSVC\14.30.30705\include\header-units.json czy można skompilować nagłówek standardowej biblioteki szablonów do jednostki nagłówka. Ta funkcja istnieje, aby służyć jako mostek ze starszym kodem, aby uzyskać pewne korzyści z jednostek nagłówka.

Plik header-units.json służy do dwóch celów. Oprócz określenia, które pliki nagłówków można skompilować w jednostkach nagłówka, minimalizuje zduplikowane symbole w celu zwiększenia przepływności kompilacji. Aby uzyskać więcej informacji na temat duplikowania symboli, zobacz dokumentacja pliku header-units.json języka C++.

Te przełączniki i header-unit.json zapewniają niektóre zalety jednostek nagłówka. Wygoda wiąże się z kosztem przepływności kompilacji. Takie podejście może nie być najlepsze w przypadku większych projektów, ponieważ nie gwarantuje optymalnych czasów kompilacji. Ponadto te same pliki nagłówkowe mogą być ponownie przetwarzane wielokrotnie, co zwiększa czas kompilacji. Jednak wygoda może być warta w zależności od projektu.

Te funkcje są przeznaczone dla starszego kodu. W przypadku nowego kodu przejdź do modułów zamiast jednostek nagłówka lub #include plików. Aby zapoznać się z samouczkiem dotyczącym używania modułów, zobacz Samouczek dotyczący modułów nazw (C++).

Aby zapoznać się z przykładem użycia tej techniki do importowania plików nagłówków STL jako jednostek nagłówka, zobacz Przewodnik: importowanie bibliotek STL jako jednostek nagłówka.

Implikacje preprocesora

Standardowy preprocesor zgodny ze standardem C99/C++11 jest wymagany do tworzenia i używania jednostek nagłówka. Kompilator włącza nowy preprocesor zgodny z językiem C99/C++11 podczas kompilowania jednostek nagłówka, niejawnie dodając /Zc:preprocessor do wiersza polecenia zawsze, gdy jest używana dowolna /exportHeader forma. Próba jej wyłączenia spowoduje błąd kompilacji.

Włączenie nowego preprocesora wpływa na przetwarzanie makr wariadycznych. Aby uzyskać więcej informacji, zobacz sekcję uwagi makr wariadycznych.

Zobacz też

/translateInclude
/exportHeader
/headerUnit
header-units.json
Porównanie jednostek nagłówka, modułów i wstępnie skompilowanych nagłówków
Omówienie modułów w języku C++
Samouczek: importowanie standardowej biblioteki języka C++ przy użyciu modułów
Przewodnik: importowanie bibliotek STL jako jednostek nagłówka