Parte 2. Arquivos de cabeçalho
...

Ao escrever código, todos nós usamos as regras de formatação de código. Às vezes, as regras são inventadas, em outros casos, guias de estilo prontos são usados. Embora todos os programadores C ++ leiam inglês mais facilmente do que o nativo, é melhor ter um manual neste último.
Este artigo é uma tradução de parte do guia de estilo C ++ do Google para o russo.
Artigo original (fork no github), tradução atualizada .
Arquivos de cabeçalho
É desejável que cada arquivo de origem .cc tenha um arquivo de cabeçalho .h correspondente . Também existem exceções conhecidas a esta regra, como testes de unidade ou pequenos arquivos .cc contendo apenas a função main () .
O uso correto de arquivos de cabeçalho pode ter um grande impacto na legibilidade, tamanho e desempenho de seu código.
As regras a seguir o ajudarão a evitar problemas frequentes com arquivos de cabeçalho.
Arquivos de cabeçalho independentes
Os arquivos de cabeçalho devem ser autossuficientes (em termos de compilação) e ter a extensão .h . Outros arquivos (não de cabeçalho) destinados a serem incluídos no código devem ter a extensão .inc e ser pareados com o código de inclusão.
Todos os arquivos de cabeçalho devem ser autocontidos. Os usuários e as ferramentas de desenvolvimento não devem depender de dependências especiais ao usar o arquivo de cabeçalho. O arquivo de cabeçalho deve ser bloqueável e incluir todos os arquivos necessários.
É preferível colocar as definições de modelos e funções embutidas no mesmo arquivo com suas declarações. E essas definições devem ser incluídas em cada .ccarquivo usando-os, caso contrário, pode haver erros de link em algumas configurações de compilação. Se as declarações e definições estiverem em arquivos diferentes, incluir um deve incluir o outro. Não separe as definições em arquivos de cabeçalho separados ( -inl.h ). Anteriormente, essa prática era muito popular, agora é indesejável.
Como exceção, se todas as variantes disponíveis dos argumentos do modelo forem criadas a partir do modelo, ou se o modelo implementar a funcionalidade usada por apenas uma classe, então é permitido definir o modelo em um (e apenas um) arquivo .cc , no qual este modelo é usado.
Existem raras situações em que o arquivo de cabeçalho não é independente. Isso pode acontecer quando um arquivo é incluído em um local não padrão, como no meio de outro arquivo. Nesse caso, pode não haver bloqueio de reativação e arquivos de cabeçalho adicionais também podem não ser incluídos. Nomeie esses arquivos com a extensão .inc . Use-os em pares e tente atender aos requisitos gerais tanto quanto possível.
Reiniciar bloqueio
Todos os arquivos de cabeçalho devem ser #define protegidos contra reinclusão . O formato de definição da macro deve ser: <PROJETO> _ < Caminho > _ < FILLE > _H_ .
Para garantir exclusividade, use os componentes do caminho de arquivo completo na árvore do projeto. Por exemplo, o arquivo foo / src / bar / baz.h no projeto foo pode ter o seguinte bloqueio:
#ifndef FOO_BAR_BAZ_H_
#define FOO_BAR_BAZ_H_
...
#endif // FOO_BAR_BAZ_H_
Anúncio preliminar
Se possível, não use anúncios antecipados. #Include os arquivos de cabeçalho necessários vez .
Definição
"Pré-declaração" é uma declaração de uma classe, função, modelo sem uma definição correspondente.
Por
- . #include ( ) .
- . #include - .
- , .
- API, . , API: , , .
- std:: .
- , : #include. , #include ( ) :
// b.h: struct B {}; struct D : B {}; // good_user.cc: #include "b.h" void f(B*); void f(void*); void test(D* x) { f(x); } // calls f(B*)
#include B D, test() f(void*). - , .
- Uma estrutura de código que permite a declaração preliminar (e, além disso, usar ponteiros como membros da classe) pode tornar o código confuso e lento.
Veredito
- Tente evitar pré-declarar entidades declaradas em outro projeto.
- Ao usar uma função declarada em um arquivo de cabeçalho, sempre #inclua esse arquivo.
- Ao usar um modelo de classe, é preferível # incluir seu arquivo de cabeçalho.
Veja também as regras para inclusão em Nomes e Pedido de Inclusão .
Funções inline
Defina funções como funções embutidas apenas quando forem pequenas, por exemplo, não mais do que 10 linhas.
Definição
Você pode declarar funções embutidas e dizer ao compilador para incluí-las diretamente no código de chamada, além da maneira padrão de chamar uma função.
Prós O
uso de funções embutidas pode gerar código mais eficiente, especialmente quando as funções são pequenas. Use este recurso para obter / definir funções, outras funções curtas e de desempenho crítico.
Contra
O uso excessivo de funções embutidas pode tornar o programa mais lento. Além disso, as funções embutidas, dependendo de seu tamanho, podem aumentar ou diminuir o tamanho do código. Se forem funções pequenas, o código pode ser reduzido. Se a função for grande, o tamanho do código pode aumentar muito. Observe que em processadores modernos, o código mais enxuto é executado mais rápido devido ao melhor uso do cache de instruções.
Veredicto
Uma boa regra prática é não tornar as funções inlináveis se elas excederem 10 linhas de código. Evite fazer destruidores inline, uma vez que eles podem conter implicitamente muitos códigos extras: chamadas para destruidores de variáveis e classes base!
Outra boa regra prática é que geralmente não faz sentido para funções inline que possuem loops ou instruções switch (exceto em casos degenerados onde o loop ou outras instruções nunca são executados).
É importante entender que uma função embutida não será necessariamente compilada no código dessa maneira. Por exemplo, normalmente as funções virtuais e recursivas são compiladas com uma chamada padrão. Em geral, as funções recursivas não devem ser declaradas funções embutidas. A principal razão para fazer funções virtuais embutidas é colocar a definição (código) na própria definição de classe (para documentar o comportamento ou legibilidade) - freqüentemente usada para obter / definir funções.
Nomes e pedido de inclusão
Insira os arquivos de cabeçalho na seguinte ordem: arquivo emparelhado (por exemplo, foo.h - foo.cc), arquivos de sistema C, biblioteca padrão C ++, outras bibliotecas, seus arquivos de projeto.
Todos os cabeçalhos do projeto devem ser relativos ao diretório de origem do projeto sem usar aliases do UNIX, como . (diretório atual) ou .. (diretório pai). Por exemplo, google-awesome-project / src / base / logging.h deve ser incluído assim:
#include "base/logging.h"
Outro exemplo: se a função principal dos arquivos dir / foo.cc e dir / foo_test.cc for implementar e testar o código declarado em dir2 / foo2.h , escreva os arquivos de cabeçalho na seguinte ordem:
- dir2 / foo2.h .
- —
- C (: .h), <unistd.h>, <stdlib.h>.
- —
- C++ ( ), <algorithm>, <cstddef>.
- —
- .h .
- .h .
Separe cada grupo (não vazio) de arquivos com uma linha vazia.
Esta ordem de arquivos permite detectar erros quando os arquivos de cabeçalho necessários (sistema, etc.) estão faltando no arquivo de cabeçalho emparelhado ( dir2 / foo2.h ) e a montagem dos arquivos dir / foo.cc ou dir / foo_test.cc correspondentes falhará. Como resultado, o erro aparecerá imediatamente para o desenvolvedor que trabalha com esses arquivos (e não para outra equipe que usa apenas uma biblioteca externa).
Normalmente, os arquivos emparelhados dir / foo.cc e dir2 / foo2.h estão no mesmo diretório (por exemplo, base / basictypes_test.cc e base / basictypes.h ), embora isso não seja necessário.
Observe que os arquivos de cabeçalho C, como stddef.h, geralmente são intercambiáveis com os arquivos C ++ correspondentes ( cstddef ). Qualquer variação pode ser usada, mas é melhor seguir o estilo do código existente.
Dentro de cada seção, os arquivos de cabeçalho são melhor listados em ordem alfabética. Observe que o código escrito anteriormente pode não seguir esta regra. Se possível (por exemplo, ao corrigir um arquivo), corrija a ordem dos arquivos para o correto.
Todos os arquivos de cabeçalho que declaram os tipos que você deseja devem ser incluídos, exceto onde declarados anteriormente . Se o seu código usa tipos de bar.h , não conte com outro arquivo foo.h para incluir bar.he você pode se limitar a incluir apenas foo.h : inclua bar.h explicitamente (a menos que seja explicitamente declarado (talvez na documentação) que foo.h também fornecerá os tipos de bar.h ).
Por exemplo, a lista de arquivos de cabeçalho em google-awesome-project / src / foo / internal / fooserver.cc pode ter a seguinte aparência:
#include "foo/server/fooserver.h"
#include <sys/types.h>
#include <unistd.h>
#include <string>
#include <vector>
#include "base/basictypes.h"
#include "base/commandlineflags.h"
#include "foo/server/bar.h"
Exceções
Há casos em que você precisa incluir arquivos de cabeçalho dependendo das condições do pré-processador (por exemplo, dependendo do sistema operacional usado). Tente manter essa inclusão o mais curta (localizada) possível e coloque-a após outros arquivos de cabeçalho. Por exemplo:
#include "foo/public/fooserver.h"
#include "base/port.h" // For LANG_CXX11.
#ifdef LANG_CXX11
#include <initializer_list>
#endif // LANG_CXX11
Notas:
Imagem obtida de código aberto .