Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
In diesem Artikel
Die Namen von Programmelementen wie Variablen, Funktionen, Klassen usw. müssen deklariert werden, bevor sie verwendet werden können. Sie können z. B. nicht nur schreiben x = 42
, ohne zuerst "x" zu deklarieren.
int x; // declaration
x = 42; // use x
Die Deklaration teilt dem Compiler mit, ob es sich bei dem Element um eine int
, eine double
Funktion, eine class
oder eine andere Sache handelt. Darüber hinaus muss jeder Name (direkt oder indirekt) in jeder .cpp Datei deklariert werden, in der sie verwendet wird. Wenn Sie ein Programm kompilieren, wird jede .cpp Datei unabhängig in eine Kompilierungseinheit kompiliert. Der Compiler hat keine Kenntnisse darüber, welche Namen in anderen Kompilierungseinheiten deklariert werden. Das bedeutet: Wenn Sie eine Klasse oder Funktion oder globale Variable definieren, müssen Sie in jeder zusätzlichen .cpp Datei, die sie verwendet, eine Deklaration dieser Datei angeben. Jede Deklaration dieser Sache muss in allen Dateien exakt identisch sein. Eine leichte Inkonsistenz verursacht Fehler oder unbeabsichtigtes Verhalten, wenn der Linker versucht, alle Kompilierungseinheiten in einem einzigen Programm zusammenzuführen.
Um das Fehlerpotenzial zu minimieren, hat C++ die Konvention der Verwendung von Headerdateien für Deklarationen übernommen. Sie erstellen die Deklarationen in einer Headerdatei und verwenden dann die #include Direktive in jeder .cpp Datei oder einer anderen Headerdatei, die diese Deklaration erfordert. Die #include Direktive fügt vor der Kompilierung eine Kopie der Headerdatei direkt in die .cpp Datei ein.
Hinweis
In Visual Studio 2019 wird das C++20-Modulfeature als Verbesserung und letztendlicher Ersatz für Headerdateien eingeführt. Weitere Informationen finden Sie unter Übersicht über Module in C++.
Das folgende Beispiel zeigt eine gängige Methode zum Deklarieren einer Klasse und anschließender Verwendung in einer anderen Quelldatei. Wir beginnen mit der Headerdatei. my_class.h
Sie enthält eine Klassendefinition, beachten Sie jedoch, dass die Definition unvollständig ist; die Memberfunktion do_something
ist nicht definiert:
// my_class.h
namespace N
{
class my_class
{
public:
void do_something();
};
}
Erstellen Sie als Nächstes eine Implementierungsdatei (in der Regel mit einer .cpp oder einer ähnlichen Erweiterung). Wir rufen die Datei my_class.cpp auf und stellen eine Definition für die Memberdeklaration bereit. Wir fügen eine #include
Direktive für die Datei "my_class.h" hinzu, damit die my_class-Deklaration an diesem Punkt in die .cpp-Datei eingefügt wird, und wir fügen <iostream>
die Deklaration für std::cout
. Beachten Sie, dass Anführungszeichen für Kopfzeilendateien im selben Verzeichnis wie die Quelldatei verwendet werden, und winkelige Klammern werden für Standardbibliotheksheader verwendet. Außerdem verfügen viele Standardbibliotheksheader nicht über .h oder eine andere Dateierweiterung.
In der Implementierungsdatei können wir optional eine using
Anweisung verwenden, um zu vermeiden, dass jede Erwähnung von "my_class" oder "Cout" mit "N::" oder "std::"" qualifiziert werden muss. Fügen using
Sie keine Anweisungen in Ihre Kopfzeilendateien ein!
// my_class.cpp
#include "my_class.h" // header in local directory
#include <iostream> // header in standard library
using namespace N;
using namespace std;
void my_class::do_something()
{
cout << "Doing something!" << endl;
}
Jetzt können wir in einer anderen .cpp Datei verwenden my_class
. Wir #include die Headerdatei, sodass der Compiler die Deklaration abruft. Der gesamte Compiler muss wissen, dass my_class eine Klasse ist, die eine öffentliche Memberfunktion aufgerufen do_something()
hat.
// my_program.cpp
#include "my_class.h"
using namespace N;
int main()
{
my_class mc;
mc.do_something();
return 0;
}
Nachdem der Compiler die Kompilierung jeder .cpp Datei in .obj Dateien abgeschlossen hat, werden die .obj Dateien an den Linker übergeben. Wenn der Linker die Objektdateien zusammenführt, findet er genau eine Definition für my_class; sie befindet sich in der .obj Datei, die für my_class.cpp erstellt wurde, und der Build erfolgreich ist.
In der Regel verfügen Headerdateien über einen Include Guard oder eine #pragma once
Direktive, um sicherzustellen, dass sie nicht mehrmals in eine einzelne .cpp Datei eingefügt werden.
// my_class.h
#ifndef MY_CLASS_H // include guard
#define MY_CLASS_H
namespace N
{
class my_class
{
public:
void do_something();
};
}
#endif /* MY_CLASS_H */
Da eine Headerdatei möglicherweise von mehreren Dateien eingeschlossen werden kann, kann sie keine Definitionen enthalten, die mehrere Definitionen desselben Namens erzeugen können. Folgendes ist nicht zulässig oder gilt als sehr schlechte Praxis:
- Integrierte Typdefinitionen im Namespace- oder globalen Bereich
- Nicht-Inline-Funktionsdefinitionen
- Nichtkonstvariablendefinitionen
- Aggregatdefinitionen
- Unbenannte Namespaces
- using-Direktiven
Die Verwendung der using
Direktive führt nicht unbedingt zu einem Fehler, kann aber möglicherweise ein Problem verursachen, da er den Namespace in jede .cpp Datei einführt, die direkt oder indirekt diesen Header enthält.
Das folgende Beispiel zeigt die verschiedenen Arten von Deklarationen und Definitionen, die in einer Headerdatei zulässig sind:
// sample.h
#pragma once
#include <vector> // #include directive
#include <string>
namespace N // namespace declaration
{
inline namespace P
{
//...
}
enum class colors : short { red, blue, purple, azure };
const double PI = 3.14; // const and constexpr definitions
constexpr int MeaningOfLife{ 42 };
constexpr int get_meaning()
{
static_assert(MeaningOfLife == 42, "unexpected!"); // static_assert
return MeaningOfLife;
}
using vstr = std::vector<int>; // type alias
extern double d; // extern variable
#define LOG // macro definition
#ifdef LOG // conditional compilation directive
void print_to_log();
#endif
class my_class // regular class definition,
{ // but no non-inline function definitions
friend class other_class;
public:
void do_something(); // definition in my_class.cpp
inline void put_value(int i) { vals.push_back(i); } // inline OK
private:
vstr vals;
int i;
};
struct RGB
{
short r{ 0 }; // member initialization
short g{ 0 };
short b{ 0 };
};
template <typename T> // template definition
class value_store
{
public:
value_store<T>() = default;
void write_value(T val)
{
//... function definition OK in template
}
private:
std::vector<T> vals;
};
template <typename T> // template declaration
class value_widget;
}