Polymorphes clone ist einfach, wie aber (polymorph) assignen?
Info:
-Das ist ein stark simplifiziertes Beispiel - minimal Konstruktor damit
mal von was von "Aussen" reinkommt (normalerweise viel komplexer)
-Ich kann das nicht statisch realisieren weil ich Basis-Klassen-Zeiger
von einer Factory bekommen
-die behavior-Methode ist einfach nur ein Aktion die ich per
Basis-Klassen Zeiger aufrufen kann (normalerweise mehr davon und
komplexer)
-Ich clone polymorph mit einem Basis-Klassen Zeiger
-Original und Clone sind vom selben Typ - aber eben nur als
Basis-Klassen Zeiger greifbar
-In meinem Echt-Bespiel sind es noch schlimmere Komposits auf vielen
verscheidenen Basis-Klassen Zeigern (Ideen von hier sind aber wohl
anwendbar)
-Für die Hetzjäger unter euch - Nein!, es ist keine Hausaufgabe :)
Ziel: ich möchte auf den polymorphen clone verändern und wieder auf das
original zurückschreiben
Ideen:
-dynamic-cast geht nicht weil ich nur die Basis-Zeiger habe
-Ich könnte mit dem Visitor Pattern arbeiten - aber ist das
zielführend/schön
Hat jemand noch ein paar nette (typesafe, ohne wildes ge-caste usw.)
Ideen?
1
#include<iostream>
2
3
structBase
4
{
5
intcommon=0;
6
7
Base(intcommon):common(common)
8
{
9
}
10
11
// das "operative" Interface meiner Ableitungen
12
virtualvoidbehavior()=0;
13
14
// polymorph clonen
15
virtualBase*clone()=0;
16
17
// nur als Debug-Hilfe
18
virtualvoidinfo()
19
{
20
std::cout<<"Base int common: "<<common<<std::endl;;
Zum Online Kompilieren/Spielen: https://onlinegdb.com/HJkWlge14
Ausgabe:
Mit den Problemen die ich klarerweise habe
CloneAndReassigne
Original:
Base int common: 1
TypeA int value: 2
Clone nach Aenderung:
Base int common: 3
TypeA int value: 4
Original nach Aenderung:
Base int common: 3
TypeA int value: 2 <-- nicht Basis-Klassen-Member wurde nicht veraendert
CloneAndReassigne
Original:
Base int common: 2
TypeB double value: 4
Clone nach Aenderung:
Base int common: 4
TypeB double value: 8
Original nach Aenderung:
Base int common: 4
TypeB double value: 4 <-- nicht Basis-Klassen-Member wurde nicht
veraendert
CppBert schrieb:> Polymorphes clone ist einfach, wie aber (polymorph) assignen?>> Hat jemand noch ein paar nette (typesafe, ohne wildes ge-caste usw.)> Ideen?
Verfrachte die polymorphen Typen einfach in Implementierungsklassen und
lasse die Klassen, die das Interface zum Nutzer darstellen einfach nur
noch aus einem Zeig auf die Basis der Implementierungsklassen zeigen.
Wenn Du std::shared_ptr<> verwendest, bekommst Du die Zuweisungen frei
Haus geliefert.
Du must nur darauf achten, dass Du evtl. deep copies machen musst, wenn
Du ein Objekt änderst. Beispiel dafür findest Du z.B hier:
:https://github.com/TorstenRobitzki/Sioux/blob/master/source/json/json.h
mfg Torsten
Torsten R. schrieb:> Verfrachte die polymorphen Typen einfach in Implementierungsklassen und> lasse die Klassen, die das Interface zum Nutzer darstellen einfach nur> noch aus einem Zeig auf die Basis der Implementierungsklassen zeigen.> Wenn Du std::shared_ptr<> verwendest, bekommst Du die Zuweisungen frei> Haus geliefert.
Hört sich gut an - leider kann ich deinen Ausführungen nicht folgen,
auch dein Beispiel bringt mich gerade nicht weiter
Kannst du es bitte (noch) ein bisschen konkreter beschreiben?
Tom schrieb:> Die Basis-Klasse als Wert zu behandeln wie hier*original = *clone; führt> zu Slicing: https://en.wikipedia.org/wiki/Object_slicing> Minimalinvasive Idee: https://onlinegdb.com/Sy7XuZeJE
Zuerst dachte ich - Wow super Idee, aber es gibt leider andere
Komponenten die auf das Original per Pointer verweisen
...und vielleicht muss ich in Zukunft auch nur die durch behavior()
veränderten Teile ersetzen - also wäre ein gezieltes Assign wohl doch
irgendwie besser, trotzdem super Idee
std::cout << "TypeB double value: " << value << std::endl;;
94
}
95
96
double value;
97
};
98
99
};
100
101
void CloneAndReassigne(Base& original)
102
{
103
std::cout << "CloneAndReassigne" << std::endl;
104
Base clone = original;
105
std::cout << "Original:" << std::endl;
106
original.info();
107
clone.behavior();
108
std::cout << "Clone nach Aenderung:" << std::endl;
109
clone.info();
110
original = clone;
111
std::cout << "Original nach Aenderung:" << std::endl;
112
original.info();
113
}
114
115
int main()
116
{
117
TypeA a(1, 2);
118
TypeB b(2, 4);
119
120
CloneAndReassigne(a);
121
CloneAndReassigne(b);
122
}
Das polymorphe Verhalten bekommst Du in den von Base::Impl abgeleiteten
Klasse. Die von Base abgeleiteten Klassen fungieren eigentlich nur noch
als factories. Das ganze hat dann value Semantik und polymorphes
Verhalten, ohne dass Zeiger an der Schnittstelle genutzt werden müssen.
@Torsten R.
Auf jeden Fall ein nettes Konzept - aber es hat mir viel zu starke
Auswirkungen auf meine Gesamtarchitektur (Impl, Shared-Pointer, alle
werden zu Value-Types), sonst aber interessant
Ich habe jetzt meine Clone Routine so veraendert das sie nicht einfach
eine Kopie liefert sondern einen FeedbackClone - der eine virtuelle
Feedback() anbietet die man dazu nutzen kann den Clone wieder in das
Original "einzuprägen" - das Konzept ist leider immer noch nicht
wirklich sauber von meinen "Arbeitsklassen" getrennt aber eine
ausreichend kleine konzeptionell sehr lokale Lösung
Danke für eure Ideen - wie immer sehr Informativ hier
CppBert schrieb:> -dynamic-cast geht nicht weil ich nur die Basis-Zeiger habeCppBert schrieb:> *original = *clone;
Man könnte das schon mit einem virtual copy-assignment machen:
Alternativen:
- Auf die Kovarianz kannst du verzichten, wenn du sie nicht brauchst.
- Wenn du garantieren kannst, dass die Zuweisung zur Laufzeit nur mit
kompatiblen Objekten geschieht, kannst du den dynamic_cast mit
Referenzen machen und auf den else-Zweig verzichten (dann kommt eben
eine Exception, sollten die Typen mal doch nicht passen).
Gegen das Slicing im else-Zweig kann man nichts machen. Soll man
wahrscheinlich aber auch nicht, denn was soll sonst die Bedeutung einer
Zuweisung von X an Y sein, wenn deren Bestandteile nur teilweise
übereinstimmen?