Ich habe nie wirklich verstanden, warum C ++ eine separate Header-Datei mit den gleichen Funktionen wie in der CPP-Datei benötigt. Es macht das Erstellen und Umgestalten von Klassen sehr schwierig und fügt dem Projekt unnötige Dateien hinzu. Und dann gibt es das Problem, dass Header-Dateien eingeschlossen werden müssen, aber explizit überprüft werden muss, ob sie bereits enthalten sind.
C ++ wurde 1998 ratifiziert. Warum ist es so konzipiert? Welche Vorteile hat eine separate Header-Datei?
Zusatzfrage:
Wie findet der Compiler die CPP-Datei mit dem darin enthaltenen Code, wenn ich nur die H-Datei einbinde? Wird davon ausgegangen, dass die CPP-Datei denselben Namen wie die H-Datei hat, oder werden tatsächlich alle Dateien in der Verzeichnisstruktur durchsucht?
quelle
Antworten:
Sie scheinen nach der Trennung von Definitionen und Deklarationen zu fragen, obwohl es andere Verwendungszwecke für Header-Dateien gibt.
Die Antwort ist, dass C ++ dies nicht "braucht". Wenn Sie alles inline markieren (was für in einer Klassendefinition definierte Elementfunktionen ohnehin automatisch ist), ist die Trennung nicht erforderlich. Sie können einfach alles in den Header-Dateien definieren.
Die Gründe, die Sie möglicherweise trennen möchten , sind:
Wenn Ihre allgemeinere Frage lautet: "Warum ist C ++ nicht mit Java identisch?", Muss ich fragen: "Warum schreiben Sie C ++ anstelle von Java?" ;-p
Im Ernst, der Grund ist, dass der C ++ - Compiler nicht einfach in eine andere Übersetzungseinheit greifen und herausfinden kann, wie seine Symbole so verwendet werden, wie es Javac kann und tut. Die Header-Datei wird benötigt, um dem Compiler zu deklarieren, was zum Zeitpunkt der Verknüpfung voraussichtlich verfügbar sein wird.
So
#include
ist eine reine Textsubstitution. Wenn Sie alles in Header-Dateien definieren, erstellt der Präprozessor eine enorme Kopie und Einfügung jeder Quelldatei in Ihrem Projekt und speist diese in den Compiler ein. Die Tatsache, dass der C ++ - Standard 1998 ratifiziert wurde, hat nichts damit zu tun. Es ist die Tatsache, dass die Kompilierungsumgebung für C ++ so eng an der von C basiert.Konvertieren meiner Kommentare zur Beantwortung Ihrer Folgefrage:
Zumindest nicht zu dem Zeitpunkt, an dem der Code kompiliert wird, der die Header-Datei verwendet hat. Die Funktionen, mit denen Sie verknüpfen, müssen noch nicht einmal geschrieben worden sein, unabhängig davon, ob der Compiler weiß, in welcher
.cpp
Datei sie sich befinden. Alles, was der aufrufende Code zur Kompilierungszeit wissen muss, wird in der Funktionsdeklaration ausgedrückt. Zur Verbindungszeit stellen Sie eine Liste von.o
Dateien oder statischen oder dynamischen Bibliotheken bereit , und der Header ist ein Versprechen, dass die Definitionen der Funktionen irgendwo dort sein werden.quelle
C ++ macht es so, weil C es so gemacht hat. Die eigentliche Frage ist also, warum C es so gemacht hat. Wikipedia spricht ein wenig dazu.
quelle
Einige Leute halten Header-Dateien für einen Vorteil:
Letztendlich ist das Header-System ein Artefakt aus den 70er Jahren, als C entworfen wurde. Computer hatten damals nur sehr wenig Speicher, und es war einfach keine Option, das gesamte Modul im Speicher zu halten. Ein Compiler musste die Datei oben lesen und dann linear durch den Quellcode gehen. Der Header-Mechanismus ermöglicht dies. Der Compiler muss keine anderen Übersetzungseinheiten berücksichtigen, sondern nur den Code von oben nach unten lesen.
Und C ++ hat dieses System aus Gründen der Abwärtskompatibilität beibehalten.
Heute macht es keinen Sinn. Es ist ineffizient, fehleranfällig und überkompliziert. Es gibt viel bessere Möglichkeiten zur Trennung von Schnittstelle und Implementierung, wenn das war das Ziel.
Einer der Vorschläge für C ++ 0x war jedoch, ein geeignetes Modulsystem hinzuzufügen, mit dem Code ähnlich wie .NET oder Java in größere Module kompiliert werden kann, alles auf einmal und ohne Header. Dieser Vorschlag hat den Schnitt in C ++ 0x nicht geschafft, aber ich glaube, er gehört immer noch zur Kategorie "Wir würden das gerne später machen". Vielleicht in einem TR2 oder ähnlichem.
quelle
Nach meinem (eingeschränkten - ich bin normalerweise kein C-Entwickler) Verständnis ist dies in C verwurzelt. Denken Sie daran, dass C nicht weiß, was Klassen oder Namespaces sind, es ist nur ein langes Programm. Außerdem müssen Funktionen deklariert werden, bevor Sie sie verwenden.
Folgendes sollte beispielsweise einen Compilerfehler ergeben:
Der Fehler sollte sein, dass "SomeOtherFunction nicht deklariert ist", weil Sie es vor seiner Deklaration aufrufen. Eine Möglichkeit, dies zu beheben, besteht darin, SomeOtherFunction über SomeFunction zu verschieben. Ein anderer Ansatz besteht darin, zuerst die Funktionssignatur zu deklarieren:
Dies lässt den Compiler wissen: Schauen Sie irgendwo im Code nach, es gibt eine Funktion namens SomeOtherFunction, die void zurückgibt und keine Parameter akzeptiert. Wenn Sie also Code finden, der versucht, SomeOtherFunction aufzurufen, geraten Sie nicht in Panik und suchen Sie stattdessen danach.
Stellen Sie sich vor, Sie haben SomeFunction und SomeOtherFunction in zwei verschiedenen .c-Dateien. Sie müssen dann "SomeOther.c" in Some.c einschließen. Fügen Sie nun SomeOther.c einige "private" Funktionen hinzu. Da C keine privaten Funktionen kennt, wäre diese Funktion auch in Some.c verfügbar.
Hier kommen .h-Dateien ins Spiel: Sie geben alle Funktionen (und Variablen) an, die Sie aus einer .c-Datei exportieren möchten, auf die in anderen .c-Dateien zugegriffen werden kann. Auf diese Weise erhalten Sie so etwas wie einen öffentlichen / privaten Bereich. Sie können diese .h-Datei auch an andere Personen weitergeben, ohne Ihren Quellcode freigeben zu müssen - .h-Dateien funktionieren auch mit kompilierten .lib-Dateien.
Der Hauptgrund liegt also in der Bequemlichkeit, im Schutz des Quellcodes und in der leichten Entkopplung zwischen den Teilen Ihrer Anwendung.
Das war allerdings C. C ++ hat Klassen und private / öffentliche Modifikatoren eingeführt. Während Sie also immer noch fragen können, ob sie benötigt werden, erfordert C ++ AFAIK vor der Verwendung immer noch eine Deklaration von Funktionen. Viele C ++ - Entwickler sind oder waren auch C-Entwickler und haben ihre Konzepte und Gewohnheiten in C ++ übernommen - warum ändern, was nicht kaputt ist?
quelle
Erster Vorteil: Wenn Sie keine Header-Dateien haben, müssten Sie Quelldateien in andere Quelldateien aufnehmen. Dies würde dazu führen, dass die eingeschlossenen Dateien erneut kompiliert werden, wenn sich die eingeschlossene Datei ändert.
Zweiter Vorteil: Es ermöglicht die gemeinsame Nutzung der Schnittstellen, ohne den Code zwischen verschiedenen Einheiten (verschiedenen Entwicklern, Teams, Unternehmen usw.) zu teilen.
quelle
Die Notwendigkeit von Header-Dateien ergibt sich aus den Einschränkungen, die der Compiler hat, um die Typinformationen für Funktionen und / oder Variablen in anderen Modulen zu kennen. Das kompilierte Programm oder die kompilierte Bibliothek enthält nicht die Typinformationen, die der Compiler benötigt, um an Objekte zu binden, die in anderen Kompilierungseinheiten definiert sind.
Um diese Einschränkung zu kompensieren, lassen C und C ++ Deklarationen zu, und diese Deklarationen können mithilfe der # include-Direktive des Präprozessors in Module aufgenommen werden, die sie verwenden.
Sprachen wie Java oder C # enthalten andererseits die Informationen, die für die Bindung in der Ausgabe des Compilers erforderlich sind (Klassendatei oder Assembly). Daher besteht keine Notwendigkeit mehr, eigenständige Deklarationen zu verwalten, die von Clients eines Moduls aufgenommen werden müssen.
Der Grund dafür, dass die Bindungsinformationen nicht in der Compilerausgabe enthalten sind, ist einfach: Sie werden zur Laufzeit nicht benötigt (zur Kompilierungszeit erfolgt eine Typprüfung). Es würde nur Platz verschwenden. Denken Sie daran, dass C / C ++ aus einer Zeit stammt, in der die Größe einer ausführbaren Datei oder Bibliothek eine große Rolle spielte.
quelle
C ++ wurde entwickelt, um der C-Infrastruktur moderne Programmiersprachenfunktionen hinzuzufügen, ohne unnötig etwas an C zu ändern, das sich nicht speziell auf die Sprache selbst bezieht.
Ja, zu diesem Zeitpunkt (10 Jahre nach dem ersten C ++ - Standard und 20 Jahre nach dem Beginn der ernsthaften Zunahme der Nutzung) ist es leicht zu fragen, warum es kein geeignetes Modulsystem gibt. Offensichtlich würde jede neue Sprache, die heute entworfen wird, nicht wie C ++ funktionieren. Aber darum geht es in C ++ nicht.
Der Sinn von C ++ ist es, evolutionär zu sein, eine reibungslose Fortsetzung der bestehenden Praxis, nur neue Funktionen hinzuzufügen, ohne (zu oft) Dinge zu beschädigen, die für die Benutzergemeinschaft angemessen funktionieren.
Dies bedeutet, dass einige Dinge schwieriger sind (insbesondere für Leute, die ein neues Projekt starten) und einige Dinge einfacher sind (insbesondere für diejenigen, die vorhandenen Code pflegen) als andere Sprachen.
Anstatt zu erwarten, dass C ++ zu C # wird (was sinnlos wäre, da wir bereits C # haben), warum nicht einfach das richtige Werkzeug für den Job auswählen? Ich selbst bemühe mich, bedeutende Teile neuer Funktionen in einer modernen Sprache zu schreiben (ich verwende zufällig C #), und ich habe eine große Menge an vorhandenem C ++, das ich in C ++ behalte, weil es keinen wirklichen Wert hätte, es neu zu schreiben alles. Sie integrieren sich sowieso sehr gut, so dass es weitgehend schmerzlos ist.
quelle
Nun, C ++ wurde 1998 ratifiziert, aber es war schon viel länger in Gebrauch, und die Ratifizierung legte in erster Linie die aktuelle Nutzung fest, anstatt eine Struktur aufzuerlegen. Und da C ++ auf C basiert und C Header-Dateien hat, hat C ++ diese auch.
Der Hauptgrund für Header-Dateien besteht darin, die separate Kompilierung von Dateien zu ermöglichen und Abhängigkeiten zu minimieren.
Angenommen, ich habe foo.cpp und möchte Code aus den Dateien bar.h / bar.cpp verwenden.
Ich kann "bar.h" in foo.cpp einschließen und dann foo.cpp programmieren und kompilieren, auch wenn bar.cpp nicht existiert. Die Header-Datei verspricht dem Compiler, dass die Klassen / Funktionen in bar.h zur Laufzeit vorhanden sind und alles enthält, was er bereits wissen muss.
Wenn die Funktionen in bar.h keine Körper haben, wenn ich versuche, mein Programm zu verknüpfen, wird es natürlich nicht verknüpft und ich erhalte eine Fehlermeldung.
Ein Nebeneffekt ist, dass Sie Benutzern eine Header-Datei geben können, ohne Ihren Quellcode preiszugeben.
Ein weiterer Grund ist, dass Sie, wenn Sie die Implementierung Ihres Codes in der * .cpp-Datei ändern, aber den Header überhaupt nicht ändern, nur die * .cpp-Datei kompilieren müssen, anstatt alles, was sie verwendet. Wenn Sie viel Implementierung in die Header-Datei einfügen, wird dies natürlich weniger nützlich.
quelle
Es wird keine separate Header-Datei mit den gleichen Funktionen wie in main benötigt. Es wird nur benötigt, wenn Sie eine Anwendung mit mehreren Codedateien entwickeln und eine Funktion verwenden, die zuvor nicht deklariert wurde.
Es ist wirklich ein Umfangsproblem.
quelle
Tatsächlich sind Header-Dateien sehr nützlich, wenn Sie Programme zum ersten Mal untersuchen. Durch das Auschecken von Header-Dateien (nur mit einem Texteditor) erhalten Sie einen Überblick über die Architektur des Programms, im Gegensatz zu anderen Sprachen, in denen Sie zum Anzeigen von Klassen und ausgefeilte Tools verwenden müssen ihre Mitgliedsfunktionen.
quelle
Ich denke, der wahre (historische) Grund für Header-Dateien war, dass es für Compiler-Entwickler einfacher war ... aber dann bieten Header-Dateien Vorteile.
Überprüfen Sie diesen vorherigen Beitrag für weitere Diskussionen ...
quelle
Nun, Sie können C ++ perfekt ohne Header-Dateien entwickeln. Tatsächlich verwenden einige Bibliotheken, die intensiv Vorlagen verwenden, nicht das Paradigma der Header- / Codedateien (siehe Boost). In C / C ++ können Sie jedoch nichts verwenden, was nicht deklariert ist. Eine praktische Möglichkeit, damit umzugehen, ist die Verwendung von Header-Dateien. Außerdem profitieren Sie von der Freigabe der Benutzeroberfläche ohne Freigabe von Code / Implementierung. Und ich denke, es wurde von den C-Erstellern nicht ins Auge gefasst: Wenn Sie gemeinsam genutzte Header-Dateien verwenden, müssen Sie die berühmten verwenden:
Das ist nicht wirklich ein Sprachmerkmal, sondern ein praktischer Weg, um mit multipler Inklusion umzugehen.
Ich denke also, als C erstellt wurde, wurden die Probleme mit der Vorwärtsdeklaration unterschätzt, und jetzt, wenn wir eine Hochsprache wie C ++ verwenden, müssen wir uns mit solchen Dingen befassen.
Eine weitere Belastung für uns arme C ++ - Benutzer ...
quelle
Wenn der Compiler Symbole, die in anderen Dateien definiert sind, automatisch herausfinden soll, müssen Sie den Programmierer zwingen, diese Dateien an vordefinierten Speicherorten abzulegen (wie die Struktur von Java-Paketen die Ordnerstruktur des Projekts bestimmt). Ich bevorzuge Header-Dateien. Außerdem benötigen Sie entweder die von Ihnen verwendeten Bibliotheksquellen oder eine einheitliche Methode, um die vom Compiler benötigten Informationen in Binärdateien abzulegen.
quelle