Trennen des Klassencodes in eine Header- und eine CPP-Datei

169

Ich bin verwirrt darüber, wie Implementierungs- und Deklarationscode einer einfachen Klasse in eine neue Header- und CPP-Datei getrennt werden kann. Wie würde ich beispielsweise den Code für die folgende Klasse trennen?

class A2DD
{
  private:
  int gx;
  int gy;

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

  int getSum()
  {
    return gx + gy;
  }
};
drdrdr
quelle
12
Nur ein paar Kommentare: Der Konstruktor sollte immer eine Initialisierungsliste verwenden, anstatt die Elemente im Hauptteil festzulegen. Eine gute und einfache Erklärung finden Sie unter: codeguru.com/forum/showthread.php?t=464084 Zumindest an den meisten Stellen ist es auch üblich, das öffentliche Feld oben zu haben. Es hat keine Auswirkungen, aber da die öffentlichen Felder die Dokumentation Ihrer Klasse sind, ist es sinnvoll, diese oben zu haben.
Martiert
2
@martiert Mit public:Mitgliedern an der Spitze könnte einen Einfluss auf Menge , wenn der Benutzer bewegt sie diesen Rat nach - aber hatte Abhängigkeiten zwischen den Mitgliedern der Bestellung und war noch nicht bewusst , dass die Mitglieder in der Reihenfolge ihrer Deklaration ;-) initialisiert werden
underscore_d
1
@underscore_d das ist wahr. Aber andererseits kompilieren wir alle mit Warnungen als Fehlern und allen Warnungen, die wir uns vorstellen können, oder? Das würde dir zumindest sagen, dass du das vermasselst, aber ja, die Leute benutzen viel zu kleine Warnungen und ignorieren sie einfach :(
martiert
@martiert Guter Punkt, irgendwie vergessen, dass Warnungen erzeugt - wenn nur Warnungen von den meisten gelesen wurden :-) Ich benutze sie und versuche sie alle weg zu codieren. Einige sind unvermeidlich - also sage ich "Danke für die Warnung, aber ich weiß, was ich tue!" - aber die meisten lassen sich am besten beheben, um später Verwirrung zu vermeiden.
underscore_d
Öffentliche Felder an der Spitze zu haben, ist nur ein Stil, den meiner Meinung nach leider zu viele übernommen haben. Darüber hinaus müssen Sie einige Dinge beachten, wie @martiert erwähnt.
Vassilis

Antworten:

232

Die Klassendeklaration geht in die Header-Datei. Es ist wichtig, dass Sie die #ifndefInclude-Schutzvorrichtungen hinzufügen. Wenn Sie sich auf einer MS-Plattform befinden, können Sie diese auch verwenden #pragma once. Außerdem habe ich das private weggelassen, standardmäßig sind C ++ - Klassenmitglieder privat.

// A2DD.h
#ifndef A2DD_H
#define A2DD_H

class A2DD
{
  int gx;
  int gy;

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

};

#endif

und die Implementierung geht in die CPP-Datei:

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

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

int A2DD::getSum()
{
  return gx + gy;
}
Ferenc Deak
quelle
52
Denken Sie daran, dass Sie bei der Vorlagenprogrammierung alles in der .h-Datei behalten müssen, damit der Compiler zum Zeitpunkt der Kompilierung den richtigen Code instanziiert.
Linello
2
Hast du das #ifndefZeug in der Kopfzeile?
Ferenc Deak
4
Dies bedeutet, dass alle Dateien, die Ihre Header-Datei enthalten, die privaten Mitglieder "sehen". Wenn Sie beispielsweise eine Bibliothek und ihren Header veröffentlichen möchten, müssen Sie die privaten Mitglieder der Klasse anzeigen?
Gauthier
1
Nein, es gibt die wunderbare private Implementierungssprache: en.wikipedia.org/wiki/Opaque_pointer Sie können sie verwenden, um die Implementierungsdetails auszublenden.
Ferenc Deak
3
Kleiner Nitpick mit dem Wortlaut: "Die Klassendeklaration geht in die Header-Datei". Dies ist in der Tat eine Deklaration, aber es ist auch eine Definition, aber da die letztere die erstere enthält, würde ich eher sagen, dass die Klassendefinition in die Header-Datei aufgenommen wird. In der Übersetzungseinheit haben Sie die Definition der Elementfunktionen, nicht die Definition der Klasse. Ich stimme Ihnen zu, dies könnte eine kleine Bearbeitung wert sein?
lubgr
17

Im Allgemeinen enthält Ihre .h die Klassendefinition, die alle Ihre Daten und alle Ihre Methodendeklarationen enthält. So in Ihrem Fall:

A2DD.h:

class A2DD
{
  private:
  int gx;
  int gy;

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

Und dann enthält Ihre .cpp die Implementierungen der folgenden Methoden:

A2DD.cpp:

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

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

Es ist wichtig, die Leser, die über diese Frage stolpern, darauf hinzuweisen, dass das Verfahren der akzeptierten Antwort nicht erforderlich ist, wenn Sie Ihr Projekt nur in Dateien aufteilen möchten. Es wird nur benötigt, wenn Sie mehrere Implementierungen einzelner Klassen benötigen. Wenn Ihre Implementierung pro Klasse eine ist, reicht nur eine Header-Datei für jede aus.

Aus dem Beispiel der akzeptierten Antwort wird daher nur dieser Teil benötigt:

#ifndef MYHEADER_H
#define MYHEADER_H

//Class goes here, full declaration AND implementation

#endif

Die Präprozessordefinitionen #ifndef usw. ermöglichen die mehrfache Verwendung.

PS. Das Thema wird klarer, sobald Sie feststellen, dass C / C ++ 'dumm' ist und #include nur eine Möglichkeit ist, "diesen Text an dieser Stelle ausgeben" zu sagen.

j riv
quelle
Könnten Sie dies tun, indem Sie die "geteilten" Dateien einfügen .cpp, oder ist dies nur .hwirklich "gut" für diese Methode der Code-Organisation?
Benny Jobigan
1
Ich dachte, dass einige Projekte Header- und (einzelne) Implementierungsdateien aufteilen, damit sie die Header-Dateien einfach verteilen können, ohne den Quellcode der Implementierungen preiszugeben.
Carl G
Ich bin so froh, dass Sie darauf hingewiesen haben, weil ich ursprünglich in C ++ gelernt habe und dann vor vielen Jahren zu C # gewechselt bin und in letzter Zeit wieder viel C ++ gemacht habe. Ich habe vergessen, wie mühsam und nervig das Aufteilen der Dateien ist, und habe gerade angefangen, alles einzulegen Ich suchte herum und suchte nach jemandem, der gute Gründe gab, das NICHT zu tun, als ich das fand. @CarlG hat einen guten Punkt, aber abgesehen von diesem Szenario denke ich, dass es der richtige Weg ist, alles inline zu machen.
Peter Moore
6

Grundsätzlich eine modifizierte Syntax der Funktionsdeklaration / -definitionen:

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

Die Idee ist, alle Funktionssignaturen und Mitglieder in der Header-Datei zu behalten.
Auf diese Weise können andere Projektdateien sehen, wie die Klasse aussieht, ohne die Implementierung kennen zu müssen.

Außerdem können Sie anstelle des Headers andere Header-Dateien in die Implementierung aufnehmen. Dies ist wichtig, da alle in Ihrer Header-Datei enthaltenen Header in jeder anderen Datei, die Ihre Header-Datei enthält, enthalten (geerbt) werden.

Yochai Timmer
quelle
4

Sie belassen die Deklarationen in der Header-Datei:

class A2DD
{
  private:
  int gx;
  int gy;

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

Und fügen Sie die Definitionen in die Implementierungsdatei ein.

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

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

Sie können die beiden mischen (lassen Sie die getSum()Definition beispielsweise in der Kopfzeile). Dies ist nützlich, da der Compiler beispielsweise eine bessere Chance zum Inlining hat. Dies bedeutet jedoch auch, dass das Ändern der Implementierung (falls im Header belassen) eine Neuerstellung aller anderen Dateien auslösen kann, die den Header enthalten.

Beachten Sie, dass Sie für Vorlagen alles in den Kopfzeilen behalten müssen.

Matte
quelle
1
Das Einfügen privater Mitglieder und Funktionen in die Header-Datei wird nicht als Verlust von Implementierungsdetails angesehen.
Jason
1
@ Jason, irgendwie. Dies sind notwendige Implementierungsdetails. Zum Beispiel muss ich wissen, wie viel Platz eine Klasse auf dem Stapel verbraucht. Funktionsimplementierungen sind für andere Kompilierungseinheiten nicht erforderlich.
Paul Draper
1

Normalerweise fügen Sie nur Deklarationen und wirklich kurze Inline-Funktionen in die Header-Datei ein:

Zum Beispiel:

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

Ich werde nicht zu sehr auf Ihr Beispiel verweisen, da es für eine allgemeine Antwort recht einfach ist (zum Beispiel enthält es keine Vorlagenfunktionen, die Sie zwingen, sie in der Kopfzeile zu implementieren). Als Faustregel befolge ich den Pickel Idiom

Es hat einige Vorteile, da Sie schnellere Kompilierungszeiten und den syntaktischen Zucker erhalten:

class->member anstatt class.member

Der einzige Nachteil ist der zusätzliche Zeiger, den Sie bezahlen.

Spyros Mourelatos
quelle