Problem mit Klassenreferenzen



  • Hallo

    Gleich vorweg gesagt: Ich bin relativ neu in C++ (habe aber schon eine solide Erfahrung im Programmieren aus Java und anderen C++-Projekten) und deshalb kann es sein, dass meine Frage bestimmt ziemlich einfach wird aber ich hab jetzt echt schon Stunden rumprobiert und gegoogelt und anscheinend bin ich einfach zu doof, dass allein hin zu kriegen, weswegen ich jetzt euch frage muss.

    Folgendes, wahrscheinlich ganz einfaches Problem: Ich möchte in Zwei verschiedenen Klassen jeweils eine Referenz auf ein Objekt der anderen Klasse haben und diese nach Möglichkeit im Konstruktor übergeben.

    Ich habe 4 Dateien (jeweils eine *.h und *.cpp pro Klasse):

    Eins.h:

    #pragma once
    #include "Zwei.h"
    
    class Eins
    {
    private:
    	Zwei zwei;
    public:	
    	Eins(void);
    	~Eins(void);
    };
    

    Eins.cpp:

    #include "Eins.h"
    
    Eins::Eins(void)
    {	
    }
    
    Eins::~Eins(void)
    {
    }
    

    Zwei.h:

    #pragma once
    #include "Eins.h"
    
    class Zwei
    {
    private:
    	Eins eins;
    public:
    	Zwei(Eins eins);
    	~Zwei(void);
    };
    

    Zwei.cpp:

    #include "Zwei.h"
    
    Zwei::Zwei(Eins e) : eins(e)
    {
    }
    
    Zwei::~Zwei(void)
    {
    }
    

    Ich hab mir vorgestellt, dass das vielleicht irgendwie so zu realisieren ist:

    Abgeänderte Eins.cpp:

    #include "Eins.h"
    
    Eins::Eins(void)
    {	
    	zwei = new Zwei(this);
    }
    
    Eins::~Eins(void)
    {
    }
    

    Hab jetzt aber folgende Fehler in der Fehlerkonsole und komm nicht weiter 😞

    Fehler	1	error C2146: Syntaxfehler: Fehlendes ';' vor Bezeichner 'zwei'	c:\users\paul.paul-laptop\documents\visual studio 2010\projects\test\test\eins.h Zeile: 7
    Fehler	2	error C4430: Fehlender Typspezifizierer - int wird angenommen. Hinweis: "default-int" wird von C++ nicht unterstützt.	c:\users\paul.paul-laptop\documents\visual studio 2010\projects\test\test\eins.h Zeile: 7
    Fehler	3	error C4430: Fehlender Typspezifizierer - int wird angenommen. Hinweis: "default-int" wird von C++ nicht unterstützt.	c:\users\paul.paul-laptop\documents\visual studio 2010\projects\test\test\eins.h Zeile: 7
    Fehler	4	error C2146: Syntaxfehler: Fehlendes ';' vor Bezeichner 'eins'	c:\users\paul.paul-laptop\documents\visual studio 2010\projects\test\test\zwei.h Zeile: 7
    Fehler	5	error C4430: Fehlender Typspezifizierer - int wird angenommen. Hinweis: "default-int" wird von C++ nicht unterstützt.	c:\users\paul.paul-laptop\documents\visual studio 2010\projects\test\test\zwei.h Zeile: 7
    Fehler	6	error C4430: Fehlender Typspezifizierer - int wird angenommen. Hinweis: "default-int" wird von C++ nicht unterstützt.	c:\users\paul.paul-laptop\documents\visual studio 2010\projects\test\test\zwei.h Zeile: 7
    Fehler	7	error C2061: Syntaxfehler: Bezeichner 'Eins'	c:\users\paul.paul-laptop\documents\visual studio 2010\projects\test\test\zwei.h Zeile: 9
    Fehler	8	IntelliSense: Für die Klasse ""Zwei"" ist kein Standardkonstruktor vorhanden.	c:\users\paul.paul-laptop\documents\visual studio 2010\projects\test\test\eins.cpp	Zeile: 6
    

    Mein Ziel ist es, dass ich danach von beiden Objekten auf Methoden des jeweils anderen zugreifen kann. Eine möglichst einfache Lösung wäre super, da ich wie gesagt nicht viel von C++ verstehe 😞

    Danke schonmal,
    Paul


  • Mod

    Mein Ziel ist es, dass ich danach von beiden Objekten auf Methoden des jeweils anderen zugreifen kann.

    Das ist aber eine ungewöhnliche Anforderung. Ich bin mir fast sicher, dass du es eigentlich nicht so möchtest und bloß irgendwas aus Java nachbaust, was man sonst nie so machen würde.

    Aber gut:

    class Zwei;
    
    class Eins
    {
      Zwei &zwei;
      public:
      Eins(Zwei &zwei): zwei(zwei) {}  
    };
    
    class Zwei
    {
      Eins &eins;
      public:
      Zwei(Eins &eins): eins(eins) {}  
    };
    

    Und nun sitzt du in der Zwickmühle und erkennst, wieso das ein doofes Design ist: Wo bekommst du jetzt das erste Eins-Objekt her, um damit ein Zwei-Objekt zu initialisieren? Das Eins-Objekt bräuchte schließlich ein Zwei-Objekt. Das Zwei-Objekt bräuchte ein Eins-Objekt. Das Eins-Objekt bräuchte ein ...

    Ich denke, du erkennst den Designfehler, oder?

    P.S.: Noch zwei weitere (Java?)-Sachen, die du dir abgewöhnen solltest:
    1. new ist in C++ zu 99.9% falsch, wenn es von einem Java-Programmierer benutzt wird.
    2. Leere Funktionen bedeuten, dass du die Funktion wohl nicht brauchst.



  • Hey

    erstmal Danke für die schnelle Antwort.
    Dass das so wie du es geschrieben hast ein Designfehler ist, weiß ich ^^

    Ich glaub ich hab meine Frage zu schwammig formuliert bzw. ungeschickt:

    Meine Klasse "Eins" soll im Konstruktor nicht eine Referenz auf ein bestehendes Objekt der Klasse "Zwei" enthalten, sondern erstellen.

    Sprich dass ich den Konstruktor der Klasse "Eins" aufrufe und dieser dann eine Instanz der Klasse "Zwei" erzeugt mit der Referenz auf sich selbst.

    In etwa so:

    class Eins
    {
      Zwei &zwei;
      public:
      Eins(void) {
          zwei = Zwei(this);
      }  
    };
    
    class Zwei
    {
      Eins &eins;
      public:
      Zwei(Eins &eins): eins(eins) {}  
    };
    

    Geht das so?

    Danke schonmal 🙂

    Paul

    P.S: Die leere Methode ist soweit ich weiß der Dekonstruktor (?)(wie gesagt ich programmier noch nicht lang in C++) und der wurde mir automatisch erstellt ^^
    Ok, ich werde nie new benutzen 😃 danke ^^



  • Hach, ich liebe die Java-Leute, die auf C++ umsteigen... 😉

    Raid0or - Paul schrieb:

    class Eins
    {
      Zwei &zwei;
      public:
      Eins(void) {
          zwei = Zwei(this);
      }  
    };
     
     
    class Zwei
    {
      Eins &eins;
      public:
      Zwei(Eins &eins): eins(eins) {}  
    };
    

    Eine Referenz (gekennzeichnet durch das & bei einer Variablendeklaration) ist ein Verweis auf ein Objekt. Im Konstruktor von eins wird das einer temporären Variable zugewiesen (klappt das überhaupt oder gibts da ein Error, ich denke error, oder?), was Schwachsinn ist.
    this ist übrigens ein Pointer auf ein Objekt, damit du das Objekt selber hast musst du *this schreiben.
    Aber was willst du machen?
    Ich empfehle dir übrigens ein gutes C++-Buch. Da sind zwar schon viele Sachen, die du kennst, aber das kannst du ja überfliegen. Denn das Behandlen von Objekten ist in C++ anders als in Java. Und Pointer verwirren auch noch mal.



  • Nathan schrieb:

    Hach, ich liebe die Java-Leute, die auf C++ umsteigen... 😉

    😃 Ist halt dann doch was anderes ^^

    Nein geht nicht, gibt mit fehler 😞

    Also von Pointern und so versteh ich zumindest oberflächlich was 😃 Aber alles andere als gut ^^

    Mein Ziel ist ein Programm mit mehreren Threads zu schreiben, die auf Sockets lauschen. Das Programm drum rum mit Sockets und Threads (via Boost) funktioniert auch alles Super (und ja ich versteh auch was von kritischen Abschnitten etc.)

    Ich mein wenn ich alles in eine Klasse geschrieben hätte funktioniert auch alles bestens, aber ich als Java-Programmierer krieg Anfälle wenn ich sowas seh 😃

    Und zum Thema Bücher:
    Ich hab mir schon mal was aus der Uni-Bib ausgeliehen und alles, aber ich bin nicht der Typ, der aus Büchern lernt ^^ V.a. beim Programmieren mach ich lieber alles auf Try&Error und wenn ich mal wo stecken bleib schau ich mir an wie man es schön macht ^^ Hat beim Java lernen eigentlich ganz gut funktioniert 😃 Logischerweise kommen da jetzt am Anfang weder optimierte noch gute Programme raus, aber ich will ja was draus lernen und es mir merken, was ich falsch gemacht habe ^^ Und wenn ich mir jetzt ein Buch durchlese merk ich mir das nie 😃

    Ich kann ja mal schreiben, wie ich das in Java gemacht hätte:

    public class Eins
    {
        private Zwei zwei;
        public Eins()
        {
            zwei = new Zwei(this);
        }
    }
    
    public class Zwei
    {
        private Eins eins;
        public Zwei(Eins e)
        {
             eins=e;
        }
    }
    

    Und ich frag mich jetzt, wie ich sowas in C++ umsetzen kann.

    P.S. Ich setz das Programm nur zur Übung in C++ um, die Java Variante funktioniert super ^^ Ich wills halt lernen 🙂



  • class Eins;
    
    class Zwei
    {
      Eins &eins;
      public:
      Zwei(Eins &eins): eins(eins) {}
    };
    
    class Eins
    {
      Zwei zwei;
      public:
      Eins(void) : zwei(*this) {}  
    };
    


  • Dein C++ Ansatz war schon fast richtig, hier die korrigierte (d.h. kompilierfähige) Version:

    class Eins
    {
      Zwei zwei;
    public:
      Eins() : zwei(*this)
      {}
    };
    
    class Zwei
    {
      Eins &eins;
    public:
      Zwei(Eins &eins): eins(eins)
      {}
    };
    

    Du erhältst aber evtl. eine Warnung vom Compiler, daß du 'this' an ein anderes Objekt im Konstruktor übergibst (obwohl es noch nicht vollständig konstruiert ist). Macht aber nichts, solange man keine Zugriffe auf dieses Objekt macht (und das passiert ja im anderen Konstruktor auch nicht).

    Der andere Ansatz wäre dann doch mittels 'new' (oder einem Smartpointer) den Speicher zu reservieren.

    Und wenn du das Aufteilen der Klassen in verschiedene Dateien (Header + Source) machen willst, dann benötigt nur 'Eins' ein #include "Zwei", bei 'Zwei' reicht dann eine Vorwärtsreferenz:

    class Eins;
    
    class Zwei
    {
      Eins &eins;
    public:
      Zwei(Eins &eins): eins(eins) {}
    };
    

    Die generelle Frage aber bleibt, warum muß Zwei eine Referenz auf Eins haben?



  • Th69 schrieb:

    hier die korrigierte (d.h. kompilierfähige) Version:

    class Eins
    {
      Zwei zwei;
    public:
      Eins() : Zwei(*this)
      {
      }
    };
    
    class Zwei
    {
      Eins &eins;
    public:
      Zwei(Eins &eins): eins(eins) {}
    };
    

    Kompiliert nicht.



  • Raid0or - Paul schrieb:

    Also von Pointern und so versteh ich zumindest oberflächlich was 😃 Aber alles andere als gut ^^

    Das, was du in Java unter "Referenz" verstehst, gleicht eher einem Zeiger in C++ als einer Referenz in C++. Du hast also während deiner Java-Zeit schon ständig Zeiger verwendet.

    Raid0or - Paul schrieb:

    Ich hab mir schon mal was aus der Uni-Bib ausgeliehen und alles, aber ich bin nicht der Typ, der aus Büchern lernt ^^ V.a. beim Programmieren mach ich lieber alles auf Try&Error

    Das ist für das Lernen von C++ mit die schlechteste Strategie. Ohne Scheisz.

    Raid0or - Paul schrieb:

    und wenn ich mal wo stecken bleib schau ich mir an wie man es schön macht ^^ Hat beim Java lernen eigentlich ganz gut funktioniert 😃 Logischerweise kommen da jetzt am Anfang weder optimierte noch gute Programme raus, aber ich will ja was draus lernen und es mir merken, was ich falsch gemacht habe ^^ Und wenn ich mir jetzt ein Buch durchlese merk ich mir das nie 😃

    Sagt ja keiner, dass du ein Buch erst komplett durchlesen sollst, bevor Du Dinge ausprobiert. Gar nicht erst in sowas reinzugucken ist allerdings verschenkte Zeit!

    Raid0or - Paul schrieb:

    Ich kann ja mal schreiben, wie ich das in Java gemacht hätte:

    public class Eins
    {
        private Zwei zwei;
        public Eins()
        {
            zwei = new Zwei(this);
        }
    }
    
    public class Zwei
    {
        private Eins eins;
        public Zwei(Eins e)
        {
             eins=e;
        }
    }
    

    Und warum hättest du das so gemacht? Warum muss Zwei Eins kennen?

    Raid0or - Paul schrieb:

    Und ich frag mich jetzt, wie ich sowas in C++ umsetzen kann.

    Es geht. So ähnlich. Was würdest du sagen, ist die Beziehung zwischen Eins und Zwei? Ist ein Zwei immer ein (logischer) Bestandteil von Eins? Kann ein Zwei separat existieren ohne ein Eins? Dann wäre das eher eine "kennt" statt einer "besteht aus"-Beziehung. Ich frage deswegen, weil es Implikationen für die Lebenszeit eines Zwei-Objekts hat. Lebenszeit ist eine Sache, an die Du noch nicht intensiv denkst. Solltest du aber. In C++ brauchst du für etwas wie ein Zwei-Objekt nämlich einen Verantwortlichen, der sich darum kümmert. Garbage Collection ist nicht. Das coole ist, dass man diese Verantworlichkeit deligieren kann und dass Objekte wirklich Teil eines anderen sein können (logisch oder sogar physisch), wobei dieses Teil dann auch automatisch entfernt wird, wenn das "umschließende" Objekt zerstört wird.

    Beispiel:

    class Motor
    {
      :::
    };
    
    class Golf
    {
      :::
      Motor mymotor; // nix mit Zeiger oder Referenz. Ein Auto 
                     // BESTEHT AUS einem Motor und anderem Zeug.
    };
    

    Wenn der Motor jetzt noch das Auto kennen muss, in dem er sitzt, fände ich das echt komisch. Da würde dann an der Kapselung etwas nicht stimmen. Geht, fänd' ich aber fragwürdig vom Design her:

    class Golf;
    
    class Motor
    {
    public:
      explicit Motor(Golf* aptr);
      :::
    private:
      Golf* das_auto_in_dem_ich_sitze;
      :::
    };
    
    class Golf
    {
    public:
      Golf();
      :::
    private:
      :::
      Motor mymotor;
    };
    
    Golf::Golf()
    : motor(this)
    {
    }
    

    Man könnte das jetzt noch etwas entschärfen:

    class Fahrzeug
    {
    public:
      virtual void sonstwas() = 0;
    protected:
      ~Fahrzeug() {}
    };
    
    class Motor
    {
    public:
      explicit Motor(Fahrzeug* aptr);
      :::
    private:
      Fahrzeug* das_fahrzeug_in_dem_ich_sitze;
      :::
    };
    
    class Golf : public Fahrzeug
    {
    public:
      Golf();
      void sonstwas() override;
      :::
    private:
      :::
      Motor mymotor;
    };
    
    Golf::Golf()
    : motor(this)
    {
    }
    

    Vielleicht auch so:

    class Golf : public Fahrzeug
    {
    public:
      void sonstwas() override;
      bool hat_motor() const {return !!mymotor;}
      void neuer_motor(unique_ptr<Motor> m);
      :::
    private:
      :::
      std::unique_ptr<Motor> mymotor;
    };
    
    void Golf::neuer_motor(unique_ptr<Motor> m)
    {
      mymotor=std::move(m);
      mymotor->eingebaut_in(this);
    }
    

    falls es einen Golf ohne Motor geben kann und du ihm zu einem späteren Zeitpunkt einen geben willst. In diesem Fall könnte man sogar mit einer abstrakten Motorklasse arbeiten, da diese Indirektion Polymorphie erlaubt.

    Manchmal kann man aber auch Ähnliches erreichen ganz ohne Vererbung:

    class Motor
    {
    public:
      explicit Motor(std::function<void()> sonstwas_callback)
      : sonstwas_callback(std::move(sonstwas_callback))
      {}
      :::
    private:
      std::function<void()> sonstwas_callback;
      :::
    };
    
    class Golf
    {
    public:
      Golf();
    private:
      Motor mymotor;
    
      void sonstwas();
    };
    
    Golf::Golf()
    : motor([this](){this->sonstwas();})
    {
      :::
    }
    
    void Golf::sonstwas()
    {
      using namespace std;
      cout << "Der Motor hat sich gemeldet!\n";
    }
    

    Das hat den Vorteil, dass man keine so große Weitsicht beim Design, was sich auf Vererbung beschränkt, haben muss. Man kann im Prinzip alles mit allem verknoten, indem man entsprechende "Funktoren" in std::function<>-Objekte verpackt.

    Ich hoffe, dass ich dir damit etwas geholfen habe.
    Ich bin auch ein von-Java-Umsteiger. 😉

    PS: Kannst du bitte Deine Fehlermeldungen editieren und die Zeilen kürzen oder quote statt code verwenden? Ich glaube, ich muss wegen der langen Zeilen deines Fehlermeldungstexts immer horizontal scrollen, um die Texte hier lesen zu können.

    kk



  • krümelkacker schrieb:

    class Motor
    {
    public:
      explicit Motor(Fahrzeug* aptr);
      :::
    private:
      Fahrzeug* das_fahrzeug_in_dem_ich_sitze;
      :::
    };
    

    FTFY



  • Nathan schrieb:

    FTFY

    Danke, habs geändert. Hatte vorhin auch noch einen anderen Fehler gesehen. Ich übernehme keine Garantie, dass da keine anderen Fehler drin sind. Aber das jetzt auch noch zu testen/kompilieren möchte ich nicht. 😉 Ich hoffe, es ist klar, wie's gemeint ist. 😃



  • Okay,

    Also erstmal vielen Dank für die ganzen Rückmeldungen ! v.a. dass mir gleich noch viel drum rum erklärt wird, was man nicht machen sollte und so weiter! Echt klasse 🙂

    Ich hab ja in die Bücher reingeschaut und auch gelesen ^^ Aber ich irgendwie hatte ich immer das Gefühl nicht wirklich etwas zu lernen 😕 Wie ichs schon versucht habe zu erklären: Es fällt mir viel einfach, einfach mal zu Programmieren und dann nachzusehen obs richtig ist bzw. wie mans eleganter lösen könnte (Ressourcen/Effizienz). Weil dann habe ich meistens schon was fertig programmiert was auch funktioniert und dann seh ich auch ein Ergebnis und dieses versuche ich dann besser zu machen. Also ist nicht so, dass ich einfach irgendwas mache ^^

    Also ich versteh schon, dass es im Regelfall nicht sinnvoll ist, dass die beiden Klassen gegenseitig voneinander wissen müssen, aber ich bin irgendwie zu keiner anderen sinnvollen Lösung gekommen (außer in eine Klasse schreiben ^^).

    Mein Ziel ist es 2 Klassen zu haben, aber dass die eine nicht ohne die andere existieren kann.

    Sprich ich hab in meinem Projekt eine Klasse die nennt sich Verwaltung und eine Klasse Server.

    Beide haben mehrere Threads am laufen, die verschiedene Sachen machen (Verwaltung eben was se halt so machen muss und Server eben die Socket-Verwaltung und die Sockets an sich) und ich will, dass die Verwaltung arbeiten kann ohne zwangsläufig einen Server haben zu müssen. Wenn aber ein Server existiert soll dieser den Input von den Sockets an meine Verwaltung übergeben, indem er dort eine Methode aufruft, die entsprechend dann Werte verändert, die wiederum die Threads der Verwaltung beeinflussen was diese machen.

    Das Problem ist, dass der recv Befehl ein Blocking-Call ist, d.h. ich kann ihn nicht von der Verwaltung ausführen lassen, da der entsprechende Thread ansonsten nicht weiterarbeiten kann 😕

    Bin auch gerne offen für andere Vorschläge wenns besser umsetzbar ist 🙂

    Und ums mal am Beispiel mit dem Motor zu sagen:
    Der Motor muss wissen, was er antreiben soll, weil es bringt mir ja nichts wenn er im Auto ist, aber er nicht weiß, dass er mein Auto antreiben soll. Und Quasi ist dann der Motor mein Server der eben den Input von mir (Gas geben z.B.) bekommt und dann eben die Geschwindigkeit meines Autos verändert. Aber mein Auto kann ja auch ohne, dass der Motor eingebaut ist rollen ^^ (Macht das Sinn? :D)

    Nochmals Vielen Dank 🙂 🙂


  • Mod

    Raid0or - Paul schrieb:

    Mein Ziel ist es 2 Klassen zu haben, aber dass die eine nicht ohne die andere existieren kann.

    Da geht doch schon im ersten Schritt der Modellierung schief. Dein Ziel ist bestimmt nicht, zwei Klassen zu haben. Erst Recht nicht, zwei Klassen mit diesen komischen Abhängigkeiten. Du hast irgendein ganz anderes Ziel (der eigentliche Zweck deines Programms) und nun glaubst du, dass diese zwei Klassen dir bei der Lösung helfen. Das heißt nicht, dass dies wirklich ein guter Lösungsweg ist. Wenn man auf solch komische Designfehler stößt (du gibst ja selber zu, dass das nicht so dolle ist), ist das sogar ein sehr starker Hinweis, dass diese Idee nicht gut ist.

    Zu dem von dir beschriebenen System fällt mir spontan ein, dass es da ja wohl anscheinend einen Kommunikator gibt. Dieser ist wohl unabhängig von Server und Verwaltung, kann aber jeweils von diesen benutzt werden. Vielleicht ist ein Kommunikator Teil des Servers. Kommt auf die Details an. Dann kann er aber trotzdem auch von der Verwaltung benutzt werden, wenn der Server dies anbietet. Oder die Verwaltung hat ihren eigenen Kommunikator. Jedenfalls sehe ich in der Beschreibung keine Verbindung zwischen Server und Verwaltung. Zwischen den kommunizierenden Objekten und dem Kommunikator kann eventuell eine Verbindung bestehen. Die Verwaltung/Server kennen vielleicht ihren Kommunikator. Der Kommunikator kennt sicherlich die Liste der Objekte, die von ihm Botschaften erwarten. Diese Liste kann sicherlich auch leer sein.



  • SeppJ schrieb:

    Raid0or - Paul schrieb:

    Mein Ziel ist es 2 Klassen zu haben, aber dass die eine nicht ohne die andere existieren kann.

    Da geht doch schon im ersten Schritt der Modellierung schief. Dein Ziel ist bestimmt nicht, zwei Klassen zu haben. Erst Recht nicht, zwei Klassen mit diesen komischen Abhängigkeiten.

    Ich bin echt nicht Fähig mich auszudrücken, also ich mein dass eine von denen allein existieren kann, aber die andere eben nicht ohne diese ^^ (siehe Java-Beispiel)

    Ich dachte mir auch, dass ich es über eine dritte Klasse mache, auf die beide zugreifen, wobei ich aber denke, dass es effizienter ist wenn ich das so löse. Weil dann spar ich mir komplett die dritte Klasse und zudem spar ich mir die Abfragen der Klasse Verwaltung, ob sich in dieser dritten Klasse etwas geändert hat, da der Server dann nicht in der dritten Klasse etwas ändert, sonder direkt in der Verwaltung, von der er abhängt. ^^



  • ...


Log in to reply