Ok, keineswegs ein C / C ++ - Experte, aber ich dachte, der Sinn einer Header-Datei besteht darin, die Funktionen zu deklarieren, und dann sollte die C / CPP-Datei die Implementierung definieren.
Der wahre Zweck einer Header-Datei besteht darin, Code für mehrere Quelldateien freizugeben. Es wird häufig verwendet, um Deklarationen von Implementierungen zu trennen, um die Codeverwaltung zu verbessern. Dies ist jedoch keine Voraussetzung. Es ist möglich, Code zu schreiben, der nicht auf Header-Dateien basiert, und es ist möglich, Code zu schreiben, der nur aus Header-Dateien besteht (die STL- und Boost-Bibliotheken sind gute Beispiele dafür). Denken Sie daran, wenn der Präprozessor auf eine #include
Anweisung stößt , ersetzt er die Anweisung durch den Inhalt der Datei, auf die verwiesen wird, und der Compiler sieht nur den vollständigen vorverarbeiteten Code.
Zum Beispiel, wenn Sie die folgenden Dateien haben:
Foo.h:
#ifndef FooH
#define FooH
class Foo
{
public:
UInt32 GetNumberChannels() const;
private:
UInt32 _numberChannels;
};
#endif
Foo.cpp:
#include "Foo.h"
UInt32 Foo::GetNumberChannels() const
{
return _numberChannels;
}
Bar.cpp:
#include "Foo.h"
Foo f;
UInt32 chans = f.GetNumberChannels();
Der Präprozessor analysiert Foo.cpp und Bar.cpp getrennt und erzeugt den folgenden Code, den der Compiler dann analysiert:
Foo.cpp:
class Foo
{
public:
UInt32 GetNumberChannels() const;
private:
UInt32 _numberChannels;
};
UInt32 Foo::GetNumberChannels() const
{
return _numberChannels;
}
Bar.cpp:
class Foo
{
public:
UInt32 GetNumberChannels() const;
private:
UInt32 _numberChannels;
};
Foo f;
UInt32 chans = f.GetNumberChannels();
Bar.cpp wird in Bar.obj kompiliert und enthält einen Verweis zum Aufrufen Foo::GetNumberChannels()
. Foo.cpp wird in Foo.obj kompiliert und enthält die eigentliche Implementierung von Foo::GetNumberChannels()
. Nach dem Kompilieren vergleicht der Linker die OBJ-Dateien und verknüpft sie miteinander, um die endgültige ausführbare Datei zu erstellen.
Warum gibt es eine Implementierung in einem Header?
Durch die Aufnahme der Methodenimplementierung in die Methodendeklaration wird sie implizit als inline deklariert (es gibt ein tatsächliches inline
Schlüsselwort, das auch explizit verwendet werden kann). Die Angabe, dass der Compiler eine Funktion einbinden soll, ist nur ein Hinweis, der nicht garantiert, dass die Funktion tatsächlich inline wird. Wenn dies jedoch der Fall ist, wird der Inhalt der Funktion überall dort, wo die Inline-Funktion aufgerufen wird, direkt in die Aufrufstelle kopiert, anstatt eine CALL
Anweisung zu generieren , um in die Funktion zu springen und beim Beenden zum Aufrufer zurückzukehren. Der Compiler kann dann den umgebenden Code berücksichtigen und den kopierten Code nach Möglichkeit weiter optimieren.
Hat es mit dem Schlüsselwort const zu tun?
Nein. Das const
Schlüsselwort zeigt dem Compiler lediglich an, dass die Methode den Status des Objekts, das zur Laufzeit aufgerufen wird, nicht ändert.
Was genau ist der Vorteil / Sinn dieser Vorgehensweise gegenüber der Definition der Implementierung in der CPP-Datei?
Bei effektiver Verwendung kann der Compiler normalerweise schnelleren und besser optimierten Maschinencode erstellen.