2016-06-09 5 views
2

Ich arbeite an einem großen Projekt in C++.Setzen Methodenimplementierungen der gleichen Klasse in verschiedenen Objektdateien

Ich habe viele Klassen, die Methoden haben, die völlig verschiedene Dinge tun (wie ein Dump, ändert ein anderes das Objekt, ein anderes überprüft es, um zu sehen, ob es gültig ist und so weiter ...).

Ist es ein guter Stil, um die Implementierung einer Methode für alle Klassen in einer Quelldatei (oder in einer Gruppe von Objektdateien, die archiviert werden können) und alle Implementierungen einer anderen Methode der Klassen in einem anderen Archiv?

Konnte das beim Linken gut sein, vielleicht wenn jemand die Dumping-Methoden nicht benötigt (zum Beispiel), oder es besser ist, die Methoden-Implementierungen derselben Klasse in der gleichen Quelldatei zu behalten, um keine Verwirrung zu stiften ?

+0

Jede Klasse ist in einem Header deklariert, der mit der Klasse selbst zu tun hat. Dann gibt es in der zugehörigen Quelldatei Implementierungen von Methoden, die mit dem Objekt selbst zu tun haben (wie Sie gesagt haben, getters, setter, ctors/dtors ...). Dann werden andere Methoden (die in fast der gesamten Klassenhierarchie überschrieben werden) in einem vollständig getrennten Teil des Projekts implementiert. – NoImaginationGuy

+0

Wenn Sie Archiv aus Objektdateien erstellen, wird Linker nur die Objektdateien auswählen, die benötigt werden, aber beachten Sie, dass, wenn es sich entscheidet, eine Objektdatei auszuwählen, die ganze Objektdatei enthalten ist, nicht nur ein Teil, der benötigt wird Ich denke, es ist besser, Funktionalität in mehreren Objektdateien zu trennen und dann Archiv von ihnen zu erstellen. – PcAF

Antwort

4

Es gibt Kompromisse.

Wenn Sie die Implementierung einer Funktion ändern, muss die gesamte Übersetzungseinheit in eine neue Objektdatei neu kompiliert werden.

Wenn Sie nur eine einzige Funktion pro Übersetzungseinheit schreiben, minimieren Sie die Länge der Kompilierungszeit, die durch unnötige Neuerstellungen verursacht wird.

Auf der anderen Seite, indem Sie eine einzelne Funktion pro Übersetzungseinheit schreiben, maximieren Sie die Länge der Kompilierungszeit von Grund auf, weil es langsamer ist, viele kleine TUs als einige wenige TUs zu kompilieren.

Die optimale Lösung ist die persönliche Präferenz, aber normalerweise irgendwo zwischen "single function per TU" und "one massiver TU für das gesamte Programm" (anstatt genau einer davon). Für Elementfunktionen ist eine TU pro Klasse eine beliebte Heuristik, aber nicht immer die beste Wahl.


Eine weitere Überlegung ist die Optimierung. Aufrufe von Nicht-Inline-Funktionen können inline erweitert werden, jedoch nur innerhalb derselben Übersetzungseinheit. Daher ist es für den Compiler einfacher, eine einzelne massive TU zu optimieren.

Natürlich können Sie die Funktionen inline in der Header-Datei definieren, aber das führt zu einem Problem beim erneuten Erstellen, denn wenn eine der Inline-Funktionen geändert wird, müssen alle, die den Header enthalten, neu erstellt werden. Dies ist ein schlechteres Problem, als einfach größere TUs zu haben, aber nicht so schlecht wie eine massive TU.

Also, die Definition von verwandten nicht-inline-Funktionen innerhalb der gleichen TU ermöglicht dem Compiler, über die Optimierung innerhalb dieser TU zu entscheiden, während eine Rebuild-Kaskade verhindert wird. Dies ist vorteilhaft, wenn diese verwandten Funktionen von einer Inline-Erweiterung profitieren und sich gegenseitig viel aufrufen würden.

Dieser Vorteil wird durch ganze Programmoptimierung gemildert.


Dritte Überlegung ist Organisation. Es ist wahrscheinlich, dass ein Programmierer, der die Elementfunktion einer Klasse betrachtet, auch an anderen Elementfunktionen dieser Klasse interessiert ist. Wenn sie in derselben Quelldatei gespeichert sind, können sie weniger Zeit für die Suche in der richtigen Datei aufwenden.

Der organisatorische Vorteil, alle Klassenfunktionen in einer gemeinsamen Quelldatei zu gruppieren, wird durch moderne IDEs gemildert, die einen schnellen Sprung von der Quelldatei zum Header und von dort zur anderen Funktion ermöglichen.


Vierte Überlegung ist die Leistung des Editors. Das Analysieren einer Datei mit Zehntausenden von Zeilen oder mehr kann langsam sein und je nach Parsing-Technik viel Speicher belegen. Eine massive TU verursacht das nicht notwendigerweise, da Sie separate Dateien verwenden können, die nur zusammen enthalten sind.

Auf der anderen Seite kann eine große Anzahl von Dateien für einige Dateibrowser (wahrscheinlich nicht viel heutzutage) und auch für Versionskontrollsysteme problematisch sein.


Endlich meine Meinung: Ich denke, dass eine Quelldatei pro Klasse eine anständige Heuristik ist. Aber es sollte nicht religiös befolgt werden, wenn es nicht angemessen ist.

+0

Ja! Die Frage bezog sich mehr auf das Projektmanagement als auf die Kompilierungseffizienz. Da sich die Funktionen nicht so oft ändern werden, ist eine massive TU in Bezug auf die Übersetzungsgeschwindigkeit in meinem Fall besser. – NoImaginationGuy

+0

@osnapitzkindle Wenn sich die Funktionen nicht ändern, müssen Sie überhaupt nicht kompilieren. Wenn sich eine einzelne Funktion ändert, bewirkt eine massive TU, dass das gesamte Programm neu aufgebaut wird, und eine einzige Funktion vor der TU erlaubt nur die Neukompilierung dieser einen Funktion. Ich bin zu dem entgegengesetzten Schluss gekommen. – user2079303

1

Einige Organisationen haben Regeln, die eine Definition pro Einheit vorschreiben. In diesen Organisationen kann eine Headerdatei nur eine Klasse definieren und eine Übersetzungseinheit kann nur eine Funktion definieren. Andere Organisationen schreiben höchstens eine Quelldatei für jede Header-Datei vor (einige Header-Dateien haben keine Implementierung).

Die optimale Lösung ist irgendwo dazwischen. Im Allgemeinen interessiert mich nicht die Compiler- oder Linker-Leistung. Ich sorge mich sehr um die Lesbarkeit und Wartbarkeit des Codes. Eine Quelldatei, die eine Klasse mit Tausenden von Zeilen implementiert, ist schwer zu navigieren. Es ist besser, diese Datei in mehrere Dateien zu unterteilen. Die Aufteilung in Hunderte von Dateien, eine Datei pro Funktion, führt zu einer schwer zu navigierenden Verzeichnisstruktur. Durch das Aufteilen in eng verwandte Funktionen bleiben die Verzeichnisstruktur und der Inhalt jeder Datei navigierbar.

Allerdings, und das ist eine große jedoch: Warum ist Ihre Klasse so groß, dass Sie sich darum kümmern müssen? Eine Klasse, deren Implementierung Tausende von Zeilen oder Dutzende von Dateien umfasst, ist ein Code-Geruch.

Verwandte Themen