Separando o código de classe em um cabeçalho e arquivo cpp

170

Estou confuso sobre como separar código de implementação e declarações de uma classe simples em um novo cabeçalho e arquivo cpp. Por exemplo, como eu separaria o código da classe a seguir?

class A2DD
{
  private:
  int gx;
  int gy;

  public:
  A2DD(int x,int y)
  {
    gx = x;
    gy = y;
  }

  int getSum()
  {
    return gx + gy;
  }
};
drdrdr
fonte
12
Apenas alguns comentários: O construtor sempre deve usar uma lista de inicialização em vez de definir os membros no corpo. Para uma explicação boa e simples, consulte: codeguru.com/forum/showthread.php?t=464084 Também é, pelo menos na maioria dos lugares, habitual ter o campo público no topo. Isso não afetará nada, mas como os campos públicos são a documentação da sua turma, faz sentido ter isso no topo.
martiert
2
@martiert Ter public:membros no topo pode afetar muito , se o usuário os mover de acordo com este conselho - mas tiver dependências de pedidos entre os membros e ainda não estiver ciente de que os membros são inicializados na ordem de sua declaração ;-)
underscore_d
1
@underscore_d isso é verdade. Mas, novamente, estamos todos compilando avisos como erros e todos os avisos que podemos pensar, certo? Isso, pelo menos, dizer-lhe que você está enroscando isso, mas sim, as pessoas usam forma de pequenos avisos, e simplesmente ignorá-los :(
martiert
@martiert Bom ponto, meio que esqueci que gera avisos - se apenas os avisos fossem lidos pela maioria :-) Eu os uso e tento codificá-los todos. Alguns são inevitáveis ​​- então eu digo 'obrigado pelo aviso, mas eu sei o que estou fazendo!' - mas a maioria é melhor corrigida para evitar confusão mais tarde.
underscore_d
Ter campos públicos no topo é apenas um estilo que, infelizmente, muitos adotaram na minha opinião. Além disso, você deve ter em mente algumas coisas, como o @martiert mencionado.
Vassilis

Respostas:

233

A declaração de classe entra no arquivo de cabeçalho. É importante adicionar os #ifndefprotetores de inclusão ou, se você estiver em uma plataforma MS, também poderá usar #pragma once. Também omiti o privado, por padrão, os membros da classe C ++ são privados.

// A2DD.h
#ifndef A2DD_H
#define A2DD_H

class A2DD
{
  int gx;
  int gy;

public:
  A2DD(int x,int y);
  int getSum();

};

#endif

e a implementação está no arquivo CPP:

// A2DD.cpp
#include "A2DD.h"

A2DD::A2DD(int x,int y)
{
  gx = x;
  gy = y;
}

int A2DD::getSum()
{
  return gx + gy;
}
Ferenc Deak
fonte
53
Lembre-se de que, se você estiver fazendo programação de modelos, precisará manter tudo no arquivo .h para que o compilador instancie o código correto no momento da compilação.
Linux 6/03/12
2
você tem as #ifndefcoisas no cabeçalho?
Ferenc Deak
4
Portanto, isso significa que todos os arquivos que incluem o arquivo de cabeçalho "verão" os membros privados. Se, por exemplo, você deseja publicar uma biblioteca e seu cabeçalho, você precisa mostrar os membros privados da classe?
Gauthier
1
Não, existe o maravilhoso idioma de implementação privada: en.wikipedia.org/wiki/Opaque_pointer Você pode usá-lo para ocultar os detalhes da implementação.
Ferenc Deak
3
Pequen nitpick com o texto: "A declaração de classe entra no arquivo de cabeçalho". Esta é realmente uma declaração, mas também é uma definição, mas como a última inclui a primeira, prefiro dizer que a definição de classe entra no arquivo de cabeçalho. Na unidade de tradução, você tem a definição das funções de membro, não a definição da classe. Concordo que isso pode valer uma pequena edição?
lubgr
17

Em geral, seu .h contém a definição de classe, que é todos os seus dados e todas as suas declarações de método. Assim no seu caso:

A2DD.h:

class A2DD
{
  private:
  int gx;
  int gy;

  public:
  A2DD(int x,int y);    
  int getSum();
};

E então o seu .cpp contém as implementações dos métodos como este:

A2DD.cpp:

A2DD::A2DD(int x,int y)
{
  gx = x;
  gy = y;
}

int A2DD::getSum()
{
  return gx + gy;
}
usuario
fonte
7

É importante ressaltar aos leitores que se deparam com essa pergunta ao pesquisar o assunto de maneira mais ampla que o procedimento da resposta aceita não é necessário no caso de você apenas querer dividir seu projeto em arquivos. Só é necessário quando você precisa de várias implementações de classes únicas. Se sua implementação por classe for uma, basta apenas um arquivo de cabeçalho para cada uma.

Portanto, a partir do exemplo da resposta aceita, apenas esta parte é necessária:

#ifndef MYHEADER_H
#define MYHEADER_H

//Class goes here, full declaration AND implementation

#endif

As definições de pré-processador #ifndef etc. permitem que ele seja usado várias vezes.

PS. O tópico fica mais claro depois que você percebe que C / C ++ é 'idiota' e #include é apenas uma maneira de dizer "despejar este texto neste local".

j riv
fonte
você poderia fazer isso colocando os arquivos "divididos" .cppou apenas é .hrealmente "bom" para esse método de organização de código?
Benny Jobigan
1
Eu pensei que alguns projetos dividissem os arquivos de cabeçalho e (únicos) de implementação, para que pudessem distribuir os arquivos de cabeçalho facilmente, sem revelar o código fonte das implementações.
Carl G
Estou tão feliz que você apontou isso porque, originalmente, aprendi sobre C ++ e depois mudei para C # há muitos anos e recentemente fiz muito C ++ novamente. os cabeçalhos .Eu estava procurando por alguém que apresentasse boas razões para NÃO fazer isso quando eu encontrei isso. O @CarlG tem um bom argumento, mas, fora esse cenário, acho que fazer tudo em linha é o caminho a percorrer.
Peter Moore
6

Basicamente, uma sintaxe modificada da declaração / definição da função:

a2dd.h

class A2DD
{
private:
  int gx;
  int gy;

public:
  A2DD(int x,int y);

  int getSum();
};

a2dd.cpp

A2DD::A2DD(int x,int y)
{
  gx = x;
  gy = y;
}

int A2DD::getSum()
{
  return gx + gy;
}
Corbin
fonte
5

A2DD.h

class A2DD
{
  private:
  int gx;
  int gy;

  public:
  A2DD(int x,int y);

  int getSum();
};

A2DD.cpp

  A2DD::A2DD(int x,int y)
  {
    gx = x;
    gy = y;
  }

  int A2DD::getSum()
  {
    return gx + gy;
  }

A idéia é manter todas as assinaturas de funções e membros no arquivo de cabeçalho.
Isso permitirá que outros arquivos do projeto vejam como é a classe sem precisar conhecer a implementação.

Além disso, você pode incluir outros arquivos de cabeçalho na implementação, em vez do cabeçalho. Isso é importante porque quaisquer cabeçalhos incluídos no arquivo de cabeçalho serão incluídos (herdados) em qualquer outro arquivo que inclua o arquivo de cabeçalho.

Yochai Timmer
fonte
4

Você deixa as declarações no arquivo de cabeçalho:

class A2DD
{
  private:
  int gx;
  int gy;

  public:
    A2DD(int x,int y); // leave the declarations here
    int getSum();
};

E coloque as definições no arquivo de implementação.

A2DD::A2DD(int x,int y) // prefix the definitions with the class name
{
  gx = x;
  gy = y;
}

int A2DD::getSum()
{
  return gx + gy;
}

Você pode misturar os dois (deixe a getSum()definição no cabeçalho, por exemplo). Isso é útil, pois fornece ao compilador uma melhor chance de incluir, por exemplo. Mas isso também significa que alterar a implementação (se deixada no cabeçalho) pode desencadear uma reconstrução de todos os outros arquivos que incluem o cabeçalho.

Observe que, para os modelos, você precisa manter tudo nos cabeçalhos.

Esteira
fonte
1
Colocar membros e funções privadas no arquivo de cabeçalho não é considerado vazar detalhes da implementação?
Jason As
1
@ Jason, mais ou menos. Esses são os detalhes de implementação necessários . Por exemplo, eu tenho que saber quanto espaço uma classe consumirá na pilha. Implementações de funções não são necessárias para outras unidades de compilação.
Paul Draper
1

Normalmente, você coloca apenas declarações e funções embutidas muito curtas no arquivo de cabeçalho:

Por exemplo:

class A {
 public:
  A(); // only declaration in the .h unless only a short initialization list is used.

  inline int GetA() const {
    return a_;
  }

  void DoSomethingCoplex(); // only declaration
  private:
   int a_;
 };
Ivaylo Strandjev
fonte
0

Também não vou referir o seu exemplo, pois é bastante simples para uma resposta geral (por exemplo, não contém funções modeladas , o que força você a implementá-las no cabeçalho), o que sigo como regra geral é o pimpl idioma

Tem alguns benefícios à medida que você obtém tempos de compilação mais rápidos e o açúcar sintático:

class->member ao invés de class.member

A única desvantagem é o ponteiro extra que você paga.

Spyros Mourelatos
fonte