Lokale Kopie von Base Klasse anlegen?



  • Naja, ich habe mehrere Derived Klassen die von einer Base Klasse erben. Jetzt will ich in einer Funktion (hier: foo) mit einer Instanz dieser Klassen arbeiten. Allerdings soll der Zustand der Instanz durch diese Funktion nach außen hin nicht verändert werden (intern soll er aber schon upgedatet werden, die Derived Klassen haben ja einen internen Speicher, hier: in_prev).

    Letztenlich ist das aber nicht so wichtig, das eigentliche Problem was ich im moment ja habe ist: Ich will eine einheitliche Implementierung einer Funktion in eine Derived Klasse aus einer Base Klasse, die aber den Typ der Derived Klasse verwendet. Das wäre ja auch unabhängig von meinem eigentlichen Problem sinnvoll, da es Code-Duplikate und implementierungs Fehler seitens der User verhindern würde.

    EDIT:

    Mr.Long schrieb:

    Wieso geht das nicht so:

    template<typename T> T* foo(T& base)
    {
        return new T(base);
    }
    

    Base ist doch abstrakt, davon kann ich ja keine Instanz erstellen oder was meinst du jetzt?



  • Bitte was?



  • happystudent schrieb:

    hustbaer schrieb:

    Die Methode soll ja nicht gleich sein, sie soll nicht mal das gleiche tun.
    In Derived1 soll sie tun: eine Derived1-Kopie anlegen.
    In Derived2 soll sie tun: eine Derived2-Kopie anlegen.
    In Derived3 soll sie tun: eine Derived3-Kopie anlegen.
    ...

    Ja gut, aber mit dem gleichen Argument könnte ich ja auch sagen cout macht nicht immer das gleiche sondern:

    Einmal will ich einen string ausgeben.
    Einmal will ich einen double ausgeben.
    Einmal will ich einen int ausgeben. etc.

    Ist ja auch so, trotzdem kann ich das da sehr elegant immer einheitlich machen.

    Achje bitte keine total unpassenden Vergleiche, hm?
    Du willst die *Implementierung* für verschiedene Typen nicht wiederholen, und das nochdazu ohne ein Template zu verwenden.
    Das kann iostreams auch nicht. Dort sind die Insertion-Operatoren für int, double, string etc. auch extra ausprogrammiert.
    Das einzige was bei iostreams "einheitlich" geht, ist die Syntax bei der *Verwendung*. Und das ist ja bei dir auch kein Problem, auch nicht wenn du die clone() Funktion für 100 verschiedene Klassen 100x per Hand neu runtertippst.
    Aufrufen kannst du sie ja immer gleich.

    happystudent schrieb:

    Aber dem entnehmen ich dass es keine "saubere" Lösung für so ein Problem gibt, richtig?

    Ich sehe nicht was daran unsauber sein soll.
    Aber es gibt noch eine andere Möglichkeit, hatte ich vergessen: CRTP.

    template <class Derived, class Base>
    class ThingBase : public Base
    {
        Derived* clone() const
        {
            return new Derived(*static_cast<Derived const*>(this));
        }
    };
    
    class DerivedThing : public ThingBase<DerivedThing, Thing>
    {
    };
    

    happystudent schrieb:

    Weil eigentlich könnte der Compiler doch bei einer Spracherweiterung wie im folgenden das Leisten oder?
    ...

    Was würde gegen so eine Erweiterung sprechen? Weil ich fände es schon sinnvoll wenn man sowas könnte, würde hier einiges vereinfachen.

    Sehr sehr viel, aber es würde zu lange dauern das zu erklären.



  • happystudent schrieb:

    EDIT:

    Mr.Long schrieb:

    Wieso geht das nicht so:

    template<typename T> T* foo(T& base)
    {
        return new T(base);
    }
    

    Base ist doch abstrakt, davon kann ich ja keine Instanz erstellen oder was meinst du jetzt?

    In deinem ersten Beispiel, würde der Compiler eine Funktion mit dem impliziet angegebenen Typen compilieren (d.h. du musst keinen < > angeben) welche dir eine Instanz von Derivered1 als Kopie erstellt, so wie du es wolltest, nicht?
    Allerdings verstehe ich noch immer nicht was du damit bewirken willst.



  • Mr.Long schrieb:

    In deinem ersten Beispiel, würde der Compiler eine Funktion mit dem impliziet angegebenen Typen compilieren (d.h. du musst keinen < > angeben) welche dir eine Instanz von Derivered1 als Kopie erstellt, so wie du es wolltest, nicht?
    Allerdings verstehe ich noch immer nicht was du damit bewirken willst.

    Äh nein, foo erstellt ja keine Kopie, da Derived als Referenz übergeben wird. Was verstehst du denn nicht bei meiner Erklärung?

    hustbaer schrieb:

    Ich sehe nicht was daran unsauber sein soll.

    Naja, ich muss den immer gleichen Code immer wieder in jede Derived Klasse reinkopieren. Komplett redundant und trivial.

    hustbaer schrieb:

    Sehr sehr viel, aber es würde zu lange dauern das zu erklären.

    Kannst du nicht wenigstens ein, zwei kleine Beispiele geben was genau? Wenns so viel ist muss es da ja was geben 😉

    Mich interessiert das einfach. Gut, die Syntax war ein bisschen unglücklich gewählt, aber sowas:

    struct Base
    {
      typedef std::get_child<Base>::type child_type; // So im type traits Stil halt
    
      virtual int out(int in) = 0;
      child_type* create(){ 
        child_type* e = new child_type();
        return e;
      }
    };
    
    struct Derived1 : Base
    {
      // bekommt jetzt ein create implementiert
      int out(int in){ return in; }
    };
    

    Wenn kein Child vorhanden halt Compilefehler?



  • Wenn du in der Baseklasse unbedingt wissen willst, von was sie gerbt wurdest, kannst du eine virtuelle-Funktion definieren die dann von jeder Kindklasse überschrieben wird und eine eindeutige Object-Id zurück gibt.



  • Ja, aber genauso gut kann ich dann auch gleich die copy methode implementieren lassen. Es geht darum dass ich eine Funktion habe die für alle Child Klassen gleich ist, bis auf den verwendeten Typ.



  • happystudent schrieb:

    Naja, ich muss den immer gleichen Code immer wieder in jede Derived Klasse reinkopieren. Komplett redundant und trivial.

    Nein, es ist nicht der selbe Code. Er unterscheidet sich im Namen der abgeleiteten Klasse. Wäre es der selbe Code, würde ja auch ein parameterloses Makro reichen, nen?

    Ansonsten: Siehe CRTP Lösung oben.

    happystudent schrieb:

    Mich interessiert das einfach.

    Dann denk drüber nach.

    happystudent schrieb:

    Gut, die Syntax war ein bisschen unglücklich gewählt, aber sowas:

    struct Base
    {
      typedef std::get_child<Base>::type child_type; // So im type traits Stil halt
    
      virtual int out(int in) = 0;
      child_type* create(){ 
        child_type* e = new child_type();
        return e;
      }
    };
     
    struct Derived1 : Base
    {
      // bekommt jetzt ein create implementiert
      int out(int in){ return in; }
    };
    

    Wenn kein Child vorhanden halt Compilefehler?

    struct Base
    {
      typedef std::get_child<Base>::type child_type;
    
      virtual int out(int in) = 0;
      child_type* create(){ 
        child_type* e = new child_type();
        return e;
      }
    };
    
    struct Derived1 : Base
    {
      int out(int in){ return in; }
    };
    
    struct Derived2 : Base
    {
      int out(int in){ return in; }
    };
    
    Base::child_type foo; // welcher typ?
    auto bar = &Base::create; // welcher typ? zeiger auf welche Funktion?
    

    Uswusf.
    Das sind echt so triviale Dinge wo's da gleich hakt, da müsstest du selbst draufkommen.
    Und dann macht es nicht viel Spass dir das zu erklären, weil man halt den Eindruck hat dass du da ne sehr verschwommene Idee hattest die du süss findest, und nun meinst du es müsste gehen. Aber ohne dass du dir mal ein paar Minuten Zeit genommen hast die Sache durchzudenken.



  • hustbaer schrieb:

    Nein, es ist nicht der selbe Code. Er unterscheidet sich im Namen der abgeleiteten Klasse. Wäre es der selbe Code, würde ja auch ein parameterloses Makro reichen, nen?

    Das ist schon klar dass das nicht der selbe Code ist. Aber das ist bei templates ja auch so. Da generiert sich der Compiler ja auch von alleine das zusammen was er braucht und nur so etwas fände ich halt praktisch.

    hustbaer schrieb:

    Ansonsten: Siehe CRTP Lösung oben.

    Ist auch nur (noch) eine andere Schreibweise für das gleiche Problem.

    hustbaer schrieb:

    Uswusf.
    Das sind echt so triviale Dinge wo's da gleich hakt, da müsstest du selbst draufkommen.
    Und dann macht es nicht viel Spass dir das zu erklären, weil man halt den Eindruck hat dass du da ne sehr verschwommene Idee hattest die du süss findest, und nun meinst du es müsste gehen. Aber ohne dass du dir mal ein paar Minuten Zeit genommen hast die Sache durchzudenken.

    Nein, das ist jetzt eine Unterstellung, ich hab mir sehr wohl Zeit genommen. Genauer gesagt denke ich den halben Tag schon darüber nach. Zu deinen Beispielen:

    Base::child_type foo; // Compilefehler: child_type nur im Kontext eines childs verfügbar
    auto bar = &Base::create; // Compilefehler: Base hat kein member create, da dieses nur in childs implementiert wird
    


  • happystudent schrieb:

    hustbaer schrieb:

    Nein, es ist nicht der selbe Code. Er unterscheidet sich im Namen der abgeleiteten Klasse. Wäre es der selbe Code, würde ja auch ein parameterloses Makro reichen, nen?

    Das ist schon klar dass das nicht der selbe Code ist. Aber das ist bei templates ja auch so. Da generiert sich der Compiler ja auch von alleine das zusammen was er braucht und nur so etwas fände ich halt praktisch.

    Du willst doch aber kein Template. Wir bewegen uns im Kreis.

    happystudent schrieb:

    hustbaer schrieb:

    Ansonsten: Siehe CRTP Lösung oben.

    Ist auch nur (noch) eine andere Schreibweise für das gleiche Problem.

    Also willst du das Feature nur, weil es dir nicht gefällt wenn du den Klassennamen der abgeleiteten Klasse an genau einer Stelle wiederholen musst? WTF?

    happystudent schrieb:

    hustbaer schrieb:

    Uswusf.
    Das sind echt so triviale Dinge wo's da gleich hakt, da müsstest du selbst draufkommen.
    Und dann macht es nicht viel Spass dir das zu erklären, weil man halt den Eindruck hat dass du da ne sehr verschwommene Idee hattest die du süss findest, und nun meinst du es müsste gehen. Aber ohne dass du dir mal ein paar Minuten Zeit genommen hast die Sache durchzudenken.

    Nein, das ist jetzt eine Unterstellung, ich hab mir sehr wohl Zeit genommen.

    OK, ich hätte "ich" statt "man" schreiben sollen. Und dann ist es gar keine Unterstellung - ICH habe nämlich den Eindruck.

    happystudent schrieb:

    Base::child_type foo; // Compilefehler: child_type nur im Kontext eines childs verfügbar
    auto bar = &Base::create; // Compilefehler: Base hat kein member create, da dieses nur in childs implementiert wird
    

    Wie willst du create dann über einen Basisklassenzeiger aufrufen, wenn Base kein Member create hat?



  • tl;dr:
    Das Problem aus einem Basisklassenzeiger einen Zeiger zu machen, der auf eine entsprechende Derived-Klasse zeigt, ist schon länger bekannt.
    Gegen den naheliegenden Ansatz - den mit der virtual clone Funktion - sprechen die Nachteile, die du genannt hast: es ist quasi immer dasselbe, man muss es in jede Basisklasse reinschreiben und kann es bei tieferen Hierachien evtl. vergessen.
    Eine andere Methode wäre eine clone-Factory. Die speichert intern eine std::(unordered)_map von dem std::type_index eines Typen und eine entsprechende Funktion, die bei Parameter-Übergabe einen Klon zurückgibt. Man muss sie dann nur bei Programmstart initialisieren.
    In etwa so: http://ideone.com/GLH7h4
    Das ist dann aber langsamer als die andere Variante.
    Die beste Lösung ist es imo, klonen zu vermeiden.



  • happystudent schrieb:

    großbuchstaben schrieb:

    meinst du sowas ?

    Nee, dann muss ich ja bei jedem Aufruf von foo immer das template Argument mit angeben.

    mußt du doch gar nicht:

    #include<iostream>
    
    struct Base
    {
      virtual int out(int in) = 0;
      template<typename T> T* create(){
        T* e = new T();
        return e;
      }
    };
    
    struct Derived1 : Base
    {
      int out(int in){ return in; }
    };
    
    struct Derived2 : Base
    {
      int out(int in){ return 2*in; }
    };
    
    template<typename T> void foo(Base &bar, int in)
    {
      Base* copy2 = bar.create<T>();
      std::cout << copy2->out(in) << '\n';
    }
    
    #define Foo(b, i) foo<decltype(b)>(b, i);
    
    int main(void){
      Derived1 b1;
      Derived2 b2;
      Foo(b1, 3);
      Foo(b2, 3);
      return 0;
    }
    


  • happystudent schrieb:

    Lokale Kopie von Base Klasse anlegen?

    abgesehen von den mehr oder weniger pfiffigen vorschlägen:
    dein vorhaben ist doch unfug.



  • hustbaer schrieb:

    Du willst doch aber kein Template. Wir bewegen uns im Kreis.

    Das war natürlich nur ein Beispiel:

    template <typename T> void foo(T const &t_) { std::cout << t_; }
    
    int main()
    {
        foo(1);
        foo("hallo");
        foo(2.5);
    }
    

    ist auch jeweils ein anderer Code (dank templates), trotzdem muss ich den template Parameter nicht explizit angeben.

    hustbaer schrieb:

    Wie willst du create dann über einen Basisklassenzeiger aufrufen, wenn Base kein Member create hat?

    Ganz einfach, über ein optional anzugebendes template argument. Wie gesagt, es geht mir eigentlich nur um die Idee, wie das von der Sprache dann letztendlich umgesetzt wird ist ja wieder was anderes. Mit typdef war vielleicht blöd, aber so in der Richtung, mit einem neuen template-typ:

    struct Base
    {
    	template <childname T> T* clone() { return new T(); } // childname bezeichnet den abgeleiteten Typ
    };
    
    struct Derived : public Base
    {
    
    };
    
    int main()
    {
    	Derived d;
    	Base *b = d.clone(); // Eindeutig, kann aus dem Kontext hergeleitet werden
    	auto bar = &Base::clone<Derived>; // Nicht eindeutig, Kontext muss explizit angegeben werden
    }
    

    Wäre doch viel besser, oder?

    Nathan schrieb:

    Die beste Lösung ist es imo, klonen zu vermeiden.

    Hm, auch nicht ganz befriedigend aber Danke...

    großbuchstaben schrieb:

    mußt du doch gar nicht:

    Hm, werd ich mal versuchen, kommt schon recht nahe ans Optimum denke ich...



  • Das ist doch alles Quatsch. Ich klinke mich aus.



  • volkard schrieb:

    abgesehen von den mehr oder weniger pfiffigen vorschlägen:
    dein vorhaben ist doch unfug.

    Warum ist es Unfug eine lokale Kopie einer Base Klasse anfertigen zu wollen?

    Jockelx schrieb:

    Mach doch sowas (grob, smartPointer & Co fehlen)

    Also ich hab das mal gemacht und hab dazu noch eine Frage. Das war ja der Code mit rohen Pointern:

    struct Base
    {
    	virtual int output(int input) = 0;
    	virtual Base* clone() const = 0;
    };
    
    struct Derived1 : Base
    {
    	Derived1() : prev_input(0) {}
    
    	int prev_input;
    
    	int output(int input) {
    		prev_input += input;
    		return prev_input;
    	}
    
    	Derived1* clone() const
    	{
    		return new Derived1(*this);
    	}
    };
    
    void foo(Base &bar)
    {
    	Base *clone = bar.clone();
    }
    

    Was auch klappt. Jetzt habe ich zu Smart-Pointern gewechselt und habe das hier:

    struct Base
    {
    	virtual int output(int input) = 0;
    	virtual std::shared_ptr<Base> clone() const = 0;
    };
    
    struct Derived1 : Base
    {
    	Derived1() : prev_input(0) {}
    
    	int prev_input;
    
    	int output(int input) {
    		prev_input += input;
    		return prev_input;
    	}
    
    	std::shared_ptr<Base> clone() const // <- Frage
    	{
    		return std::make_shared<Derived1>(*this);
    	}
    };
    
    void foo(Base &bar)
    {
    	auto clone = bar.clone();
    }
    

    Dabei geht das aber nur wenn ich einen std::shared_ptr<Base> zurückgebe und nicht bei einem std::shared_pointer<Derived1> da smart pointer anscheinend keine kovarianten Rückgabewerte unterstützen.

    Allerdings klappt es trotzdem so wie ich will, daher mal die Frage: Bringt das irgendwelche Probleme mit sich, das so zu machen?



  • happystudent schrieb:

    volkard schrieb:

    abgesehen von den mehr oder weniger pfiffigen vorschlägen:
    dein vorhaben ist doch unfug.

    Warum ist es Unfug eine lokale Kopie einer Base Klasse anfertigen zu wollen?

    Um aus einem Hund eine Katze zu machen, indem man den gemeinsamen Säugetier-Kram rauskopiert und ganz vorne Schnurrhaare dranklebt? Das ist so abwegig, daß ich gar nicht verstehe, daß Du so viele Antworten bekommen hast.

    Mach mal ein Beispiel nicht nur mit foo und bar und ganz abstrakten Templates, wo dein Vorhaben sinnvoll ist und nicht geradewegs auf Abwege und umkippende Software führt.

    Ich muss mir jetzt nochmal http://www.c-plusplus.net/forum/75672-full durchlesen, damit Du mich nicht verwirrst.



  • volkard schrieb:

    Um aus einem Hund eine Katze zu machen, indem man den gemeinsamen Säugetier-Kram rauskopiert und ganz vorne Schnurrhaare dranklebt? Das ist so abwegig, daß ich gar nicht verstehe, daß Du so viele Antworten bekommen hast.

    Mach mal ein Beispiel nicht nur mit foo und bar und ganz abstrakten Templates, wo dein Vorhaben sinnvoll ist und nicht geradewegs auf Abwege und umkippende Software führt.

    Ich muss mir jetzt nochmal http://www.c-plusplus.net/forum/75672-full durchlesen, damit Du mich nicht verwirrst.

    Ok, hier ein weniger abstraktes Beispiel:

    struct Bauelement
    {
    	virtual int output(int input) = 0;
    	virtual std::shared_ptr<Bauelement> clone() const = 0;
    };
    
    struct Heizplatte : public Bauelement
    {
    	Heizplatte() : prev_strom(0) {}
    
    	int prev_strom;
    
    	int output(int strom) {
    		prev_strom += strom; // Stark vereinfacht
    		return prev_strom;
    	}
    
    	std::shared_ptr<Bauelement> clone() const
    	{
    		return std::make_shared<Heizplatte>(*this);
    	}
    };
    
    void vergleiche_zwei_bauelemente(Bauelement &b1_, Bauelement &b2_)
    {
    	auto b1 = b1_.clone(), b2 = b2_.clone();
    	// Hier müssen wieder outputs berechnet werden, die Bauelemente werden anhand ihres outputs verglichen
    	// Dank der clone-Funktion bleiben die ursprünglichen Bauelemente unverändert, was auch so sein soll
    	b1.get()->output(1);
    	b2.get()->output(5); // Vergleiche die outputs
    }
    
    int main()
    {
    
    	Heizplatte h1;
    	Heizplatte h2;
    
    	// Stelle Berechnungen an, die den Speicher der Bauelemente "aufladen"
    	h1.output(1);
    	h2.output(3);
    
    	// Vergleiche die Bauelemente; durch den Vergleich sollen diese aber _nicht_ verändert werden
    	vergleiche_zwei_bauelemente(h1, h2);
    }
    

    Deinen verlinkten Artikel habe ich gelesen, aber die dort beschriebene Problematik passt nicht auf mein Problem: Heizplatte ist ein Bauelement und besitzt keines. Es kann zwar auch (beliebig viele) besitzen, von außen betrachtet verhält es sich aber wie jedes andere Bauelement auch.



  • happystudent schrieb:

    volkard schrieb:

    Um aus einem Hund eine Katze zu machen, indem man den gemeinsamen Säugetier-Kram rauskopiert und ganz vorne Schnurrhaare dranklebt? Das ist so abwegig, daß ich gar nicht verstehe, daß Du so viele Antworten bekommen hast.

    Mach mal ein Beispiel nicht nur mit foo und bar und ganz abstrakten Templates, wo dein Vorhaben sinnvoll ist und nicht geradewegs auf Abwege und umkippende Software führt.

    Ich muss mir jetzt nochmal http://www.c-plusplus.net/forum/75672-full durchlesen, damit Du mich nicht verwirrst.

    Ok, hier ein weniger abstraktes Beispiel:

    struct Bauelement
    {
    	virtual int output(int input) = 0;
    	virtual std::shared_ptr<Bauelement> clone() const = 0;
    };
    
    struct Heizplatte : public Bauelement
    {
    	Heizplatte() : prev_strom(0) {}
    
    	int prev_strom;
    
    	int output(int strom) {
    		prev_strom += strom; // Stark vereinfacht
    		return prev_strom;
    	}
    
    	std::shared_ptr<Bauelement> clone() const
    	{
    		return std::make_shared<Heizplatte>(*this);
    	}
    };
    
    void vergleiche_zwei_bauelemente(Bauelement &b1_, Bauelement &b2_)
    {
    	auto b1 = b1_.clone(), b2 = b2_.clone();
    	// Hier müssen wieder outputs berechnet werden, die Bauelemente werden anhand ihres outputs verglichen
    	// Dank der clone-Funktion bleiben die ursprünglichen Bauelemente unverändert, was auch so sein soll
    	b1.get()->output(1);
    	b2.get()->output(5); // Vergleiche die outputs
    }
    
    int main()
    {
    	
    	Heizplatte h1;
    	Heizplatte h2;
    
    	// Stelle Berechnungen an, die den Speicher der Bauelemente "aufladen"
    	h1.output(1);
    	h2.output(3);
    
    	// Vergleiche die Bauelemente; durch den Vergleich sollen diese aber _nicht_ verändert werden
    	vergleiche_zwei_bauelemente(h1, h2);
    }
    

    Deinen verlinkten Artikel habe ich gelesen, aber die dort beschriebene Problematik passt nicht auf mein Problem: Heizplatte ist ein Bauelement und besitzt keines. Es kann zwar auch (beliebig viele) besitzen, von außen betrachtet verhält es sich aber wie jedes andere Bauelement auch.

    Dann stimmt doch das Vergleichen nicht oder fehlt.
    Bauelement kopieren, obwohl sie leer ist? Kopie einer abstrakten Basisklasse?
    Also es geht nur drum, zwei Bauelemente vergleichen zu können, indem Du ihnen einen Eingangssignalverlauf schickst und sie gleich drauf reagieren sollen? Das wäre dann doch die Schnittstelle?
    Mir scheint, Du trennt nicht zwischen Bauelemetschablone(Klasse) und Bauelement(Instanz). "aufladen" wäre dann Instanziieren. Zwei Objekte sind gleich, wenn sie von der selben Klasse sind und ihre Attribute gleich sind, reicht das nicht? An den Speicher des Verzögerers, hier nur "int prev_strom;" willste ran? Ist aber nicht "lokale Kopie von Base Klasse anlegen".
    Sollte das Bauelement Bauelementspeicher haben, den man einfrieren und rumvertauschen kann?



  • volkard schrieb:

    Dann stimmt doch das Vergleichen nicht oder fehlt.

    Ja, das ist so nicht vollständig, sollte nur der Verdeutlichung dienen.

    volkard schrieb:

    Bauelement kopieren, obwohl sie leer ist? Kopie einer abstrakten Basisklasse?

    Wieso leer? Die sind nicht leer, bevor ich die vergleichs-Funktion aufrufe habe ich doch bereits geheizt (mittels der output Funktion).

    volkard schrieb:

    Also es geht nur drum, zwei Bauelemente vergleichen zu können, indem Du ihnen einen Eingangssignalverlauf schickst und sie gleich drauf reagieren sollen? Das wäre dann doch die Schnittstelle?
    Mir scheint, Du trennt nicht zwischen Bauelemetschablone(Klasse) und Bauelement(Instanz). "aufladen" wäre dann Instanziieren.

    Nein, aufgeladen bzw. entladen werden die Bauelemente durch Benutzung. Es geht letztendlich um die Vorhersage von Lebensdauer der Bauelemente, daher sieht das Ganze ungefähr so aus:

    while (true) {
    	h1.output(1);
    	h2.output(3);
    
    	// Nach dem Vergleich sollen die Bauelemente im gleichen Zustand sein wie davor
    	vergleiche_zwei_bauelemente(h1, h2);
    
    	if (irgendeine_bedingung)
    		break;
    }
    

    volkard schrieb:

    Zwei Objekte sind gleich, wenn sie von der selben Klasse sind und ihre Attribute gleich sind, reicht das nicht?

    Es geht nicht darum ob zwei Bauelemente gleich sind, sondern ob ihr Verhalten gleich ist. Zwei Heizplatten etwa können völlig unterschiedlich gebaut sein und auch völlig unterschiedliche Attribute haben und trotzdem gleiches Input/Output Verhalten aufweisen (und umgekehrt).

    volkard schrieb:

    An den Speicher des Verzögerers, hier nur "int prev_strom;" willste ran? Ist aber nicht "lokale Kopie von Base Klasse anlegen".
    Sollte das Bauelement Bauelementspeicher haben, den man einfrieren und rumvertauschen kann?

    Ne, an den will ich eben nicht ran. Ich hab irgendein Bauelement, von dem ich nur weiß dass ich ihm einen Strom geben kann und es mir einen Output zurückgibt. Für dieses Bauelement will ich jetzt berechnungen anstellen die seinen internen State nur lokal verändern, nämlich nur für diese lokalen Berechnungen (hier in vergleiche_zwei_bauelemente). Danach will ich weitere Berechnungen mit dem gleichen Bauelement und dessen ursprünglichem Status anstellen können.


Anmelden zum Antworten