Hallo,
ich möchte in C++ ein paar Funktionen die thematisch zusammengehören
auslagern.
Dafür scheint das Konzept einer weiteren cpp und headerdatei Standard zu
sein...? Dadurch muss man jede neue Funktion auch in der Headerdatei
angeben und durch das getrennte kompilieren der main.cpp und
funktionen.cpp funktionieren inline Funktionen vermutlich auch nicht
mehr...?
Daher wäre es mir eigentlich lieber, dass der ausgelagerte Code so
kompiliert wird, als wäre alles in der main Datei. Die Trennung soll
also eigentlich nur mir zur Übersicht dienen. Das ganze Konstrukt mit
header usw finde ich eher umständlich gibt es da nicht eine "bessere"
Möglichkeit?
Viele Grüße
Janush B. schrieb:
> ich möchte in C++ ein paar Funktionen die thematisch zusammengehören
> auslagern.
> Dafür scheint das Konzept einer weiteren cpp und headerdatei Standard zu
> sein...?
Ja, aber... (siehe unten).
> Dadurch muss man jede neue Funktion auch in der Headerdatei
> angeben und durch das getrennte kompilieren der main.cpp und
> funktionen.cpp funktionieren inline Funktionen vermutlich auch nicht
> mehr...?
Doch.
> Daher wäre es mir eigentlich lieber, dass der ausgelagerte Code so
> kompiliert wird, als wäre alles in der main Datei. Die Trennung soll
> also eigentlich nur mir zur Übersicht dienen. Das ganze Konstrukt mit
> header usw finde ich eher umständlich gibt es da nicht eine "bessere"
> Möglichkeit?
Die Auslagerung der Funktionssignaturen in Headerdateien ist vor allem
dann notwendig, wenn eine Bibliothek im (kompilierten) Binärformat
ausgeliefert werden soll. Damit anderer Code diese Bibliothek dann
benutzen kann, muß er die Signaturen (Funktionsnamen, Parameter,
Rückgabewerte) kennen, und die werden ihm dann durch die Headerdatei
bekannt gemacht.
Ein weiterer Vorteil dieser Aufteilung zeigt sich bei großen Projekten
mit vielen .c- oder .cpp-Dateien. Deren Objektdateien müssen dann
nämlich nur einmal und ansonsten nur dann neu übersetzt werden, wenn sie
auch geändert worden sind. Insofern spart diese Aufteilung in Source-
und Headerdateien eine Menge unnötiger Compilierungen und somit Zeit.
Zuletzt dient die Aufteilung in Header- und Sourcedateien dazu, die
Schnittstelle von der Implementierung zu trennen.
Für das, was Du vorhast, reicht es, die Funktionen in die Headerdatei zu
schreiben -- also nicht nur die Funktionssignatur, sondern auch den Code
der Funktion. Anders gesagt: Du kannst die Funktionen einfach so, wie
sie aktuell in Deiner main.cpp stehen, 1:1 in eine Headerdatei kopieren
(die notwendigen Includes nicht vergessen, wenn die Headerdatei auch
noch in anderen Projekten genutzt werden solle) und die neue Headerdatei
dann einfach mit "#include <headerdatei.hpp>" inkludieren. Die
Präprozessor-Direktive "#include" macht nichts anderes, als den Code der
inkludierten Datei dorthin zu kopieren, wo das "#include" steht.
Aber Achtung: das so in Deine main.cpp kopierte wird jedesmal
mitübersetzt, wenn Du Deine main.cpp übersetzt. Das kostet natürlich
jedesmal Zeit.
Um eventuelle Probleme durch ein mehrfaches Inkludieren einer
Headerdatei zu vermeiden, haben sich sogenannte Safeguards etabliert:
1 | #ifndef _HEADERDATEI_HPP
| 2 | #define _HEADERDATEI_HPP
| 3 |
| 4 | [...code here...]
| 5 |
| 6 | #endif // _HEADERDATEI_HPP
|
Manche Compiler bieten dazu eine eigene Präprozessor-Direktive an, die
aber nicht von allen Compilern unterstützt wird und daher nicht portabel
ist:
1 | #pragma once
| 2 |
| 3 | [...code here...]
|
Sheeva P. schrieb:
> Du kannst die Funktionen einfach so, wie
> sie aktuell in Deiner main.cpp stehen, 1:1 in eine Headerdatei kopieren
> (die notwendigen Includes nicht vergessen, wenn die Headerdatei auch
> noch in anderen Projekten genutzt werden solle) und die neue Headerdatei
> dann einfach mit "#include <headerdatei.hpp>" inkludieren. Die
> Präprozessor-Direktive "#include" macht nichts anderes, als den Code der
> inkludierten Datei dorthin zu kopieren, wo das "#include" steht.
Kannst du machen, ist aber keine so tolle Idee; sobald der Header
mehrfach benutzt wird, ist dann der Linker unglücklich. Die
Include-Guards helfen dagegen auch nicht. Lieber den Code in die
cpp-Datei schreiben und nur die Definition in den Header.
Sven B. schrieb:
> Kannst du machen, ist aber keine so tolle Idee; sobald der Header
> mehrfach benutzt wird, ist dann der Linker unglücklich. Die
> Include-Guards helfen dagegen auch nicht.
Wie das? Bitte zeig' doch mal einen Anwendungsfall. Bis dahin bleibe ich
bei meiner Überzeugung, daß alle standardkompatiblen Compiler,
Präprozessor und Linker damit vollkommen glücklich sind.
> Lieber den Code in die cpp-Datei schreiben und nur die Definition
> in den Header.
Die Boost-Entwickler liefern die meisten ihrer Libs "header-only" aus
[1]. Haben die keine Ahnung?
[1]
http://www.boost.org/doc/libs/1_64_0/more/getting_started/windows.html#header-only-libraries
Hat man mehrere Übersetzungseinheiten, die jeweils identische
Definitionen durchführen, verletzt man die one-definition-rule (ODR).
Deswegen:
- nur Deklarationen in Header
- Defininiton in cpp-Dateien
Templates selbst sind keine konkreter Code: sie werden erst später (in
der cpp-Datei) instanziiert. Doppelte Instanziierungen in
unterschiedlichen cpp-Dateien werden ggf. vom Linker wieder entfernt.
Sheeva P. schrieb:
> Sven B. schrieb:
>> Kannst du machen, ist aber keine so tolle Idee; sobald der Header
>> mehrfach benutzt wird, ist dann der Linker unglücklich. Die
>> Include-Guards helfen dagegen auch nicht.
>
> Wie das? Bitte zeig' doch mal einen Anwendungsfall.
test.h: 1 | #ifndef TEST_H
| 2 | #define TEST_H
| 3 |
| 4 | void funktion()
| 5 | {
| 6 | }
| 7 |
| 8 | #endif // TEST_H
|
a.cpp:
main.cpp: 1 | #include "test.h"
| 2 |
| 3 | int main()
| 4 | {
| 5 | }
|
Ergebnis des Compilers: 1 | $ g++ main.cpp a.cpp
| 2 | /tmp/ccZBCtOk.o: In Funktion `funktion()':
| 3 | a.cpp:(.text+0x0): Mehrfachdefinition von `funktion()'
| 4 | /tmp/ccfUjpCV.o:main.cpp:(.text+0x0): first defined here
| 5 | collect2: error: ld returned 1 exit status
|
> Bis dahin bleibe ich bei meiner Überzeugung, daß alle standardkompatiblen
> Compiler, Präprozessor und Linker damit vollkommen glücklich sind.
Es verletzt die "one definition rule", es sei denn, man markiert die
Funktionen explizit als "inline" oder als "static".
> Die Boost-Entwickler liefern die meisten ihrer Libs "header-only" aus
> [1]. Haben die keine Ahnung?
>
> [1]
>
http://www.boost.org/doc/libs/1_64_0/more/getting_started/windows.html#header-only-libraries
Zitat von dort:
"Most Boost libraries are header-only: they consist entirely of header
files containing templates and inline functions"
Inline habe ich oben genannt, und Templates sind ein ganz anderer Fall.
Die müssen im Header implementiert sein, damit sie funktionieren.
Rolf M. schrieb:
> Es verletzt die "one definition rule", es sei denn, man markiert die
> Funktionen explizit als "inline" oder als "static".
Da der TO das genau wegen inlining machen will, ist es eigentlich ganz
sinnvoll, da dann auch inline davor zu schreiben...
Und wenns dann tatsächlich um C++ und Memberfunktionen geht, die per
default inline, wenn man sie gleich in die Klassendeklaration schreibt,
auch für nicht-Templates.
Oliver
Janush B. schrieb:
> Dafür scheint das Konzept einer weiteren cpp und headerdatei Standard zu
> sein...? Dadurch muss man jede neue Funktion auch in der Headerdatei
> angeben und durch das getrennte kompilieren der main.cpp und
> funktionen.cpp funktionieren inline Funktionen vermutlich auch nicht
> mehr...?
Üblicherweise deklarierst Du nur die Funktionen in einem header, die Du
auch aus der entsprechenden Implementierungsdatei exportieren möchtest.
Sprich: Nur die Funktionen, die von ausserhalb dieser
Implementierungsdatei aufrufen möchtest. Alle anderen Funktionen kannst
Du mit `static` deklarieren.
Inlinen kann der Linker das am Ende immer noch. Bzw. der Compiler kann
das natürlich immer noch mit den Funktionen, innerhalb einer
Implementierungsdatei (translation unit) tun.
Aber: gerade Berufsanfänger überschätzen die Wirkung von `inline`. Ich
würde auf inline verzichten und am Ende mit einem Profiler messen, wo
Optimierungen am meisten Effekt haben.
Wenn Du den unbedingt die Wurzel allen Übels in Deinen Code streuen
möchtest, kannst Du auch inline Funktionen im header implementieren.
> Daher wäre es mir eigentlich lieber, dass der ausgelagerte Code so
> kompiliert wird, als wäre alles in der main Datei.
Das möchtest Du aber nur so lange, so lange Dein Projekt sehr klein
bleibt. Ansonsten muss der Compiler bei jeder kleinsten Änderung Dein
komplettes Projekt übersetzen. Ich kenne da so ein Projekt, da dauert
dann der Build immer 10 Minuten, egal wie klein die Änderung war.
> Die Trennung soll
> also eigentlich nur mir zur Übersicht dienen. Das ganze Konstrukt mit
> header usw finde ich eher umständlich gibt es da nicht eine "bessere"
> Möglichkeit?
Nein, es gibt z.Z. keine bessere Möglichkeit. Es gibt einige
Bestrebungen, Modules in C++ zu implementieren.
Ansonsten sind header auch ein wunderbarer Ort, eine Funktion ausgibig
zu Dokumentieren. Wenn ich Software designe, fange ich in der Regel mit
den headern an und dokumentiere Klassen und Funktionen, bis ich mir
sicher bin, dass das so funktionieren wird.
mfg Torsten
Torsten R. schrieb:
> Janush B. schrieb:
>
>> Dafür scheint das Konzept einer weiteren cpp und headerdatei Standard zu
>> sein...? Dadurch muss man jede neue Funktion auch in der Headerdatei
>> angeben und durch das getrennte kompilieren der main.cpp und
>> funktionen.cpp funktionieren inline Funktionen vermutlich auch nicht
>> mehr...?
>
> Üblicherweise deklarierst Du nur die Funktionen in einem header, die Du
> auch aus der entsprechenden Implementierungsdatei exportieren möchtest.
> Sprich: Nur die Funktionen, die von ausserhalb dieser
> Implementierungsdatei aufrufen möchtest. Alle anderen Funktionen kannst
> Du mit `static` deklarieren.
>
> Inlinen kann der Linker das am Ende immer noch. Bzw. der Compiler kann
> das natürlich immer noch mit den Funktionen, innerhalb einer
> Implementierungsdatei (translation unit) tun.
Die Bedeutung von inline wird oft missverstanden: inline heisst nicht
notwendigerweise, dass der Compiler diese Funktion ohne "call" in den
Code einbaut. Diese Bedeutung von inline ist nur ein Compile-Hint.
inline bedeutet im engeren Sinn, dass des mehrere(!) Definitionen in
unterschiedlichen(!) Übersetzungseinheiten geben darf, ohne das es zu
einer Verletzung der ODR kommt.
Sheeva P. schrieb:
> Sven B. schrieb:
>> Kannst du machen, ist aber keine so tolle Idee; sobald der Header
>> mehrfach benutzt wird, ist dann der Linker unglücklich. Die
>> Include-Guards helfen dagegen auch nicht.
>
> Wie das? Bitte zeig' doch mal einen Anwendungsfall. Bis dahin bleibe ich
> bei meiner Überzeugung, daß alle standardkompatiblen Compiler,
> Präprozessor und Linker damit vollkommen glücklich sind.
Beispiel steht ja schon oben ... du brauchst nur mehrere compilation
units.
>> Lieber den Code in die cpp-Datei schreiben und nur die Definition
>> in den Header.
>
> Die Boost-Entwickler liefern die meisten ihrer Libs "header-only" aus
> [1]. Haben die keine Ahnung?
Bei boost ist halt quasi alles ein Template, dann hast du eh keine
andere Wahl mehr. Die Regeln für Template- und Inline-Funktionen sind
aber anders, ja.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
|