Compartilhar via


Passo a passo: Compilar e importar unidades de cabeçalho no Microsoft Visual C++

Este artigo trata da criação e importação de unidades de cabeçalho com o Visual Studio 2022. Para saber como importar cabeçalhos de biblioteca padrão C++ como unidades de cabeçalho, consulte Demonstra Passo a passo: Importar bibliotecas STL como unidades de cabeçalho. Para obter uma maneira ainda mais rápida e robusta de importar a biblioteca padrão, consulte Tutorial: Importar a biblioteca padrão C++ usando módulos.

As unidades de cabeçalho são a alternativa recomendada para arquivos de cabeçalho pré-compilado (PCH). As unidades de cabeçalho são mais fáceis de configurar e usar, são significativamente menores em disco, fornecem benefícios de desempenho semelhantes e são mais flexíveis que um PCH compartilhado.

Para contrastar unidades de cabeçalho com outras maneiras de incluir funcionalidade em seus programas, consulte Comparar unidades de cabeçalho, módulos e cabeçalhos pré-compilados.

Pré-requisitos

Para usar unidades de cabeçalho, é necessário o Visual Studio 2019 16.10 ou posterior.

O que é uma unidade de cabeçalho

Uma unidade de cabeçalho é uma representação binária de um arquivo de cabeçalho. Uma unidade de cabeçalho termina com uma extensão .ifc. O mesmo formato é usado para módulos nomeados.

Uma diferença importante entre uma unidade de cabeçalho e um arquivo de cabeçalho é que uma unidade de cabeçalho não é afetada por definições de macro fora dela. Ou seja, você não pode definir um símbolo de pré-processador que faça com que a unidade de cabeçalho se comporte de forma diferente. Quando você importa a unidade de cabeçalho, ela já estará compilada. Isso é diferente de como um #include arquivo é tratado. Um arquivo incluído pode ser afetado por uma definição de macro fora do arquivo de cabeçalho porque o arquivo de cabeçalho passa pelo pré-processador quando você compila o arquivo de origem que o inclui.

As unidades de cabeçalho podem ser importadas em qualquer ordem, o que não é verdade para arquivos de cabeçalho. A ordem do arquivo de cabeçalho é importante porque as definições de macro definidas em um arquivo de cabeçalho podem afetar um arquivo de cabeçalho subsequente. As definições de macro em uma unidade de cabeçalho não podem afetar outra unidade de cabeçalho.

Tudo visível de um arquivo de cabeçalho também é visível de uma unidade de cabeçalho, incluindo macros definidas dentro da unidade de cabeçalho.

Um arquivo de cabeçalho deve ser convertido em uma unidade de cabeçalho antes de ser importado. Uma vantagem das unidades de cabeçalho sobre os arquivos de cabeçalho pré-compilados (PCH) é que eles podem ser usados em compilações distribuídas. Contanto que você compile o e o programa que o importa com o .ifc mesmo compilador e direcione a mesma plataforma e arquitetura, uma unidade de cabeçalho produzida em um computador pode ser consumida em outro. Ao contrário de uma PCH, quando uma unidade de cabeçalho muda, apenas ela e o que depende dela são reconstruídos. As unidades de cabeçalho podem ser até uma ordem de grandeza menor em tamanho do que um .pcharquivo .

As unidades de cabeçalho impõem menos restrições às semelhanças necessárias das combinações de comutador do compilador usadas para criar a unidade de cabeçalho e compilar o código que a consome do que uma PCH. No entanto, algumas combinações de switch e definições de macro podem criar violações da regra de uma definição (ODR) entre várias unidades de tradução.

Por fim, as unidades de cabeçalho são mais flexíveis que um PCH. Com um PCH, você não pode optar por colocar apenas um dos cabeçalhos no PCH — o compilador processa todos eles. Com unidades de cabeçalho, mesmo quando você as compila em uma biblioteca estática, somente é importado para o aplicativo o conteúdo da unidade de cabeçalho.

As unidades de cabeçalho são uma etapa entre os arquivos de cabeçalho e os módulos C++20. Elas fornecem alguns dos benefícios dos módulos. Eles são mais robustos porque as definições de macro externas não os afetam, portanto, você pode importá-los em qualquer ordem. E o compilador pode processá-las mais rápido que arquivos de cabeçalho. Mas as unidades de cabeçalho não têm todas as vantagens dos módulos porque as unidades de cabeçalho expõem as macros definidas dentro delas (módulos não). Ao contrário dos módulos, não há como ocultar a implementação privada em uma unidade de cabeçalho. Para indicar a implementação particular com arquivos de cabeçalho, diferentes técnicas são empregadas, como adicionar sublinhados à esquerda dos nomes ou colocar coisas em um namespace de implementação. Um módulo não expõe a implementação privada de forma alguma, então você não precisa fazer isso.

Considere substituir seus cabeçalhos pré-compilados por unidades de cabeçalho. Você obtém a mesma vantagem de velocidade, mas com outros benefícios de higiene e flexibilidade de código também.

Maneiras de compilar uma unidade de cabeçalho

Há várias maneiras de compilar um arquivo em uma unidade de cabeçalho:

  • Crie um projeto de unidade de cabeçalho compartilhado. Recomendamos essa abordagem porque ela fornece mais controle sobre a organização e a reutilização das unidades de cabeçalho importadas. Crie um projeto de biblioteca estática que contenha as unidades de cabeçalho desejadas e faça referência a ele para importar as unidades de cabeçalho. Para obter um passo a passo dessa abordagem, consulte Criar um projeto de biblioteca estática de unidade de cabeçalho para unidades de cabeçalho.

  • Escolha arquivos individuais para traduzir em unidades de cabeçalho. Essa abordagem fornece controle arquivo por arquivo sobre o que é tratado como uma unidade de cabeçalho. Também é útil quando você precisa compilar um arquivo como uma unidade de cabeçalho que, como ele não tem a extensão padrão (.ixx, .cppm, .h, .hpp), normalmente não seria compilado em uma unidade de cabeçalho. Essa abordagem é demonstrada neste passo a passo. Para começar, consulte Abordagem 1: Traduzir um arquivo específico em uma unidade de cabeçalho.

  • Examinar e criar automaticamente unidades de cabeçalho. Essa abordagem é conveniente, mas é mais adequada para projetos menores porque não garante a taxa de transferência de build ideal. Para obter detalhes sobre essa abordagem, consulte a Abordagem 2: examinar automaticamente as unidades de cabeçalho.

  • Conforme mencionado na introdução, você pode criar e importar arquivos de cabeçalho STL como unidades de cabeçalho e tratar #include automaticamente cabeçalhos de biblioteca STL como import sem reescrever seu código. Para ver como, acesse Passo a passo: Importar bibliotecas STL como unidades de cabeçalho.

Abordagem 1: Traduzir um arquivo específico em uma unidade de cabeçalho

Esta seção mostra como escolher um arquivo específico para traduzir em uma unidade de cabeçalho. Compile um arquivo de cabeçalho como uma unidade de cabeçalho usando as etapas a seguir no Visual Studio:

  1. Crie um novo projeto de aplicativo de console C++.

  2. Substitua o conteúdo do arquivo de origem da seguinte maneira:

    #include "Pythagorean.h"
    
    int main()
    {
        PrintPythagoreanTriple(2,3);
        return 0;
    }
    
  3. Adicione um arquivo de cabeçalho chamado Pythagorean.h e substitua seu conteúdo por este código:

    #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
    

Definir propriedades do projeto

Para habilitar unidades de cabeçalho, primeiro defina o padrão de linguagem C++ para /std:c++20 ou posterior com as seguintes etapas:

  1. No Gerenciador de Soluções, clique com o botão direito do mouse no projeto e escolha Propriedades.
  2. No painel esquerdo da janela páginas de propriedades do projeto, selecione Propriedades de Configuração>Geral.
  3. Na lista suspensa Padrão de linguagem C++ , selecione Padrão ISO C++20 (/std:c++20) ou posterior. Escolha Ok para fechar o diálogo.

Compile o arquivo de cabeçalho como uma unidade de cabeçalho:

  1. Em Gerenciador de Soluções, selecione o arquivo que deseja compilar como uma unidade de cabeçalho (nesse caso, Pythagorean.h). Clique com o botão direito do mouse no arquivo e escolha Propriedades.

  2. Defina a lista suspensa Propriedades de configuração>Geral>Tipo de item para Compilador C/C++ e escolha Ok.

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

Quando você compilar este projeto posteriormente nesta explicação passo a passo, Pythagorean.h será convertido em uma unidade de cabeçalho. Ele é convertido em uma unidade de cabeçalho porque o tipo de item para esse arquivo de cabeçalho é definido como compilador C/C++ e porque a ação padrão para .h e .hpp arquivos definidos dessa maneira é converter o arquivo em uma unidade de cabeçalho.

Observação

Isso não é necessário para este passo a passo, mas é fornecido para suas informações. Para compilar um arquivo como uma unidade de cabeçalho que não tenha uma extensão de arquivo de unidade de cabeçalho padrão, como por exemplo, defina as propriedades>de configuração C/C++>Advanced>Compile As para compilar como .cpp unidade de cabeçalho C++ (/exportHeader):Screenshot that shows changing Configuration properties > C/C++ > Advanced > Compile As to Compile as C++ Header Unit (/exportHeader).

Alterar o código para importar a unidade de cabeçalho

  1. No arquivo de origem do projeto de exemplo, altere #include "Pythagorean.h" para import "Pythagorean.h"; Não esquecer o ponto-e-vírgula à direita. É obrigatório para import declarações. Como é um arquivo de cabeçalho em um diretório local para o projeto, usamos aspas com a import instrução: import "file";. Em seus próprios projetos, para compilar uma unidade de cabeçalho a partir de um cabeçalho do sistema, use colchetes angulares: import <file>;

  2. Compile a solução selecionando Compilar>Compilar Solução no menu principal. Execute-a para ver se ela produz a saída esperada: Pythagorean triple a:2 b:3 c:13

Em seus próprios projetos, repita esse processo para compilar os arquivos de cabeçalho que você deseja importar como unidades de cabeçalho.

Se quiser converter apenas alguns arquivos de cabeçalho em unidades de cabeçalho, essa será uma boa abordagem. Mas se você tiver muitos arquivos de cabeçalho que deseja compilar e a perda potencial de desempenho de compilação for compensada pela conveniência de ter o sistema de compilação manipulá-los automaticamente, consulte a seção a seguir.

Se você estiver interessado especificamente em importar cabeçalhos da biblioteca STL como unidades de cabeçalho, confira Passo a passo: importar bibliotecas STL como unidades de cabeçalho.

Abordagem 2: examinar e criar automaticamente unidades de cabeçalho

Como leva tempo para verificar todos os arquivos de origem em busca de unidades de cabeçalho e tempo para criá-los, a abordagem a seguir é mais adequada para projetos menores. Ele não garante a taxa de transferência de compilação ideal.

Essa abordagem combina duas configurações de projeto do Visual Studio:

  • Verificar fontes de dependências de módulo faz com que o sistema de build chame o compilador para garantir que todos os módulos importados e unidades de cabeçalho sejam criados antes de compilar os arquivos que dependem deles. Quando combinado com Traduzir Inclui para importações, todos os arquivos de cabeçalho incluídos na origem que também são especificados em um header-units.json arquivo localizado no mesmo diretório que o arquivo de cabeçalho são compilados em unidades de cabeçalho.
  • Translate Includes to Imports trata um arquivo de cabeçalho como um import, caso #include se refira a um arquivo de cabeçalho que pode ser compilado como uma unidade de cabeçalho (conforme especificado em um arquivo header-units.json) e uma unidade de cabeçalho compilado está disponível para o arquivo de cabeçalho. Caso contrário, o arquivo de cabeçalho é tratado como #include normal. O header-units.json arquivo é usado para criar automaticamente unidades de cabeçalho para cada #include, sem duplicação de símbolos.

Você pode ativar essas configurações nas propriedades do projeto. Para fazer isso, clique no projeto com o botão direito do mouse no Gerenciador de Soluções e escolha Propriedades. Em seguida, escolha Propriedades de Configuração>C/C++>Geral.

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)

Verificar fontes de dependências de módulo podem ser definidas para todos os arquivos no projeto nas Propriedades do Projeto, conforme mostrado aqui, ou para arquivos individuais nas Propriedades do Arquivo. Módulos e unidades de cabeçalho são sempre verificados. Defina essa opção quando você tiver um arquivo .cpp que importe unidades de cabeçalho que quer que sejam criadas automaticamente e talvez ainda não tenham sido criadas.

Essas configurações funcionam juntas para compilar e importar automaticamente unidades de cabeçalho nessas condições:

  • O Scan Sources for Module Dependencies verifica as fontes em busca dos arquivos e suas dependências que podem ser tratadas como unidades de cabeçalho. Os arquivos que têm a extensão .ixxe os arquivos que têm suas propriedades>File C/C++Compile As definidas como Compile as C++> Header Unit (/export), são sempre verificados, independentemente dessa configuração. O compilador também procura instruções import para identificar dependências de unidade de cabeçalho. Se /translateInclude for especificado, o compilador também examinará as diretivas de #include que também são especificadas em um arquivo header-units.json para tratar como unidades de cabeçalho. É criado um grafo de dependência a partir de todos os módulos e unidades de cabeçalho em seu projeto.
  • Traduzir Inclui para Importações. Quando o compilador encontra uma instrução #include e existir um arquivo de unidade de cabeçalho correspondente (.ifc) para o arquivo de cabeçalho especificado, o compilador importará a unidade de cabeçalho em vez de tratar o arquivo de cabeçalho como um #include. Quando combinado com Verificar dependências, o compilador localiza todos os arquivos de cabeçalho que podem ser compilados em unidades de cabeçalho. Uma lista de permissões é consultada pelo compilador para decidir quais arquivos de cabeçalho podem ser compilados em unidades de cabeçalho. Essa lista é armazenada em um arquivo header-units.json que deve estar no mesmo diretório que o arquivo incluído. Você poderá ver um exemplo de um arquivo header-units.json no diretório de instalação do Visual Studio. Por exemplo, %ProgramFiles%\Microsoft Visual Studio\2022\Enterprise\VC\Tools\MSVC\14.30.30705\include\header-units.json é usado pelo compilador para determinar se um cabeçalho da Biblioteca de Modelos Padrão pode ser compilado em uma unidade de cabeçalho. Essa funcionalidade existe para servir como uma ponte com código herdado para obter alguns benefícios das unidades de cabeçalho.

O arquivo header-units.json tem duas finalidades. Além de especificar quais arquivos de cabeçalho podem ser compilados em unidades de cabeçalho, ele minimiza símbolos duplicados para aumentar a taxa de transferência de build. Para obter mais informações sobre a duplicação de símbolo, consulte Referência de header-units.json do C++.

Esses switches e header-unit.json fornecem alguns dos benefícios das unidades de cabeçalho. A conveniência vem ao custo da taxa de transferência de build. Essa abordagem pode não ser a melhor para projetos maiores porque não garante tempos de build ideais. Além disso, os mesmos arquivos de cabeçalho podem ser reprocessados repetidamente, o que aumenta o tempo de build. No entanto, a conveniência pode valer a pena dependendo do projeto.

Esses recursos foram projetados para código herdado. Para um novo código, mova para módulos em vez de unidades de cabeçalho ou arquivos #include. Para obter um tutorial sobre como usar módulos, consulte o Tutorial de módulos de nome (C++).

Para obter um exemplo de como essa técnica é usada para importar arquivos de cabeçalho STL como unidades de cabeçalho, consulte Passo a passo: importar bibliotecas STL como unidades de cabeçalho.

Implicações para o pré-processador

O pré-processador padrão C99/C++11 em conformidade é necessário para criar e usar unidades de cabeçalho. O compilador habilita o novo pré-processador C99/C++11 em conformidade ao compilar unidades de cabeçalho adicionando /Zc:preprocessor implicitamente à linha de comando sempre que qualquer forma de /exportHeader seja usada. Tentar desativá-lo resultará em um erro de compilação.

Habilitar o novo pré-processador afeta o processamento de macros variádicas. Para obter mais informações, consulte a seção comentários sobre Macros variádicas.

Confira também

/translateInclude
/exportHeader
/headerUnit
header-units.json
Comparar unidades de cabeçalho, módulos e cabeçalhos pré-compilados
Visão geral dos módulos em C++
Tutorial: Importar a biblioteca padrão do C++ usando módulos
Passo a passo: Importar bibliotecas STL como unidades de cabeçalho