Kompilieren geht nicht so wie ich das will



  • Hallo Freunde der Sonne,

    Ich lerne gerade, wie man modularisiert.

    Der Herr Breymann sagt in seinem Buch, daß man eine Header-Datei (Schnittstelle) und eine cpp-Datei mit den Funktionsdefinitionen erzeugen muß. In der Implementation wie in der main-Datei wird dann die Schnittstelle eingebunden.

    Bei der Kompilierung müssen die Implementation und die main-Datei dann in Objektdateien umgewandelt, und zusammengebunden werden. Und jetzt meine Fragen:

    Wenn ich die Schnittstelle bereits in der Implementation inkludiere, wozu das selbe dann nochmal in der main-Datei?
    Wird die Implementation Header-Datei (hab mich verschrieben) wegen der Einbindung in die Dateien automatisch in Objektcode umgewandelt?

    Außerdem müßte ja die Implementation ausreichen, weil ja eine Funtkionsdefinition syntaktisch immer zugleich eine Funtkionsdeklaration ist. Mein Experiment hat allerdings nicht funktioniert:

    programm.cpp

    #include "funktion.cpp"
    
    int nicht_schliessen;
    
    int main()
    {
    	test();
    	cin >> nicht_schliessen;
    }
    

    funktion.cpp

    #include <iostream>
    
    using namespace std;
    
    void test()
    {
    	cout << "Es funktioniert!";
    }
    

    Kompilierung:

    g++ -std=c++14 -c funktion.cpp
    g++ -std=c++14 -c programm.cpp
    g++ -std=c++14 -o projekt
    .exe function.o programm.o
    

    Fehlermeldung:

    C:\Users\ADMINI~1\AppData\Local\Temp\cc4rRGhy.o:programm.cpp:(.text+0x0): multiple definition of `test()'
    funktion.o:funktion.cpp:(.text+0x0): first defined here
    collect2.exe: error: ld returned 1 exit status
    

    Warum funktioniert das nicht?

    Ich danke vom Herzen für Antworten!



  • Dexter1997 schrieb:

    Wenn ich die Schnittstelle bereits in der Implementation inkludiere, wozu das selbe dann nochmal in der main-Datei?

    Weil der Compiler beim Übersetzen von programm.cpp prüft, ob es eine Funktione mit diesem Namen geben soll, welchen Rückgabetyp sie hat und welche Parameter die Funktion erwartet. Das kann er der Deklaration im Header entnehmen.
    In Zukunft (mit C++ Modulen) wird das möglicherweise anders geregelt.

    Dexter1997 schrieb:

    Wird die Implementation Header-Datei (hab mich verschrieben) wegen der
    Einbindung in die Dateien automatisch in Objektcode umgewandelt?

    Kein Ahnung was du damit meinst. Die .cpp zusammen mit allen Includes ergibt eine Objektdatei.

    Dexter1997 schrieb:

    Warum funktioniert das nicht?

    Was macht include? Es nimmt den Text der Datei und kopiert ihn an die entsprechende Stelle. Wie die Fehlermeldung besagt hast du jetzt eben zwei Stellen, an denen die Funktion definiert wird:
    1. vor main
    2. in der zweiten cpp-Datei



  • Vielen Dank, das hat etwas Licht in die Sache gebracht!



  • Weil der Compiler beim Übersetzen von programm.cpp prüft, ob es eine Funktione mit diesem Namen geben soll, welchen Rückgabetyp sie hat und welche Parameter die Funktion erwartet. Das kann er der Deklaration im Header entnehmen.
    In Zukunft (mit C++ Modulen) wird das möglicherweise anders geregelt.

    Aber dadurch, daß die Schnittstelle bereits in der Implementation eingebunden wurde, und die Implementation wiederum in die main-Datei, sollte doch die Schnittstelle bereits vorhanden sein, oder nicht? mit -c werden ja im Prinzip die beiden Dateien wie mit #include zusammengebunden.



  • Das Problem ist nicht eine fehlende Schnittstelle, sondern eine doppelte Implementation:

    multiple definition of `test()'



  • Das habe ich bereits verstanden, ich möchte die andere Frage nochmal wiederholen, nur damit sie nicht untergeht:

    Aber dadurch, daß die Schnittstelle bereits in der Implementation eingebunden wurde, und die Implementation wiederum in die main-Datei, sollte doch die Schnittstelle bereits vorhanden sein, oder nicht? mit -c werden ja im Prinzip die beiden Dateien wie mit #include zusammengebunden.



  • Dexter1997 schrieb:

    Aber dadurch, daß die Schnittstelle bereits in der Implementation eingebunden wurde, und die Implementation wiederum in die main-Datei, sollte doch die Schnittstelle bereits vorhanden sein, oder nicht?

    Deine Begriffe sind schwammig.
    Wenn in funktion.cpp eine Include-Direktive für funktion.h steht, und in main.cpp eine Include-Direktive für funktion.cpp, dann, ja, ist der Inhalt von funktion.h in main.cpp bekannt. Aber so etwas tut man nicht, weil es, wie du siehst, zu Linkerfehlern wegen mehrfacher Definitionen führt.

    Dexter1997 schrieb:

    mit -c werden ja im Prinzip die beiden Dateien wie mit #include zusammengebunden.

    Nein.



  • Dexter1997 schrieb:

    Weil der Compiler beim Übersetzen von programm.cpp prüft, ob es eine Funktione mit diesem Namen geben soll, welchen Rückgabetyp sie hat und welche Parameter die Funktion erwartet. Das kann er der Deklaration im Header entnehmen.
    In Zukunft (mit C++ Modulen) wird das möglicherweise anders geregelt.

    Aber dadurch, daß die Schnittstelle bereits in der Implementation eingebunden wurde, und die Implementation wiederum in die main-Datei, sollte doch die Schnittstelle bereits vorhanden sein, oder nicht? mit -c werden ja im Prinzip die beiden Dateien wie mit #include zusammengebunden.

    Die Funktionsdeklaration im Header ist im erzeugten Objectcode nicht mehr vorhanden. Sie wird nur für die beschriebenen Prüfung benötigt, danach nicht mehr.

    Im Implementierungsmodul wird sie gar nicht benötigt.



  • Noch als Hinweis (nur zum Ausprobieren, nicht zum Verwenden!):

    Du kannst natürlich auch die cpp-Datei in dein main includen, so wie du es hier zeigst. Dann aber kannst du das alles nicht mehr separat compilieren und linken, denn das programm.cpp enthält dann ja bereits den gesamten code von funktion.cpp (das #include kopiert wirklich nur stumpf den Inhalt der Datei!).

    Dann musst du nur einmal kompilieren (programm.cpp ENTHÄLT dann funktion.cpp):
    g++ -std=c++14 -o programm.exe programm.cpp

    Das ist so natürlich schlecht. Insbesondere in großen Projekten würdest du dann immer alles zusammen kompilieren.
    Außerdem würdest du dann auch interne Dinge wie using-Befehle importieren, die man aus den Header-Dateien rauslässt usw.

    Daher: nie die .cpp includen. Nur die .h includen.


Anmelden zum Antworten