Hallo,
ich lese Informationen aus einer Datei aus und lege mir daraus eine
Liste an Datensätzen an.
Momentan gibt es nur eine Klasse dafür, auch wenn die Datensätze
unterschiedlicher Natur sein können.
Um das Beispiel simpel zu halten, nenne ich die Klasse mal "Besitztum".
1
std::vector<Besitztum>meinBesitz;
Ich will mir nun von den eingetragenen Objekten verschiedene Infos
holen. Da es sich aber um recht unterschiedliche Dinge handeln könnte
sind unterschiedliche Infos interessant.
Es könnte zum Beispiel ein Haus oder eine Wohnung eingetragen sein.
Über ein Interface könnte ich mir die Wohnfläche holen:
1
intGetFläche(void);
Das mag hier SInn ergeben, aber bei einem Auto eher nicht. Ein Auto hat
keine "Wohnfläche". Das Interface würde 0 zurückgeben. Was ja durchaus
stimmt.
Allerdings liegt der Gedanke nahe, Subklassen zu erstellen die von
"Besitztum" erben (Immobilie). Beim lesen der Datensätze und Erstellen
der Liste könnte über ein Factory-Konstrukt entschieden werden, welcher
konkrete Typ nun angelegt werden muss.
Jetzt muss also die Wohnfläche-Information wirklich nur für eine
Immobilie gespeichert werden, und auch das zugehörige Interface ist nur
in der Immobilien-Klasse vorhanden.
"Außen" gibt es aber nur eine Liste "Besitztümer". Bevor der Benutzer
also GetFläche() aufruft muss er erstmal den Typen des Objektes
bestimmen.
Pseudocode:
1
if(typ==IMMOBILIE)
2
fl=GetFläche();
Welcher Ansatz hat sich denn bei solchen Fällen eher bewährt? Soll ich
allen Objekten das Selbe Interface spendieren? Und dann, bei
NichtvorhandenSein einer Information einfach 0 zurückgeben? Oder doch
eine Klassenhierachie aufbauen, und dann beim Zugriff eine
Fallunterscheidung einbauen?
Viele Grüße
Das heißt im Folgeschluss, der Anwender muss eine Fallunterscheidung
machen, richtig?
Um bei meinem Beispiel zu bleiben: nicht jede Kindklasse von "Besitztum"
hätte das Interface "GetFläche()". Ergo darf diese Funktion auch nicht
blind für jedes "Besitztum"-Objekt aufgerufen werden.
Mir kommt es komisch vor, dass sich der Anwender da drum kümmern muss.
Das Problem ist herrlich nichttrivial, wenn man es im Detail betrachtet
- und ich habe schon so manche interessante Diskussion mit anderen
Architekten dazu geführt. :) Eine allgemeingültige Lösung gibt es meiner
Meinung nach nicht, aber für viele Fälle der von Dir beschriebenen Art
würde man über das Besucher-Muster nachdenken:
http://de.wikipedia.org/wiki/Besucher_%28Entwurfsmuster%29
Masl schrieb:> Mir kommt es komisch vor, dass sich der Anwender da drum kümmern muss.
Das lässt sich nicht vermeiden. Wann immer man mit dynamischen Daten
arbeitet, muss man zu solchen Konstrukten greifen...
Masl schrieb:> Mir kommt es komisch vor, dass sich der Anwender da drum kümmern muss.
Du hast eine Kiste voll unterschiedlichster Spielsachen. Und dann sollst
du mit den Sachen was machen. Jetzt überlege mal, wie schlimm es für
dich, wenn eine Aktion nicht ausführbar ist. "Reinigen" und "Wegwerfen"
werden wohl noch universiell möglich sein. Aber z.B.
"BatterienEinlegen"? Wenn es für dich egal ist, dass beim Versuch, den
Kasperl mit Batterien zu versehen, nichts geschieht, dann kannst du die
Aktion "BatterienEinlegen" in die Basisklasse "Spielzeug" tun (wobei
sich dort die Batterien in Luft auflösen, aber der Spielzeug sonst
unverändert bleibt). Wenn das aber katastrophal ist, dann musst du dich
als Anwender wohl oder übel dafür interessieren, welche Art von
Spielzeug es ist, und "BatterienEinlegen" hat in der Basisklasse nichts
verloren.
Dann brauchst du aber einen Mechanismus, um die Klasse herauszufinden.
Eine Möglichkeit ist, eine Klasse BatteriebetriebenesSpielzeug
einzuführen, und dann mit einem dynamic_cast zu arbeiten.
Übrigens, du kannst einen std::vector<Besitztum> nicht mit Objekten
befüllen, die von Besitztum abgeleitet sind.
bal schrieb:> tictactoe schrieb:>> Übrigens, du kannst einen std::vector<Besitztum> nicht mit Objekten>> befüllen, die von Besitztum abgeleitet sind.>> doch
Nein, da der Vektor konkrete Objekte verwaltet und dazu ihre Größe
kennen muss - auch für die leeren Stellen im Vektor - die Größe der von
Besitztum abgeleiteten Klassen ist dem Vector nicht bekannt - kann also
nicht gehen. Dafür braucht's polymorphie und das geht für gewöhnlich nur
mit Zeigern.
also z.B.
std::vector<Besitztum*>
oder besser
std::vector< smart_ptr<Besitztum> >
wobei smart_ptr hier ein Smart-Ptr deiner Wahl ist. Boost hat für sowas
auch noch den Pointer-Container.
Siehe auch:
http://www.cplusplus.com/forum/general/17754/
Zitat (Ersetze Automobile durch Besitztum)
The reason for storing base class pointers is because the STL container
will make its own copy of the object being inserted. If the container is
of the base class, Automobile, it will only copy the base class portion
of the object. If the container is of base class pointers, Automobile*,
a pointer to the complete object will be stored.
bal schrieb:> tictactoe schrieb:>> Übrigens, du kannst einen std::vector<Besitztum> nicht mit Objekten>> befüllen, die von Besitztum abgeleitet sind.>> doch
Nein.
Wie kommst du eigentlich auf die Idee, hier zu widersprechen?
Von selbst kann man ja diesem Irrtum schon mal unterliegen, das passiert
jedem mal. Aber wenn schon jemand sagt, daß es nicht geht, dann müßte
man doch eignetlich mal kurz nachdenken, bevor man postet, oder nicht?
Gustav schrieb:> bal schrieb:>> tictactoe schrieb:>>> Übrigens, du kannst einen std::vector<Besitztum> nicht mit Objekten>>> befüllen, die von Besitztum abgeleitet sind.>>>> doch>> Nein.
Ohne jetzt Öl ins Feuer giessen zu wollen:
Können tut er schon. In dem Sinne, dass in der Compiler nicht daran
hindern wird.
Nur wird eben nicht das passieren, worauf er abzielte. In einem
std::vector<Besitztum> werden Besitztum Objekte gespeichert. Objekte,
die von einer abgeleiteten Klasse stammen, werden auf das reduziert, was
mit einem Besitztum Objekt verbunden ist. Der Fachbegriff dafür ist
'stripping' und darüber sollte man Bescheid wissen, weil stripping auch
bei anderen Gelegenheiten auftreten kann, wenn man unachtsam ist bzw.
manchmal ist das auch ganz gezielt so gewollt.
Falls jemand Tante Google benutzen will:
m.E. ist der Fachbegriff dafür "Slicing" in C++ und nicht Stripping.
Und ja, stimmt - man kann. Nein, man tut es nicht. :)
Karl Heinz schrieb:> Ohne jetzt Öl ins Feuer giessen zu wollen:> Können tut er schon.
Eigentlich nicht. Aber es wird dann irgendwann eine philosophische
Frage, ob man davon sprechen kann, Objekte der abgeleiteten Klasse in
den Container gefüllt zu haben, wenn man zwar auf den ersten Blick die
entsprechende Operation ausgeführt hat, aber in Wirklichkeit nur der
Basisklassen-Teil ankommt.
Ronny Spiegel schrieb:> m.E. ist der Fachbegriff dafür "Slicing" in C++ und nicht Stripping.
So kenne ich das auch, und ebenso die Wikipädie:
https://en.wikipedia.org/wiki/Object_slicing
Ronny Spiegel schrieb:> Falls jemand Tante Google benutzen will:>> m.E. ist der Fachbegriff dafür "Slicing" in C++ und nicht Stripping.
Ich werd alt. Danke für die Korrektur.
Slicing ist natürlich richtig.