Ich finde Header-Dateien beim Durchsuchen von C ++ - Quelldateien nützlich, da sie eine "Zusammenfassung" aller Funktionen und Datenelemente in einer Klasse enthalten. Warum haben so viele andere Sprachen (wie Ruby, Python, Java usw.) keine solche Funktion? Ist dies ein Bereich, in dem die Ausführlichkeit von C ++ nützlich ist?
20
Antworten:
Ursprünglicher Zweck von Header-Dateien war es, die Kompilierung in einem Durchgang und die Modularität in C zu ermöglichen. Indem die Methoden deklariert wurden, bevor sie verwendet wurden, war nur ein einziger Kompilierungsdurchgang zulässig. Dieses Zeitalter ist lange vorbei, da unsere leistungsstarken Computer problemlos und manchmal sogar schneller als C ++ - Compiler Kompilierungen mit mehreren Durchläufen durchführen können.
C ++ war abwärtskompatibel mit C, um die Header-Dateien zu behalten, fügte jedoch eine Menge hinzu, was zu einem recht problematischen Design führte. Mehr in FQA .
Aus Gründen der Modularität wurden Header-Dateien als Metadaten zum Code in Modulen benötigt. Z.B. Welche Methoden (und in C ++ - Klassen) sind in welcher Bibliothek verfügbar? Es war offensichtlich, dass Entwickler dies schreiben mussten, da die Kompilierungszeit teuer war. Heutzutage ist es kein Problem, dass der Compiler diese Metadaten aus dem Code selbst generiert. Java- und .NET-Sprachen tun dies normalerweise.
Also nein. Header-Dateien sind nicht gut. Sie waren, als wir noch Compiler und Linker auf separaten Disketten haben mussten und die Kompilierung 30 Minuten dauerte. Heutzutage stören sie nur noch und sind Zeichen für schlechtes Design.
quelle
Während sie für Sie als Dokumentationsform nützlich sein können, ist das System, das Header-Dateien umgibt, außerordentlich ineffizient.
C wurde so entworfen, dass jeder Kompilierungsdurchlauf ein einzelnes Modul erstellt. Jede Quelldatei wird in einem separaten Durchlauf des Compilers kompiliert. Header-Dateien hingegen werden für jede der Quelldateien, die auf sie verweisen, in diesen Kompilierungsschritt eingefügt.
Dies bedeutet, dass Ihre Header-Datei, wenn sie in 300 Quelldateien enthalten ist, während der Erstellung Ihres Programms 300 Mal analysiert und kompiliert wird. Genau das Gleiche mit genau dem gleichen Ergebnis, immer und immer wieder. Dies ist eine enorme Zeitverschwendung und einer der Hauptgründe, warum die Erstellung von C- und C ++ - Programmen so lange dauert.
Alle modernen Sprachen vermeiden absichtlich diese absurde kleine Ineffizienz. Stattdessen werden normalerweise in kompilierten Sprachen die erforderlichen Metadaten in der Build-Ausgabe gespeichert, sodass die kompilierte Datei als eine Art Nachschlagewerk fungiert, das beschreibt, was die kompilierte Datei enthält. Alle Vorteile einer Header-Datei, die automatisch und ohne zusätzlichen Arbeitsaufwand erstellt wird.
Abwechselnd in interpretierten Sprachen bleibt jedes Modul, das geladen wird, im Speicher. Wenn Sie auf eine Bibliothek verweisen oder diese einbeziehen oder eine Bibliothek benötigen, wird der zugehörige Quellcode gelesen und kompiliert, der bis zum Ende des Programms gespeichert bleibt. Wenn Sie es auch an anderer Stelle benötigen, ist keine zusätzliche Arbeit erforderlich, da es bereits geladen wurde.
In beiden Fällen können Sie die in diesem Schritt erstellten Daten mit den Sprachwerkzeugen "durchsuchen". Normalerweise wird die IDE einen Klassenbrowser haben. Und wenn die Sprache eine REPL hat, kann sie häufig auch verwendet werden, um eine Dokumentationszusammenfassung aller geladenen Objekte zu erstellen.
quelle
Es ist nicht klar, was Sie damit meinen, wenn Sie die Datei nach Funktionen und Datenelementen durchsuchen. Die meisten IDEs bieten jedoch Tools zum Durchsuchen der Klasse und Anzeigen von Klassendatenelementen.
Zum Beispiel: Visual Studio hat
Class View
undObject browser
bietet Ihre gewünschte Funktionalität. Wie in den folgenden Screenshots.quelle
Ein zusätzlicher Nachteil von Header-Dateien ist, dass das Programm von der Reihenfolge ihrer Aufnahme abhängt und der Programmierer zusätzliche Schritte unternehmen muss, um die Korrektheit sicherzustellen.
Zusammen mit dem Makrosystem von C (das von C ++ geerbt wird) führt dies zu vielen Fallstricken und verwirrenden Situationen.
Zur Veranschaulichung: Wenn ein Header ein Symbol mithilfe von Makros definiert und ein anderer Header das Symbol auf andere Weise verwendet, z. B. als Name für eine Funktion, hat die Reihenfolge der Einbeziehung großen Einfluss auf das Endergebnis. Es gibt viele solcher Beispiele.
quelle
Ich mochte Header-Dateien immer, da sie eine Art Schnittstelle zur Implementierung bieten, zusammen mit einigen zusätzlichen Informationen wie Klassenvariablen, die alle in einer einfach anzuzeigenden Datei enthalten sind.
Ich sehe eine Menge C # -Code (für den nicht zwei Dateien pro Klasse erforderlich sind), der mit zwei Dateien pro Klasse geschrieben wurde - eine mit der tatsächlichen Klassenimplementierung und eine mit einer Schnittstelle. Dieser Entwurf ist gut zum Verspotten geeignet (in einigen Systemen unerlässlich) und hilft beim Definieren der Dokumentation der Klasse, ohne dass die kompilierten Metadaten mit der IDE angezeigt werden müssen. Ich würde so weit gehen, um seine gute Praxis zu sagen.
Daher ist es eine gute Sache, wenn C / C ++ eine Art Äquivalent einer Schnittstelle in Header-Dateien vorschreibt.
Ich weiß, dass es Befürworter anderer Systeme gibt, die sie aus Gründen nicht mögen, darunter "es ist schwieriger, Code einfach zu hacken, wenn man Dinge in zwei Dateien packen muss", aber ich bin der Meinung, dass das Hacken von Code sowieso keine gute Praxis ist , und sobald Sie anfangen, Code mit ein wenig mehr Nachdenken zu schreiben / zu entwerfen, werden Sie selbstverständlich Header / Interfaces definieren.
quelle
Eigentlich würde ich sagen, dass Header-Dateien nicht großartig sind, weil sie die Oberfläche und die Implementierung verschleiern. Die Absicht bei der Programmierung im Allgemeinen und insbesondere bei OOP ist es, eine definierte Schnittstelle zu haben und die Implementierungsdetails auszublenden. In einer C ++ - Headerdatei werden jedoch sowohl die Methoden, die Vererbung und die öffentlichen Mitglieder (Schnittstelle) als auch die privaten Methoden und die privaten Mitglieder angezeigt (ein Teil der Implementierung). Ganz zu schweigen davon, dass in einigen Fällen Code oder Konstruktoren in die Header-Datei eingefügt werden und einige Bibliotheken Vorlagencode in die Header-Dateien einfügen, was die Implementierung wirklich mit der Schnittstelle vermischt.
Ich glaube, die ursprüngliche Absicht war es, es dem Code zu ermöglichen, andere Bibliotheken, Objekte usw. zu verwenden, ohne den gesamten Inhalt des Skripts importieren zu müssen. Alles was Sie brauchen, ist der Header zum Kompilieren und Verknüpfen. Das spart Zeit und Zyklen. In diesem Fall ist es eine anständige Idee, aber es ist nur eine Möglichkeit, diese Probleme zu lösen.
Was das Durchsuchen der Programmstruktur angeht, bieten die meisten IDEs diese Möglichkeit, und es gibt eine Reihe von Tools, mit denen Sie die Benutzeroberflächen austauschen, Code analysieren, dekompilieren usw. können, um zu sehen, was unter der Decke passiert.
Warum implementieren andere Sprachen nicht die gleiche Funktion? Nun, weil andere Sprachen von anderen Menschen kommen und diese Designer / Schöpfer eine andere Vorstellung davon haben, wie die Dinge funktionieren sollten.
Die beste Antwort ist, sich an die Aufgaben zu halten, die Sie erledigen müssen, und Sie glücklich zu machen.
quelle
In vielen Programmiersprachen kann Code in einer Programmeinheit, wenn ein Programm in mehrere Kompilierungseinheiten unterteilt ist, nur dann Dinge verwenden, die in einer anderen definiert sind, wenn der Compiler über Mittel verfügt, um zu wissen, was diese Dinge sind. Anstatt zu verlangen, dass ein Compiler, der eine Kompilierungseinheit verarbeitet, den gesamten Text jeder Kompilierungseinheit überprüft, die alles definiert, was in der vorliegenden Einheit verwendet wird, ist es am besten, dass der Compiler während der Verarbeitung jeder Einheit den vollständigen Text der zu kompilierenden Einheit empfängt mit ein paar Informationen von allen anderen.
In C ist der Programmierer dafür verantwortlich, zwei Dateien für jede Einheit zu erstellen - eine mit Informationen, die nur beim Kompilieren dieser Einheit benötigt werden, und eine mit Informationen, die auch beim Kompilieren anderer Einheiten benötigt werden. Dies ist ein ziemlich unangenehmes, hackiges Design, aber es vermeidet die Notwendigkeit, einen Sprachstandard festzulegen, der angibt, wie Compiler Zwischendateien generieren und verarbeiten sollen. Viele andere Sprachen verwenden einen anderen Ansatz, bei dem ein Compiler aus jeder Quelldatei eine Zwischendatei generiert, die alles in dieser Quelldatei beschreibt, was für externen Code zugänglich sein soll. Dieser Ansatz vermeidet die Notwendigkeit, doppelte Informationen in zwei Quelldateien zu haben, erfordert jedoch, dass eine Kompilierungsplattform die Dateisemantik auf eine Weise definiert hat, die für C nicht erforderlich ist.
quelle