Ich habe immer Leute schreiben sehen
class.h
#ifndef CLASS_H
#define CLASS_H
//blah blah blah
#endif
Die Frage ist, warum machen sie das nicht auch für die CPP-Datei, die Definitionen für Klassenfunktionen enthält?
Nehmen wir an, ich habe main.cpp
und main.cpp
schließt ein class.h
. Die class.h
Datei macht include
nichts, also woher weiß main.cpp
, was in der ist class.cpp
?
FILE_H
nicht seinCLASS_H
.Antworten:
Um Ihre erste Anfrage zu beantworten:
Wenn Sie dies in der .h- Datei sehen:
Dies ist eine Präprozessortechnik, mit der verhindert wird, dass eine Headerdatei mehrmals enthalten ist, was aus verschiedenen Gründen problematisch sein kann. Während der Kompilierung Ihres Projekts wird (normalerweise) jede CPP- Datei kompiliert. In einfachen Worten bedeutet dies, dass der Compiler Ihre CPP- Datei nimmt, alle Dateien
#included
damit öffnet , sie alle zu einer massiven Textdatei verkettet, dann eine Syntaxanalyse durchführt und sie schließlich in einen Zwischencode konvertiert, andere optimiert / ausführt Aufgaben und generieren schließlich die Assembly-Ausgabe für die Zielarchitektur. Aus diesem Grund, wenn sich eine Datei#included
mehrmals unter einer CPP befindetDatei, der Compiler hängt seinen Dateiinhalt zweimal an. Wenn also Definitionen in dieser Datei vorhanden sind, wird ein Compilerfehler angezeigt, der Sie darüber informiert, dass Sie eine Variable neu definiert haben. Wenn die Datei vom Präprozessorschritt im Kompilierungsprozess verarbeitet wird, prüfen die ersten beiden Zeilen beim ersten Erreichen ihres Inhalts, obFILE_H
sie für den Präprozessor definiert wurde. Wenn nicht, wirdFILE_H
der Code zwischen ihm und der#endif
Direktive definiert und weiter verarbeitet . Wenn der Inhalt dieser Datei das nächste Mal vom Präprozessor angezeigt wird, ist die Prüfung gegenFILE_H
falsch, sodass sofort nach unten gescannt#endif
und danach fortgefahren wird. Dies verhindert Neudefinitionsfehler.Und um Ihr zweites Anliegen anzusprechen:
In der C ++ - Programmierung als allgemeine Praxis trennen wir die Entwicklung in zwei Dateitypen. Eine ist mit einer Erweiterung von .h und wir nennen dies eine "Header-Datei". Sie enthalten normalerweise eine Deklaration von Funktionen, Klassen, Strukturen, globalen Variablen, Typedefs, Vorverarbeitungsmakros und Definitionen usw. Grundsätzlich liefern sie nur Informationen zu Ihrem Code. Dann haben wir die Erweiterung .cpp, die wir als " Codedatei " bezeichnen. Dadurch werden Definitionen für diese Funktionen, Klassenmitglieder und alle Strukturelemente bereitgestellt , die Definitionen, globale Variablen usw. benötigen. Die .h- Datei deklariert also Code, und die .cpp- Datei implementiert diese Deklaration. Aus diesem Grund kompilieren wir in der Regel während der Kompilierung jede .cppDatei in ein Objekt und verknüpfen Sie diese Objekte (da Sie fast nie sehen, dass eine CPP- Datei eine andere CPP- Datei enthält).
Wie diese externen Elemente aufgelöst werden, ist eine Aufgabe für den Linker. Wenn Ihr Compiler verarbeitet main.cpp , wird es Erklärungen für den Code in class.cpp , indem class.h . Es muss nur wissen, wie diese Funktionen oder Variablen aussehen (was eine Deklaration Ihnen gibt). So kompiliert es Ihre main.cpp- Datei in eine Objektdatei (nennen Sie es main.obj ). In ähnlicher Weise wird class.cpp in eine class.obj kompiliertDatei. Um die endgültige ausführbare Datei zu erstellen, wird ein Linker aufgerufen, um diese beiden Objektdateien miteinander zu verknüpfen. Für nicht aufgelöste externe Variablen oder Funktionen platziert der Compiler einen Stub, an dem der Zugriff erfolgt. Der Linker nimmt dann diesen Stub und sucht nach dem Code oder der Variablen in einer anderen aufgelisteten Objektdatei. Wenn er gefunden wird, kombiniert er den Code aus den beiden Objektdateien zu einer Ausgabedatei und ersetzt den Stub durch den endgültigen Speicherort der Funktion oder Variable. Auf diese Weise kann Ihr Code in main.cpp Funktionen aufrufen und Variablen in class.cpp verwenden, WENN UND NUR, WENN SIE IN class.h ERKLÄRT SIND .
Ich hoffe das war hilfreich.
quelle
Das
CLASS_H
ist ein Include Guard ; Es wird verwendet, um zu vermeiden, dass dieselbe Header-Datei mehrmals (über verschiedene Routen) in derselben CPP-Datei (oder genauer gesagt derselben Übersetzungseinheit ) enthalten ist, was zu Fehlern bei der Mehrfachdefinition führen würde.Include Guards sind für CPP-Dateien nicht erforderlich, da der Inhalt der CPP-Datei per Definition nur einmal gelesen wird.
Sie scheinen die Include-Guards so interpretiert zu haben, dass sie dieselbe Funktion haben wie
import
Anweisungen in anderen Sprachen (z. B. Java). Dies ist jedoch nicht der Fall. Das#include
selbst entspricht in etwa demimport
in anderen Sprachen.quelle
Zumindest während der Kompilierungsphase nicht.
Die Übersetzung eines C ++ - Programms vom Quellcode in den Maschinencode erfolgt in drei Phasen:
class.h
anstelle der Zeile eingefügt#include "class.h
. Da Sie möglicherweise an mehreren Stellen in Ihre Header-Datei aufgenommen werden,#ifndef
vermeiden die Klauseln doppelte Deklarationsfehler, da die Präprozessor-Direktive nur beim ersten Einfügen der Header-Datei undefiniert ist.Zusammenfassend kann gesagt werden, dass die Deklarationen über eine Header-Datei gemeinsam genutzt werden können, während die Zuordnung von Deklarationen zu Definitionen vom Linker vorgenommen wird.
quelle
Das ist der Unterschied zwischen Deklaration und Definition. Header-Dateien enthalten normalerweise nur die Deklaration, und die Quelldatei enthält die Definition.
Um etwas zu verwenden, müssen Sie nur die Deklaration und nicht die Definition kennen. Nur der Linker muss die Definition kennen.
Aus diesem Grund fügen Sie eine Header-Datei in eine oder mehrere Quelldateien ein, aber keine Quelldatei in eine andere.
Auch du meinst
#include
und nicht importieren.quelle
Dies geschieht für Header-Dateien, sodass der Inhalt in jeder vorverarbeiteten Quelldatei nur einmal angezeigt wird, selbst wenn er mehr als einmal enthalten ist (normalerweise, weil er aus anderen Header-Dateien stammt). Beim ersten Einfügen wurde das Symbol
CLASS_H
(als Include Guard bezeichnet ) noch nicht definiert, sodass der gesamte Inhalt der Datei enthalten ist. Dadurch wird das Symbol definiert. Wenn es erneut enthalten ist, wird der Inhalt der Datei (innerhalb des#ifndef
/#endif
-Blocks) übersprungen.Dies ist für die Quelldatei selbst nicht erforderlich, da dies (normalerweise) in keiner anderen Datei enthalten ist.
Für Ihre letzte Frage
class.h
sollte die Definition der Klasse und Deklarationen aller ihrer Mitglieder, zugehörigen Funktionen und was auch immer enthalten sein, damit jede Datei, die sie enthält, über genügend Informationen verfügt, um die Klasse zu verwenden. Die Implementierungen der Funktionen können in einer separaten Quelldatei erfolgen. Sie brauchen nur die Deklarationen, um sie aufzurufen.quelle
main.cpp muss nicht wissen, was in class.cpp enthalten ist . Es muss nur die Deklarationen der Funktionen / Klassen kennen, die verwendet werden sollen, und diese Deklarationen befinden sich in class.h .
Der Linker verknüpft die Stellen, an denen die in class.h deklarierten Funktionen / Klassen verwendet werden, und ihre Implementierungen in class.cpp
quelle
.cpp
Dateien sind nicht#include
in anderen Dateien enthalten. Daher müssen sie keine Bewachung einschließen.Main.cpp
kennt die Namen und Signaturen der Klasse, in der Sie implementiert haben,class.cpp
nur, weil Sie all das angegeben habenclass.h
- dies ist der Zweck einer Header-Datei. (Es liegt an Ihnen, sicherzustellen, dassclass.h
der Code, in dem Sie implementieren , genau beschrieben wirdclass.cpp
.) Der ausführbare Code inclass.cpp
wirdmain.cpp
dank der Bemühungen des Linkers dem ausführbaren Code zur Verfügung gestellt .quelle
Es wird allgemein erwartet, dass Codemodule wie
.cpp
Dateien einmal kompiliert und in mehreren Projekten verknüpft werden, um eine unnötige wiederholte Kompilierung der Logik zu vermeiden. Zum Beispielg++ -o class.cpp
würde produzieren,class.o
die Sie dann von mehreren Projekten zur Verwendung verknüpfen könnteng++ main.cpp class.o
.Wir könnten
#include
als Linker verwenden, wie Sie zu implizieren scheinen, aber das wäre einfach albern, wenn wir wissen, wie man mit unserem Compiler mit weniger Tastenanschlägen und weniger verschwenderischen Wiederholungen der Kompilierung richtig verknüpft, anstatt unseren Code mit mehr Tastenanschlägen und verschwenderischer Wiederholung der Zusammenstellung ...Die Header-Dateien müssen jedoch weiterhin in jedem der mehreren Projekte enthalten sein, da dies die Schnittstelle für jedes Modul darstellt. Ohne diese Header würde der Compiler keines der von den
.o
Dateien eingeführten Symbole kennen .Es ist wichtig zu wissen, dass die Header-Dateien die Definitionen der Symbole für diese Module einführen. Sobald dies realisiert ist, ist es sinnvoll, dass mehrere Einschlüsse zu Neudefinitionen von Symbolen führen können (was zu Fehlern führt). Daher verwenden wir Include-Wachen, um solche Neudefinitionen zu verhindern.
quelle
Aufgrund von Header-Dateien wird definiert, was die Klasse enthält (Mitglieder, Datenstrukturen), und CPP-Dateien implementieren sie.
Der Hauptgrund dafür ist natürlich, dass Sie eine .h-Datei mehrmals in andere .h-Dateien aufnehmen können. Dies würde jedoch zu mehreren Definitionen einer Klasse führen, was ungültig ist.
quelle