private Methoden vs. Funktionen in Übersetzungseinheit
-
Wenn ich eine Methode so schreiben kann, dass sie keinen Zugriff auf Objektdaten benötigt, so mache ich aus Ihr gerne eine Funktion, die nur in der Übersetzungseinheit bekannt ist. Ich lasse sie (wenn ich sie sonst nicht benötige) auch direkt in der Übersetzungseinheit der Klasse. Das versuche ich so oft wie möglich.
Wie sieht das bei Euch aus? Ganz normaler Usus oder hat es Nachteile?
-
Wenn ich dich richtig verstanden habe, dann machst du Funktionen, die von Clients der Klasse nicht benötigt werden (also Implementationsdetails sind) und die keinen Zugriff auf private Member der Klasse haben, zu freien Funktionen lokal in der .cpp der Klasse.
Das ist genau der richtige Weg. Die Alternative, eine private Methode daraus zu machen würde unnötige Compilezeit-Abhängigkeiten erzeugen, da dann jede Änderung in der Signatur der Funktion zu einem Recompile aller Clients führen würde.
Außerdem haben derartige Hilfsfunktionen nach Möglichkeit nichts im Interface einer Klasse verloren (dass private Methoden allgemein im Header einer Klasse stehen müssen ist eine der Unzulänglichkeiten der Sprache C++, die allerdings durch das pimpl-Idiom häufig ausgebügelt werden kann).
-
Danke für die Bestätigung!
Das pimpl-Idom ist mir auch bekannt. Nutze es aber bisher noch nicht.
-
(dass private Methoden allgemein im Header einer Klasse stehen müssen ist eine der Unzulänglichkeiten der Sprache C++
Beziehungsweise einen solchen Header-Mechanismus ist vollständig abwesend. Header sind ja nur ein gängiges Idiom von C++, um nicht in jede Quelldatei die Klassendefinitionen hinschreiben zu müssen.
-
drakon schrieb:
(dass private Methoden allgemein im Header einer Klasse stehen müssen ist eine der Unzulänglichkeiten der Sprache C++
Beziehungsweise einen solchen Header-Mechanismus ist vollständig abwesend. Header sind ja nur ein gängiges Idiom von C++, um nicht in jede Quelldatei die Klassendefinitionen hinschreiben zu müssen.
Ersetze "Header" in meiner Aussage durch "Klassendefinition/Interface"
-
pumuckl schrieb:
drakon schrieb:
(dass private Methoden allgemein im Header einer Klasse stehen müssen ist eine der Unzulänglichkeiten der Sprache C++
Beziehungsweise einen solchen Header-Mechanismus ist vollständig abwesend. Header sind ja nur ein gängiges Idiom von C++, um nicht in jede Quelldatei die Klassendefinitionen hinschreiben zu müssen.
Ersetze "Header" in meiner Aussage durch "Klassendefinition/Interface"
Jup, dann passt es.

-
Wie ist das mit privaten Attributen vs. globale Variable im cpp-File?
Die sind dann ja auch nach außen hin sogar besser gekapselt wie Attribute/Member (da nicht sichtbar). Haben aber den Vor- oder Nachteil, dass sie von jeder Funktion innerhalb der Übersetzungseinheit verwendbar sind.
Edit: Frage gestrichen
(Macht nur Sinn mit static-Variablen, schon klar)
-
Die sind dann ja auch nach außen hin sogar besser gekapselt wie Attribute/Member (da nicht sichtbar). Haben aber den Vor- oder Nachteil, dass sie von jeder Funktion innerhalb der Übersetzungseinheit verwendbar sind.
Nach aussen ja, aber stell dir vor du hast ein .cpp File, dass ~5000 Zeilen Code enthält, wenn möglich noch mit verschiedenen Klassen. Dann hast du dasselbe Problem wie sonst auch.
Ich würde sagen, dass, wenn du meinst so etwas zu brauchen du dir das sehr gut überlegst und die Vor/Nachteile abwägst. Es gibt ja kein richtig, oder falsch. Es kommt halt immer drauf an. In der Regel wird man so etwas nicht brauchen, aber wenn doch und das die geeignetste Lösung ist, warum also nicht so?
-
Roger Wilco schrieb:
Wie ist das mit privaten Attributen vs. globale Variable im cpp-File?
Wenn statische Attribute nicht im Header benötigt werden, würde ich mir diese Abhängigkeit sparen und globale Variablen in der .cpp-Datei anlegen. Hier finde ich globale Variablen durchaus okay, da sie eigentlich nur "lokal global" sind.
Zu dem Thema habe ich vor einiger Zeit einen Thread aufgemacht:
http://www.c-plusplus.net/forum/viewtopic-var-t-is-237943.htmldrakon schrieb:
Nach aussen ja, aber stell dir vor du hast ein .cpp File, dass ~5000 Zeilen Code enthält, wenn möglich noch mit verschiedenen Klassen. Dann hast du dasselbe Problem wie sonst auch.
Naja, bei soviel Code - und vor allem bei mehreren Klassen - wäre es wohl an der Zeit, sich die Vorzüge einer Auftrennung durch den Kopf gehen lassen.

-
Nexus schrieb:
drakon schrieb:
Nach aussen ja, aber stell dir vor du hast ein .cpp File, dass ~5000 Zeilen Code enthält, wenn möglich noch mit verschiedenen Klassen. Dann hast du dasselbe Problem wie sonst auch.
Naja, bei soviel Code - und vor allem bei mehreren Klassen - wäre es wohl an der Zeit, sich die Vorzüge einer Auftrennung durch den Kopf gehen lassen.

Naja. Ich habe schon solche Files gesehen. Und die waren nicht unbedingt unsauber programmiert. Die Klasse hatte einfach viele Funktionen und dann noch grosse Funktionen. Im übrigen kann es ja durchaus sein, dass die Verwandten Klassen in ein File kommen.
-
drakon schrieb:
Und die waren nicht unbedingt unsauber programmiert. Die Klasse hatte einfach viele Funktionen und dann noch grosse Funktionen.
Das ist für mich zumindest ein Hinweis darauf, dass man besser hätte auftrennen können.
drakon schrieb:
Im übrigen kann es ja durchaus sein, dass die Verwandten Klassen in ein File kommen.
Bei kleinen Klassen kann das ja gut sein. Aber bei grösseren hat man dadurch meist nur mehr Abhängigkeiten. Besonders, wenn die Implementierungsdatei so gross wird, dass man ein Problem mit der Übersicht bekommt, spricht wirklich nichts mehr gegen mehrere Dateien.
-
Das ist für mich zumindest ein Hinweis darauf, dass man besser hätte auftrennen können.
Naja. Ich bezweifle, dass es eine gute Idee gewesen wäre das ganze in mehrere Klassen aufzuteilen. Viele Funktionen wären es so oder so geworden und wenn da alles noch auf 10-20 Klassen aufgeteilt worden wäre.. Ich weiss ja nicht..

Klar ist das nicht der Regelfall, aber bei wirklich grossen Projekten können Dateien, denke ich recht schnell zu solchen Grössen anwachsen. Vor allem, wenn mehrere Leute dran arbeiten.
-
drakon schrieb:
Naja. Ich bezweifle, dass es eine gute Idee gewesen wäre das ganze in mehrere Klassen aufzuteilen. Viele Funktionen wären es so oder so geworden und wenn da alles noch auf 10-20 Klassen aufgeteilt worden wäre.. Ich weiss ja nicht..

Ich meine ja nicht 10-20 Klassen.

Aber wenigstens die zwei Klassen, die im der selben Datei implementiert werden, in zwei Dateien aufteilen. Und lange Funktionen kann man auch oft in kürzere aufspalten (evtl. freie).Ich bin normalerweise auch nicht so der Aufteilungs-Typ. Gerade lange Funktionen gibt es bei mir nicht selten. Aber wenn ich bei .cpp-Dateien so langsam den Überblick verliere und gerade eine schöne Möglichkeit zur Auftrennung sehe, versuche ich das auch durchzuführen.
-
Ich erzähle mal, wie es bei mir ist.
Oft gibt es private Methoden. Die werden ganz normal behandelt wie alle Methoden: Wenn deren Code so einfach ist, daß er selber die beste Dokumentation ist, bleibt er im Header, ansonsten wandert er raus. (Fußnote1)
Manchmal gibt es private Klassen. Die waren aber bisher immer so einfach, daß sie inklusive aller ihrer Methoden innerhalb der umgebenden Klasse inline leben durften.
Manchmal gibt es globale Funktionen, die man öffentlich machen darf. Davon gibt es zwei Sorten:
- "private reentrante Funktionen" wie string stripHtmlTags(string), die wandern regelmäßig in eine Datei wie globals.cpp nebst globals.hpp, vielleicht auch tools, utils oder so.
- "private böse Funktionen" wie rand(), die kriegen eine eigene *.cpp.Und dann gibt es noch die globalen Funktionen, die einfach nur global sind, weil sie der Einfachheit oder Geschwindigkeit halber auf globale Variablen zugreifen. Extrem selten eigentlich. Bei mir zum Beispiel wo PageWithHead<head=DequeuHead,data=Foo> die Page (immer 8192 Bytes, am anfang der Header, danach eine Array) benutzt und die Page.cpp einfach darauf verzichten will, die ganzen Hilfsunktonen des Seitenallokators private in Page zu machen. Die sind dann global static in der Page.cpp (Fußnote2).
Fußnote1: Bei Primzahlenfunktionen ist isPrime(...) doch kompliziert und inline. Triviale Prüfungen wie ob die Zahl sehr klein ist oder gerade ist und ein wenig mehr, will ich inline haben, daß auch der dümmste Compiler es inlinisieren kann (nicht muss).
Fußnote2: So wenigstens in der Theorie. Kleinere Versuche mit Page und seitenbasierten Containern habe ich gemacht und die Geschwindigkeit war überzeugend. Nebenbei eröffnet das Konzept weitere spaßige Sachen. Ich werde alles auf seitenbasierte Container umstellen, koste es, was es wolle, um die Grenzen dieses Konzepts kennenzulernen. Da ich fast nie große Arrays verwende, sondern eh nur std::vector, und wenn ich große vectors verwende, weil sie so schnell sortierbar sind, und das seitenbasierte Speichern nach merge sort schreit und das auch schnell ist, unter umständen mit einem Acht-Takte-Allokator für Pages, und ich dann doch wieder selten sortiere, ... Leider bin ich zu doof, eine überzeugend schnelle Implementierung für eine seitenbasierte PriorityQueue zu finden, aber die ist KO-Kriterium. Alle anderen Container halte ich für trivial.