Vererbung etwas genauer :)



  • Hallo,

    ich habe volgendes Bsp. Programm geschrieben:

    //////////////////////////////////////////////////////////////////////
    // Liste.h
    #ifndef __LISTE_H_
    #define __LISTE_H_
    
    class Liste
    {
    	private:
    		Liste * _begin;
    		Liste * _vor;
    		Liste * _next;
    
    	public:
    		Liste();
    		~Liste();
    
    		Liste * InsertNew();
    
    		Liste * GetBegin() const { return _begin; };
    		Liste * GetVor() const { return _vor; };
    		Liste * GetNext() const { return _next; };
    
    		bool * SetVor();
    		bool * SetNext();
    };
    
    #endif // __LISTE_H_
    
    //////////////////////////////////////////////////////////////////////
    // Liste.cpp
    #include <windows.h>
    #include <stdio.h>
    
    #include "Liste.h"
    
    Liste::Liste()
    {
    	MessageBox(NULL, "Liste::Liste()", "Liste::Liste()"
    		, MB_OK | MB_ICONINFORMATION);
    
    	_begin = NULL;
    	_vor = NULL;
    	_next = NULL;
    }
    
    Liste::~Liste()
    {
    	MessageBox(NULL, "Liste::~Liste()", "Liste::~Liste()"
    		, MB_OK | MB_ICONINFORMATION);
    }
    
    Liste * Liste::InsertNew()
    {
    	if(!_begin)
    	{
    		return _begin = this;
    	}
    	else
    	{
    		if((_next = new Liste()) != NULL)
    		{
    			_next->_begin = _begin;
    			_next->_vor = this;
    		}
    	}
    
    	return _next;
    }
    
    //////////////////////////////////////////////////////////////////////
    // Klasse.h
    #ifndef __KLASSE_H_
    #define __KLASSE_H_
    
    class Klasse : public Liste
    {
    	private:
    		int i;
    		char * ch;
    
    	public:
    		Klasse();
    		~Klasse();
    
    		int GetZahl() const { return i; };
    		int SetZahl(int Zahl) { return i = Zahl; };
    
    		void ShowZahl();
    };
    
    #endif // __KLASSE_H_
    
    //////////////////////////////////////////////////////////////////////
    // Klasse.cpp
    #include <windows.h>
    #include <stdio.h>
    
    #include "Liste.h"
    #include "Klasse.h"
    
    Klasse::Klasse()
    {
    	MessageBox(NULL, "Klasse::Klasse()", "Klasse::Klasse()"
    		, MB_OK | MB_ICONINFORMATION);
    
    	ch = new char[10];
    }
    
    Klasse::~Klasse()
    {
    
    	char buf[50];
    	sprintf(buf, "Klasse::~Klasse() -> Del: %d", i);
    
    	MessageBox(NULL, buf, "Klasse::~Klasse()"
    		, MB_OK | MB_ICONINFORMATION);
    
    	if(ch) delete[] ch; // hier Crash beim nächsten Objekt!!!
    
    	if(GetNext()) delete (Klasse *)GetNext();
    }
    
    void Klasse::ShowZahl()
    {
    	char buf[25];
    	sprintf(buf, "Zahl: %d", i);
    
    	MessageBox(NULL, buf, "Klasse::ShowZahl()"
    		, MB_OK | MB_ICONINFORMATION);
    }
    

    Und das Programm dazu:

    //////////////////////////////////////////////////////////////////////
    // main.cpp
    #include <windows.h>
    
    #include "Liste.h"
    #include "Klasse.h"
    
    int WINAPI WinMain(HINSTANCE hInstance
    	, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow)
    {
    	Klasse * kl = new Klasse();
    
    	kl->InsertNew();
    	kl->SetZahl(1);
    	kl->ShowZahl();
    
    	kl = (Klasse *)kl->InsertNew();
    	kl->SetZahl(2);
    	kl->ShowZahl();
    
    	kl = (Klasse *)kl->InsertNew();
    	kl->SetZahl(3);
    	kl->ShowZahl();
    
    	kl = (Klasse *)kl->GetBegin();
    	if(kl) delete kl;
    
    	return 0;
    }
    

    Soweit funktioniert eigentlich, wenn ich dies hier weglasse.

    ch = new char[10];
             // bzw.
    	if(ch) delete[] ch;
    

    Warum? 😕

    Danke schon mal für die Mühe.

    Gruß, Alex



  • Hi,

    Du erzeugst innerhalb der InsertNew Methode ein Objekt vom Typ Liste
    und castest den Pointer auf dieses Object später in einen Typ einer abgeleiteten
    Klasse ("Klasse"). Das ist schlicht falsch, denn das Objekt ist vom Typ "Liste"
    und nicht vom Typ "Klasse". Der Typ "Liste" enthält nicht das Member "ch", weshalb
    der Zugriff an besagter Stelle schief geht.

    Du solltest zum Casten dynamic_cast verwenden. Dann wäre bei deinem illegalen
    Cast schlicht ein null pointer das Ergebnis gewesen.

    Gruß,

    Matthias.



  • Ok!

    Wulk schrieb:

    Du erzeugst innerhalb der InsertNew Methode ein Objekt vom Typ Liste
    und castest den Pointer auf dieses Object später in einen Typ einer abgeleiteten
    Klasse ("Klasse"). Das ist schlicht falsch, denn das Objekt ist vom Typ "Liste"
    und nicht vom Typ "Klasse". Der Typ "Liste" enthält nicht das Member "ch", weshalb
    der Zugriff an besagter Stelle schief geht.

    Das ist mir schon klar 😉

    Wulk schrieb:

    Du solltest zum Casten dynamic_cast verwenden. Dann wäre bei deinem illegalen
    Cast schlicht ein null pointer das Ergebnis gewesen.

    Ich habe meine main-Funktion so geändert:

    int WINAPI WinMain(HINSTANCE hInstance
    	, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow)
    {
    	Klasse * kl = new Klasse();
    
    	kl->InsertNew();
    	kl->SetZahl(1);
    	kl->ShowZahl();
    
    	kl = dynamic_cast<Klasse *>(kl->InsertNew());
    	kl->SetZahl(2);
    	kl->ShowZahl();
    
    	kl = dynamic_cast<Klasse *>(kl->InsertNew());
    	kl->SetZahl(3);
    	kl->ShowZahl();
    
    	kl = dynamic_cast<Klasse *>(kl->GetBegin());
    	if(kl) delete kl;
    
    	return 0;
    }
    

    offensichtlich falsch! Bekomme den Fehler:

    main.cpp(15) : Error: expression must be a pointer or reference to a polymorphic type
    

    Wie mache ich das denn? 😕

    Ich möchte eine Klasse -> hier "Liste" und davon praktisch beliebige Objekte ableiten, so dass diese sich wie eine Liste ansprächen lassen. Geht das so? Oder irre ich mich völlig?

    Danke und Gruß
    Alex



  • Erstmal leitet man von Klassen keine Objekte ab, sondern andere Klassen. Zum zweiten ist deine Klasse Liste nicht polymorph. Mach zumindest mal den Destruktor virtuell.

    Davon abgesehen hör bitte auf mit diesem char-Array und sprintf Blödsinn auf. Da wird einem ja von schlecht. Dafür nimmt man std::string und std::ostringstream.



  • WinieX schrieb:

    Das ist mir schon klar 😉

    Warum machst Du es dann?

    offensichtlich falsch! Bekomme den Fehler:

    main.cpp(15) : Error: expression must be a pointer or reference to a polymorphic type
    

    Wie mache ich das denn? 😕

    Deine Basisklasse ist nicht polymorph. Deshalb enthält ein Objekt von ihr auch keine Informationen über die Klassenhierarchie. Dazu müsstest Du mindestens eine Methode (und den Destruktor) von Liste virtual machen. Aber auch dann compiliert das Programm zwar, wird aber abstürzen weil Du nicht abfragst ob der dynamic_cast einen Nullpointer zum Ergebnis hatte.

    Ich wiederhole nochmal Wulk: Aber Du erzeugst in InsertNew ein Objekt vom Typ Liste, das ist zwar die Basis von "Klasse", aber eben kein "Klasse". Wenn Dir das klar ist, wieso versuchst Du es?



  • Und noch was zum Thema "Vererbung und etwas genauer":

    Das was uns Vererbung (polymorphie) als den Segen schlechthin bringt, ist das Schnittstellenprinzip ^^
    Und das will gut ueberlegt und designt sein. dynamic_cast's sind nen deutliches Zeichen auf schwaechen im design. Manchmal muss man mit schwaechen leben, aber oftmals nicht. Dynamic_cast ist relativ langsam, und drueckt eine (leichte) typunsicherheit aus. Typunsicherheiten versteckt man meist gut ^^

    Vererbung drueckt eine "ist ein" Beziehung aus.

    Deine Liste ist ein Container von Liste Elementen. Die liste haelt member von der eigenen klasse, und nix weiter,ist das richtig ? Keine eigenen Daten ?

    Deine klasse ist eine Liste von Liste ??? richtig ???
    es speichert also Liste elemente ???

    dynamic_cast<Klasse *>(kl->InsertNew());
    

    Waere nicht ein "ist implementiert mit" in dem falle besser ?

    wenn du unbedingt mit polymorphie ueben willst, such dir etwas andere Beispiele. Ansonsten aergerst du dich viel zu schnell mit den Nebeneffekten eines Schlechten Designs rum, als das vererbung spass macht ^^
    Container an sich sind eigentlich kein geeignetes feld fuer vererbung. zumindest ned in c++ ...

    P.S.
    ein " da muss ich aber mehr schreiben" als ausrede zaehlt ned ^^

    Ciao ...



  • Z2 schrieb:

    Erstmal leitet man von Klassen keine Objekte ab, sondern andere Klassen.

    Danke für die Richtigstellung. Werd ich in Zukunft berücksichtigen...

    Z2 schrieb:

    ... Zum zweiten ist deine Klasse Liste nicht polymorph.

    Mal 'ne dumme Frage: Äh, wie mach ich eine Klasse polymorph? 😕

    Z2 schrieb:

    ... Davon abgesehen hör bitte auf mit diesem char-Array und sprintf Blödsinn auf. Da wird einem ja von schlecht. Dafür nimmt man std::string und std::ostringstream.

    Tut mir leid. Hab leider kein STL...

    LordJaxom schrieb:

    WinieX schrieb:

    Das ist mir schon klar 😉

    Warum machst Du es dann?

    offensichtlich falsch! Bekomme den Fehler:

    main.cpp(15) : Error: expression must be a pointer or reference to a polymorphic type
    

    Wie mache ich das denn? 😕

    Deine Basisklasse ist nicht polymorph. Deshalb enthält ein Objekt von ihr auch keine Informationen über die Klassenhierarchie. Dazu müsstest Du mindestens eine Methode (und den Destruktor) von Liste virtual machen. Aber auch dann compiliert das Programm zwar, wird aber abstürzen weil Du nicht abfragst ob der dynamic_cast einen Nullpointer zum Ergebnis hatte.

    Ich wiederhole nochmal Wulk: Aber Du erzeugst in InsertNew ein Objekt vom Typ Liste, das ist zwar die Basis von "Klasse", aber eben kein "Klasse". Wenn Dir das klar ist, wieso versuchst Du es?

    Ich versuche es, weil ich gerade versuche das Prinzip der Vererbung in meine Birne reinzubekommen. Was ist daran falsch? Mein Prinzip dabei ist: Versuch - Irrtum...

    Eigentlich habe ich mir gedacht, dass das sooo einfach ist - mal ne Klasse von einer anderen ableiten und schon habe ich alle Eigenschaften der Basis-Klasse. Soll do so oder so Ähnlich sein? Doch falsch? Was wird dann geerbt?

    Wenn das so alles falsch ist, muß ich mich wohl doch mit Templates beschäftigen? Was meint Ihr?



  • Was ist daran falsch? Mein Prinzip dabei ist: Versuch - Irrtum...

    Es gibt mehrere arten von Fehlern, aber im groben kann man die in 2 gruppen einteilen:

    - man macht ihn, und der compiler / oder spaetestens die runtime (beta tester / quality managment) wirft einen den Dreck um die ohren ^^
    - man macht ihn, und lange zeit geht es gut. Man gewoehnt sich gewisse dinge an ... irgendwann wundert man sich, das alles immer komplizierter wird, die klassen nicht mehr genrisch und kompatibel zu anderen sind, minimalste aenderung eigentlich schon zu nem kompletten redesign fuehren welches nur mit ganz schlechten kruecken zu verhindern ist.
    - Fehler die nie auftauchen, nie einen effekt haben, sind auch keine fehler ^^

    Fehler 1 sind die guten, die sieht man.
    Fehler 2 sind die ganz poesen ^^ die kosten unserer wirtschaft millionen.

    Fehler 2 ist nicht immer nur unwissen, fehler 2 ist oefters eigentlich nur angewohnheit.

    das Prinzip der Vererbung

    ist eben nicht nur das mans kann, sondern eben die nebeneffkte usw beherscht.

    Eigentlich habe ich mir gedacht, dass das sooo einfach ist

    die regeln nach der der compiler vorgeht sind einfach, sehr einfach ^^
    wie man richtig damit umgeht ist das komplizierte

    schon habe ich alle Eigenschaften der Basis-Klasse.

    oftmals liegt das Problem im detail ... dein "Fehler" war eigentlich nur:
    Du wolltest eine Liste von Klassen, hasst aber eine Liste von Liste genommen.

    Klasse * InsertNew(); und Liste * InsertNew(); sind eben nicht das selbe, also keine gemeinsamen eigenschaften. und selbst wenn:
    Code wiederverwenden in dem man ne andere klasse die schon fertig ist, nutzt, ist Domaene der Aggregation ("ist implementiert mit").

    der beruehmte satz eines Autors der versucht bisserl design in die c++ welt zu bringen ist :
    Vererbe niemals um wiederzuverwenden, sondern nur um wiederverwendet zu werden !

    Zu deutsch:
    nicht vererben weil man sich das neuschreiben der Methoden sparen will (soll man auch ned, aber dazu siehe oben) sondern nur wenn sich dein objekt wie ein anderes verhalten soll.

    Schnittstellenprinzip ... allgemeine verhaltensregeln definieren, komplett unterschiedliche objekte die sich nach den regeln verhalten, erzeugen, beliebig austauschen, das ist polymorphie ....

    muß ich mich wohl doch mit Templates beschäftigen

    frueher oder spaeter schon ^^ aber das kannst nach hinten verschieben ... das thema ist noch ... "boeser".

    Mein Prinzip dabei ist: Versuch - Irrtum...

    Ja ist ja auch ok.
    wir helfen Dir ja auch nur besonders die Fehler 2 zu erkennen und deren Signaturen zu deuten ^^

    Ciao ...


Anmelden zum Antworten