Forum: PC-Programmierung C++ Vererbung oder nicht?


von Masl (Gast)


Lesenswert?

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
 int GetFlä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

von René K. (cyprius)


Lesenswert?

Ein Interface ist da fehl am Platz. Nutze eine Basisklasse und vererbe 
entsprechend weiter.

von Masl (Gast)


Lesenswert?

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.

von Gregor O. (zappes)


Lesenswert?

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

von Borislav B. (boris_b)


Lesenswert?

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...

von tictactoe (Gast)


Lesenswert?

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.

von bal (Gast)


Lesenswert?

tictactoe schrieb:
> Übrigens, du kannst einen std::vector<Besitztum> nicht mit Objekten
> befüllen, die von Besitztum abgeleitet sind.

doch

von Ronny S. (duselbaer)


Lesenswert?

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.

von Ronny S. (duselbaer)


Lesenswert?

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.

von Gustav (Gast)


Lesenswert?

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?

von Karl H. (kbuchegg)


Lesenswert?

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.

: Bearbeitet durch User
von Ronny S. (duselbaer)


Lesenswert?

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. :)

von Rolf M. (rmagnus)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

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.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.