Separare il codice di classe in un file header e cpp


170

Sono confuso su come separare il codice di implementazione e dichiarazioni di una classe semplice in un nuovo file di intestazione e cpp. Ad esempio, come separerei il codice per la seguente classe?

class A2DD
{
  private:
  int gx;
  int gy;

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

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

12
Solo un paio di commenti: il costruttore dovrebbe sempre utilizzare un elenco di inizializzazione invece di impostare i membri nel corpo. Per una spiegazione valida e semplice, vedi: codeguru.com/forum/showthread.php?t=464084 È anche, almeno nella maggior parte dei luoghi, consuetudine avere il campo pubblico in alto. Non influirà su nulla, ma poiché i campi pubblici sono la documentazione della tua classe, ha senso averla in cima.
martiert,

2
@martiert Avere public:membri nella parte superiore potrebbe influenzare molto , se l'utente li spostava in base a questo consiglio - ma aveva delle dipendenze di ordinamento tra i membri e non era ancora consapevole che i membri sono inizializzati nell'ordine della loro dichiarazione ;-)
underscore_d

1
@underscore_d è vero. Ma ancora una volta, stiamo compilando tutti gli avvisi come errori e tutti gli avvisi a cui possiamo pensare, giusto? Ciò ti direbbe almeno che stai rovinando tutto, ma sì, le persone usano il modo per piccoli avvertimenti e li ignorano semplicemente :(
martiert

@martiert Un buon punto, un po 'dimenticato che genera avvisi - se solo gli avvisi fossero letti dalla maggior parte :-) Li uso e cerco di codificarli tutti via. Alcuni sono inevitabili - quindi dico "grazie per l'avvertimento, ma so cosa sto facendo!" - ma la maggior parte sono riparati meglio per evitare confusione in seguito.
underscore_d

Avere campi pubblici al top è solo uno stile, che troppi ha adottato purtroppo secondo me. Inoltre, devi tenere a mente alcune cose come menzionato da @martiert.
Vassilis

Risposte:


233

La dichiarazione di classe va nel file di intestazione. È importante aggiungere le #ifndefprotezioni di inclusione o, se ci si trova su una piattaforma MS, è anche possibile utilizzare #pragma once. Inoltre ho omesso il privato, per impostazione predefinita i membri della classe C ++ sono privati.

// A2DD.h
#ifndef A2DD_H
#define A2DD_H

class A2DD
{
  int gx;
  int gy;

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

};

#endif

e l'implementazione va nel file CPP:

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

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

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

53
Ricorda che se stai programmando un template, devi tenere tutto nel file .h in modo che il compilatore crei un'istanza del codice giusto al momento della compilazione.
Linello,

2
hai le #ifndefcose nell'intestazione?
Ferenc Deak

4
Ciò significa che tutti i file che includono il tuo file di intestazione "vedranno" i membri privati. Se ad esempio vuoi pubblicare una lib e la sua intestazione, devi mostrare i membri privati ​​della classe?
Gauthier,

1
No, c'è il meraviglioso linguaggio dell'implementazione privata: en.wikipedia.org/wiki/Opaque_pointer Puoi usarlo per nascondere i dettagli dell'implementazione.
Ferenc Deak,

3
Nitpick minore con la dicitura: "La dichiarazione di classe va nel file di intestazione". Questa è davvero una dichiarazione, ma è anche una definizione, ma poiché la seconda include la prima preferirei dire che la definizione della classe va nel file di intestazione. Nell'unità di traduzione, hai la definizione delle funzioni membro, non la definizione della classe. Sono d'accordo, questo potrebbe valere una piccola modifica?
lubgr,

17

In generale, il tuo .h contiene la classe defition, che è tutti i tuoi dati e tutte le dichiarazioni del tuo metodo. In questo modo nel tuo caso:

A2DD.h:

class A2DD
{
  private:
  int gx;
  int gy;

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

E poi il tuo .cpp contiene le implementazioni dei metodi come questo:

A2DD.cpp:

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

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

7

È importante sottolineare ai lettori che si imbattono in questa domanda quando ricercano l'argomento in modo più ampio che la procedura della risposta accettata non è richiesta nel caso in cui si desideri solo dividere il progetto in file. È necessario solo quando sono necessarie più implementazioni di singole classi. Se l'implementazione per classe è una, è sufficiente un solo file di intestazione per ciascuna.

Quindi, dall'esempio della risposta accettata è necessaria solo questa parte:

#ifndef MYHEADER_H
#define MYHEADER_H

//Class goes here, full declaration AND implementation

#endif

Le definizioni del preprocessore #ifndef ecc. Consentono di utilizzarlo più volte.

PS. L'argomento diventa più chiaro quando ti rendi conto che C / C ++ è 'stupido' e #include è semplicemente un modo per dire "scarica questo testo in questo punto".


potresti farlo inserendo i file "divisi" .cppo è .hdavvero "buono" per questo metodo di organizzazione del codice?
Benny Jobigan,

1
Ho pensato che alcuni progetti dividessero l'intestazione e i (singoli) file di implementazione in modo da poter distribuire facilmente i file di intestazione senza rivelare il codice sorgente delle implementazioni.
Carl G,

Sono così felice che tu l'abbia sottolineato perché in origine ho imparato su C ++ e poi sono passato a C # molti anni fa e recentemente ho fatto di nuovo molto C ++ e ho dimenticato quanto sia noioso e fastidioso dividere i file e ho appena iniziato a mettere tutto in le intestazioni. Stavo cercando in giro cercando qualcuno che desse buone ragioni per NON farlo quando l'ho trovato. @CarlG ha un buon punto, ma a parte quello scenario penso che fare tutto in linea sia la strada da percorrere.
Peter Moore,

6

Fondamentalmente una sintassi modificata della dichiarazione / definizioni delle funzioni:

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

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

L'idea è di mantenere tutte le firme e i membri delle funzioni nel file di intestazione.
Ciò consentirà ad altri file di progetto di vedere come appare la classe senza dover conoscere l'implementazione.

Inoltre, è possibile includere altri file di intestazione nell'implementazione anziché nell'intestazione. Questo è importante perché le intestazioni incluse nel file di intestazione verranno incluse (ereditate) in qualsiasi altro file che include il file di intestazione.


4

Lasciare le dichiarazioni nel file di intestazione:

class A2DD
{
  private:
  int gx;
  int gy;

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

E inserisci le definizioni nel file di implementazione.

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

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

È possibile mescolare i due (ad esempio, lasciare la getSum()definizione nell'intestazione). Ciò è utile poiché offre al compilatore una migliore possibilità di inline per esempio. Ma significa anche che la modifica dell'implementazione (se lasciata nell'intestazione) potrebbe innescare una ricostruzione di tutti gli altri file che includono l'intestazione.

Tieni presente che per i modelli devi tenere tutto nelle intestazioni.


1
Inserire membri privati ​​e funzioni nel file di intestazione non è considerato una perdita di dettagli di implementazione?
Jason,

1
@Jason, una specie di. Questi sono i dettagli necessari per l' implementazione. Ad esempio, devo sapere quanto spazio consumerà una classe in pila. Le implementazioni di funzioni non sono necessarie per altre unità di compilazione.
Paul Draper,

1

Di solito metti solo dichiarazioni e funzioni inline davvero brevi nel file di intestazione:

Per esempio:

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

0

Non farò troppo riferimento al tuo esempio in quanto è abbastanza semplice per una risposta generale (ad esempio non contiene funzioni basate su modelli, che ti costringono a implementarli nell'intestazione), quello che seguo come regola empirica è il pimpl idioma

Ha alcuni vantaggi man mano che ottieni tempi di compilazione più rapidi e lo zucchero sintattico:

class->member invece di class.member

L'unico inconveniente è il puntatore extra che paghi.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.