Diferença entre implementar uma classe dentro de um arquivo .h ou em um arquivo .cpp

StackOverflow https://stackoverflow.com/questions/1809679

  •  05-07-2019
  •  | 
  •  

Pergunta

Fiquei me perguntando quais são as diferenças entre declarar e implementar uma classe apenas em um arquivo de cabeçalho, em comparação com a abordagem normal na qual você protipo no cabeçalho e implementa no arquivo .cpp eficaz.

Para explicar melhor sobre o que estou falando, quero dizer diferenças entre a abordagem normal:

// File class.h
class MyClass 
{
private:
  //attributes
  public:
  void method1(...);
  void method2(...);
  ...
};

//file class.cpp
#include "class.h"

void MyClass::method1(...) 
{
  //implementation
}

void MyClass::method2(...) 
{
  //implementation
}

e a Just Header abordagem:

// File class.h
class MyClass 
{
private:
  //attributes
public:
  void method1(...) 
  {
      //implementation
  }

  void method2(...) 
  {
    //implementation
  }

  ...
};

Eu posso obter a principal diferença: no segundo caso, o código está incluído em todos os outros arquivos que precisam que ele gerem mais instâncias das mesmas implementações, portanto, uma redundância implícita; enquanto no primeiro caso o código é compilado por si mesmo e depois todas as chamadas referidas ao objeto de MyClass estão ligados à implementação em class.cpp.

Mas existem outras diferenças? É mais conveniente usar uma abordagem em vez de outra, dependendo da situação? Também li em algum lugar que definir o corpo de um método diretamente em um arquivo de cabeçalho é uma solicitação implícita ao compilador para embalar esse método, é verdade?

Foi útil?

Solução

A principal diferença prática é que, se as definições de função do membro estiverem no corpo do cabeçalho, é claro que elas serão compiladas uma vez para cada unidade de tradução, que inclui esse cabeçalho. Quando o seu projeto contém algumas centenas ou milhares de arquivos de origem, e a classe em questão é bastante usada, isso pode significar muita repetição. Mesmo que cada classe seja usada apenas por 2 ou 3 outras, mais código no cabeçalho, mais trabalho a fazer.

Se as definições de função do membro estiverem em uma unidade de tradução (arquivo .cpp), elas serão compiladas uma vez e apenas as declarações de função serão compiladas várias vezes.

É verdade que as funções de membro definidas (não apenas declaradas) na definição de classe são implicitamente inline. Mas inline Não significa o que as pessoas podem adivinhar razoavelmente que isso significa. inline diz que é legal que várias definições da função apareçam em diferentes unidades de tradução e posteriormente sejam vinculadas. Isso é necessário se a classe estiver em um arquivo de cabeçalho que diferentes arquivos de origem vão usar, portanto o idioma tenta ser útil.

inline também é uma dica para o compilador de que a função poderia ser usada, mas, apesar do nome, isso é opcional. Quanto mais sofisticado o seu compilador for, melhor será capaz de tomar suas próprias decisões sobre a inline e, menos a necessidade é de dicas. Mais importante que a tag embutida é se a função está disponível para o compilador. Se a função for definida em uma unidade de tradução diferente, ela não estará disponível quando a chamada para ela será compilada e, portanto, se alguma coisa estiver embutida na chamada, terá que ser o vinculador, não o compilador.

Você pode ver melhor as diferenças, considerando uma terceira maneira possível de fazê -lo:

// File class.h
class MyClass
{
    private:
        //attributes
    public:
       void method1(...);
       void method2(...);
       ...
};

inline void MyClass::method1(...)
{
     //implementation
}

inline void MyClass::method2(...)
{
     //implementation
}

Agora que o inline implícito está fora do caminho, permanecem algumas diferenças entre essa abordagem de "todos os cabeçalhos" e a abordagem "Header Plus Source". Como você divide seu código entre as unidades de tradução tem consequências para o que acontece como é construído.

Outras dicas

Sim, o compilador tentará embalar um método declarado diretamente no arquivo de cabeçalho como:

class A
{
 public:
   void method()
   {
   }
};

Eu posso pensar em seguir as conveniências na separação da implementação nos arquivos de cabeçalho:

  1. Você não terá código Bloat devido ao mesmo código ser incluído em várias unidades de tradução
  2. Seu tempo de compilação reduzirá drasticamente. Lembre -se de que, para qualquer modificação no compilador de arquivos do cabeçalho, deve criar todos os outros arquivos que o incluam direta ou indiretamente. Eu acho que será muito frustrante para alguém construir todo o binário novamente apenas para adicionar um espaço no arquivo de cabeçalho.

Qualquer alteração em um cabeçalho que inclua a implementação forçará todas as outras classes que incluem esse cabeçalho para recompilar e revincular.

Como os cabeçalhos mudam com menos frequência do que as implementações, colocando a implementação em um arquivo separado, você pode economizar tempo de compilação considerável.

Como algumas outras respostas já apontaram, sim, definindo um método dentro de um arquivo class O bloco fará com que o compilador seja embutido.

Sim, definir métodos dentro da definição de classe é equivalente a declará -los inline. Não há outra diferença. Não há benefício em definir tudo no arquivo de cabeçalho.

Algo assim geralmente é visto em C ++ com classes de modelo, pois as definições de membros do modelo também devem ser incluídas no arquivo de cabeçalho (devido ao fato de que a maioria dos compiladores não suporta export). Mas com aulas comuns não-templos, não há sentido em fazer isso, a menos que você realmente queira declarar seus métodos como inline.

Para mim, a principal diferença é que um arquivo de cabeçalho é como uma "interface" para a classe, dizendo aos clientes dessa classe quais são seus métodos públicos (as operações que ele suporta), sem que os clientes se preocupem com a implementação específica desses. No sentido, é uma maneira de encapsular seus clientes das alterações de implementação, porque apenas as alterações do arquivo CPP e, portanto, o tempo de compilação é muito menor.

Uma vez no passado, criei um módulo protegendo a partir de diferenças em várias distribuições do CORBA e esperava -se que trabalhasse uniformemente em várias combinações de OS/compilador/corba. Torná -lo implementado em um arquivo de cabeçalho tornou mais fácil adicioná -lo a um projeto com uma inclusão simples. A mesma técnica garantiu que o código foi recompilado ao mesmo tempo em que o código que chamava de recompilação quando IE estava sendo compilado com uma biblioteca diferente ou em um sistema operacional diferente.

Então, meu argumento é que, se você tem uma pequena biblioteca que deve ser reutilizável e recompilável em vários projetos, tornando -o um cabeçalho oferece vantagens na integração com outros projetos, em vez de adicionar arquivos extras ao projeto principal ou recompilar uma lib externa /arquivo obj.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top