Separar el código de clase en un encabezado y archivo cpp

170

Estoy confundido sobre cómo separar el código de implementación y declaraciones de una clase simple en un nuevo encabezado y archivo cpp. Por ejemplo, ¿cómo separaría el código para la siguiente clase?

class A2DD
{
  private:
  int gx;
  int gy;

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

  int getSum()
  {
    return gx + gy;
  }
};
drdrdr
fuente
12
Solo un par de comentarios: el constructor siempre debe usar una lista de inicialización en lugar de establecer los miembros en el cuerpo. Para una explicación buena y simple, consulte: codeguru.com/forum/showthread.php?t=464084 También es, al menos en la mayoría de los lugares, habitual tener el campo público en la parte superior. No afectará nada, pero dado que los campos públicos son la documentación de su clase, tiene sentido tener eso en la parte superior.
Martiert
2
@martiert Tener public:miembros en la parte superior podría afectar mucho , si el usuario los movió de acuerdo con este consejo, pero tenía dependencias de pedido entre los miembros y aún no sabía que los miembros se inicializan en el orden de su declaración ;-)
underscore_d
1
@underscore_d eso es cierto. Pero, de nuevo, todos estamos compilando advertencias como errores y todas las advertencias que podemos pensar, ¿verdad? Eso al menos te dirá que estás arruinando esto, pero sí, la gente usa pequeñas advertencias, y simplemente las ignora :(
martiert
@martiert Buen punto, olvidé un poco que genera advertencias, si la mayoría solo leen las advertencias :-) Las uso y trato de codificarlas. Algunos son inevitables, así que digo 'gracias por la advertencia, ¡pero sé lo que estoy haciendo!' - pero la mayoría se soluciona mejor para evitar confusiones más tarde.
underscore_d
Tener campos públicos en la parte superior es solo un estilo, que muchos adoptaron desafortunadamente en mi opinión. Además, debe tener en cuenta algunas cosas como mencionó @martiert.
Vassilis

Respuestas:

233

La declaración de clase va al archivo de encabezado. Es importante que agregue los #ifndefprotectores de inclusión, o si está en una plataforma MS también puede usar #pragma once. También he omitido lo privado, por defecto los miembros de la clase C ++ son privados.

// A2DD.h
#ifndef A2DD_H
#define A2DD_H

class A2DD
{
  int gx;
  int gy;

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

};

#endif

y la implementación va en el archivo CPP:

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

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

int A2DD::getSum()
{
  return gx + gy;
}
Ferenc Deak
fuente
53
Recuerde que si está haciendo programación de plantillas, debe mantener todo en el archivo .h para que el compilador cree una instancia del código correcto en el momento de la compilación.
linello
2
¿Tienes las #ifndefcosas en el encabezado?
Ferenc Deak
44
Esto significa que todos los archivos que incluyen su archivo de encabezado "verán" a los miembros privados. Si, por ejemplo, desea publicar una biblioteca y su encabezado, ¿tiene que mostrar los miembros privados de la clase?
Gauthier el
1
No, existe el maravilloso idioma privado de implementación: en.wikipedia.org/wiki/Opaque_pointer Puede usarlo para ocultar los detalles de la implementación.
Ferenc Deak
3
Minipick con la frase: "La declaración de clase va al archivo de encabezado". De hecho, esta es una declaración, pero también es una definición, pero como esta última incluye la primera, prefiero decir que la definición de clase va al archivo de encabezado. En la unidad de traducción, tiene la definición de las funciones miembro, no la definición de la clase. Estoy de acuerdo, ¿esto podría valer una pequeña edición?
lubgr
17

En general, su .h contiene la definición de clase, que son todos sus datos y todas sus declaraciones de método. Así en tu caso:

A2DD.h:

class A2DD
{
  private:
  int gx;
  int gy;

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

Y luego, su .cpp contiene las implementaciones de los siguientes métodos:

A2DD.cpp:

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

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

Es importante señalar a los lectores que se encuentran con esta pregunta cuando investigan el tema de manera más amplia que el procedimiento de respuesta aceptado no es necesario en el caso de que solo quieran dividir su proyecto en archivos. Solo es necesario cuando necesita múltiples implementaciones de clases individuales. Si su implementación por clase es una, solo un archivo de encabezado para cada uno es suficiente.

Por lo tanto, del ejemplo de la respuesta aceptada solo se necesita esta parte:

#ifndef MYHEADER_H
#define MYHEADER_H

//Class goes here, full declaration AND implementation

#endif

Las definiciones de preprocesador #ifndef etc. permiten que se use varias veces.

PD. El tema se vuelve más claro una vez que te das cuenta de que C / C ++ es 'tonto' y #include es simplemente una forma de decir "volcar este texto en este lugar".

j riv
fuente
¿podría hacer esto colocando los archivos "divididos" .cppo solo es .hrealmente "bueno" para este método de organización del código?
Benny Jobigan
1
Pensé que algunos proyectos dividían los archivos de encabezado y de implementación (individual) para que pudieran distribuir los archivos de encabezado fácilmente sin revelar el código fuente de las implementaciones.
Carl G
Estoy muy contento de que hayas señalado esto porque originalmente aprendí en C ++, luego cambié a C # hace muchos años y recientemente he estado haciendo mucho C ++ nuevamente y olvidé cuán tedioso y molesto es dividir los archivos y comencé a poner todo en los encabezados. Estaba buscando a alguien buscando buenas razones para NO hacerlo cuando encontré esto. @CarlG tiene un buen punto, pero aparte de ese escenario, creo que hacerlo todo en línea es el camino a seguir.
Peter Moore
6

Básicamente una sintaxis modificada de declaración / definiciones de función:

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
fuente
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;
  }

La idea es mantener todas las firmas de funciones y miembros en el archivo de encabezado.
Esto permitirá que otros archivos del proyecto vean cómo se ve la clase sin tener que conocer la implementación.

Y además de eso, puede incluir otros archivos de encabezado en la implementación en lugar del encabezado. Esto es importante porque los encabezados incluidos en su archivo de encabezado se incluirán (heredarán) en cualquier otro archivo que incluya su archivo de encabezado.

Yochai Timmer
fuente
4

Dejas las declaraciones en el archivo de encabezado:

class A2DD
{
  private:
  int gx;
  int gy;

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

Y poner las definiciones en el archivo de implementación.

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

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

Puede mezclar los dos (deje la getSum()definición en el encabezado, por ejemplo). Esto es útil ya que le da al compilador una mejor oportunidad de alinearse, por ejemplo. Pero también significa que cambiar la implementación (si se deja en el encabezado) podría desencadenar una reconstrucción de todos los demás archivos que incluyen el encabezado.

Tenga en cuenta que para las plantillas, debe mantenerlo todo en los encabezados.

Estera
fuente
1
¿Poner miembros privados y funciones en el archivo de encabezado no se considera una fuga de detalles de implementación?
Jason
1
@ Jason, más o menos. Esos son detalles de implementación necesarios . Por ejemplo, tengo que saber cuánto espacio consumirá una clase en la pila. Las implementaciones de funciones no son necesarias para otras unidades de compilación.
Paul Draper
1

Por lo general, solo coloca declaraciones y funciones en línea realmente cortas en el archivo de encabezado:

Por ejemplo:

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
fuente
0

No voy a referirme también a su ejemplo, ya que es bastante simple para una respuesta general (por ejemplo, no contiene funciones con plantilla, que lo obligan a implementarlas en el encabezado), lo que sigo como regla general es el pimpl idioma

Tiene bastantes beneficios ya que obtienes tiempos de compilación más rápidos y el azúcar sintáctico:

class->member en vez de class.member

El único inconveniente es el puntero adicional que paga.

Spyros Mourelatos
fuente