Anzeige:
Seite 2 von 2 ErsteErste 12
Ergebnis 16 bis 25 von 25

Thema: Probleme mit abgeleiteten Klassen

  1. #16
    Administrator Avatar von anda_skoa
    Registriert seit
    17.11.2001
    Ort
    Graz, Österreich
    Beiträge
    5.477
    Zitat Zitat von dml Beitrag anzeigen
    Mein Problem war, das ich ein Objekt nach Bedarf umwandeln wollte:
    Code:
     mask     = (RecipeMask) mask; 
    ...
     mask     = (BookMask) mask;
    Doch das ist schon logisch nicht möglich.
    Das ist schon möglich, nur muss man sich das Resultat des Casts merken, d.h. die Typinformation nicht gleich wieder verwerfen so wie da in diesem Beispiel der Fall ist.

    Was du hier hast sind quasi zwei Teile:
    1) der Cast, z.b. (BookMask*)mask, besser static_cast<BookMask*>(mask);

    2) eine Zuweisung: mask = resultat_des_cast

    Nach (1) "sieht" der Compiler den gecasteten Typ, also BookMask*.
    "mask" ist aber vom Typ Mask*, d.h. nach (2) "sieht" der Compiler wieder nur die Typinformation Mask*


    Zitat Zitat von dml Beitrag anzeigen
    Da bei einer Typumwandlung der neue Typ eh neu aufgebaut werden muss. Also einen Konstruktor durchläuft.
    Nein. Der Pointercast ändert nichts am Objekt selbst, er verändert lediglich wie der Compiler das Objekt hinter dem Pointer betrachtet.

    Code:
    BookMask *bookMask = new BookMask;
    
    Mask *mask = bookMask; 
    
    BookMask *bookMask2 = bookMask;
    BookMask *bookMask3 = static_cast<BookMask*>(mask);
    Alle view Pointer zeigen auf das selbe Objekt.

    Zitat Zitat von dml Beitrag anzeigen
    Hinzu kam, das das Signal erst nach Abarbeitung des Slots beendet wird und ich genau in dem Slot das Objekt löschen und neu initialisieren wollte.
    Das sollte schon klappen. Der Code, der das Signal aussendet arbeitet davor und danach nur mit dem Basistyp, oder?

    Ciao,
    _
    Qt/KDE Entwickler
    Debian Benutzer

  2. #17
    Registrierter Benutzer
    Registriert seit
    02.08.2008
    Beiträge
    177
    Code:
    BookMask *bookMask = new BookMask;
    
    Mask *mask = bookMask; 
    
    BookMask *bookMask2 = bookMask;
    BookMask *bookMask3 = static_cast<BookMask*>(mask);
    Es sieht ganz stark danach aus, das dessen Typ dann Mask ist. Wenn das so ist, ist meine Grundannahme von Typumwandlung falsch.
    Code:
    class MainWidget : public QWidget {
     	....
    	Mask		*mask;
    	Document	*document;
    }
    Werden immer Mask und Document sein. ich kann zwar Objekte abgeleiteter Klasse hinnein casten, doch am Ende werden es immer die Basisklassen Mask und Document bleiben. Und da liegt mein Fehler, ich brauchte es genau anders herrum.
    Mit new kann ich Ihnen einen neuen Typ zuweisen, doch stecke ich dann in dem Problem des Signals dessen Herrausgeber ich ja noch löschen muss. welches ich mit deleteLater() versuchen werde zu lösen. Und nach meiner ersten Testung auch funktioniert hat.
    Code:
    void MainWidget::setDocumentClass( ){
    qDebug()<<"void MainWidget::setDocumentClass()";
    
    	qDebug()<<"doctype=" << doctype;
    	
    	qDebug()<<"document="<<document<<"|\tmask="<<mask;
    	switch(doctype){
    /*	  
    		case ANIMAL:
    		      qDebug()<<"\ndocument = new AnimalDocument;";
    			      document = (AnimalDocument) new AnimalDocument;
    		      if(!document->start( absolute_document_path ))
    				doctype = EMPTY;
    		      else
    				mask->fill(document->getFileContent(), document->getFileInfo());
    		      break;
    */
    		case BOOK:
    		      qDebug()<<"\nBookDocument";
    		      
    		      disconnect(mask, 0, 0, 0);
    		      mask->deleteLater();
    		      mask = new BookMask;
    		      
    		      disconnect(document, 0, 0, 0);
    		      document->deleteLater();
    		      document = new BookDocument;
    
    		      break;
    		      
    		case RECIPE:
    		      qDebug()<<"\n\n\nRecipeDocument";
    
    		      disconnect(mask, 0, 0, 0);
    		      mask->deleteLater();
    		      mask = new RecipeMask;
    		      
    		      disconnect(document, 0, 0, 0);
    		      document->deleteLater();
    		      document = new RecipeDocument;
    
    		      break;
    		      
    		default:
    		      qDebug()<<"\n\nNo DocType found!";
    		      qDebug()<<"Document";
    		      mask     = mask;
    		      document = document;
    		      
    		      break;
    	}
    		
    	connect( mask, SIGNAL(SigDocTypeChanged( int )),
    		this, SLOT(DocumentTypeChanged( int )) );
    	
    	connect( document, SIGNAL(saved( QString )),
    		this, SLOT(loadDocument( QString )) );
    
    	connect( mask, SIGNAL(SigSave()),
    		this, SLOT(saveDocument()));
    	
    	qDebug()<<"document="<<document<<"|\tmask="<<mask;
    }
    Geändert von dml (26-01-2014 um 18:19 Uhr) Grund: Ein bisschen Angeben

  3. #18
    Administrator Avatar von anda_skoa
    Registriert seit
    17.11.2001
    Ort
    Graz, Österreich
    Beiträge
    5.477
    Zitat Zitat von dml Beitrag anzeigen
    Es sieht ganz stark danach aus, das dessen Typ dann Mask ist.
    Der Typ der Variable "mask" ist Mask* (also Pointer auf Mask). Das Objekt hinter dem Pointer ist nach wie vor vom Typ BookMask.

    Zitat Zitat von dml Beitrag anzeigen
    Werden immer Mask und Document sein. ich kann zwar Objekte abgeleiteter Klasse hinnein casten, doch am Ende werden es immer die Basisklassen Mask und Document bleiben. Und da liegt mein Fehler, ich brauchte es genau anders herrum.
    Ansich wird Polymorphie so angewandt, dass man möglichst wenig oft wissen muss, mit welchen Subtyp man gerade zu tun hat.

    Wie sieht denn ein Beispiel für eine Fallunterscheidung bei dir aus, abgesehen von der Erzeugung der Instanzen?

    Zitat Zitat von dml Beitrag anzeigen
    Mit new kann ich Ihnen einen neuen Typ zuweisen, doch stecke ich dann in dem Problem des Signals dessen Herrausgeber ich ja noch löschen muss. welches ich mit deleteLater() versuchen werde zu lösen. Und nach meiner ersten Testung auch funktioniert hat.
    Ja, deleteLater() ist dafür bestens geeignet. Das echte Löschen des Objekts wird über die Eventloop "verzögert" ausgeführt.


    Übrigens, das disconnect() kannst du auch kürzer so schreiben
    Code:
    mask->disconnect();
    Ciao,
    _
    Qt/KDE Entwickler
    Debian Benutzer

  4. #19
    Registrierter Benutzer
    Registriert seit
    02.08.2008
    Beiträge
    177
    Das Objekt hinter dem Pointer ist nach wie vor vom Typ BookMask.
    Ja aber ich hatte dabei das gleiche Objekt verwendet, weshalb es nur Mask bzw. Document sein konnte.
    Ein Beispiel in meiner Applikation ist ganz einfach:

    Code:
    void MainWidget::setDocumentClass( ){
    qDebug()<<"void MainWidget::setDocumentClass()";
    
    	qDebug()<<"doctype=" << doctype;
    	
    	qDebug()<<"document="<<document<<"|\tmask="<<mask;
    	switch(doctype){
    /*	  
    		case ANIMAL:
    		      qDebug()<<"\ndocument = new AnimalDocument;";
    			      document = (AnimalDocument) new AnimalDocument;
    		      if(!document->start( absolute_document_path ))
    				doctype = EMPTY;
    		      else
    				mask->fill(document->getFileContent(), document->getFileInfo());
    		      break;
    */
    		case BOOK:
    		      qDebug()<<"\nBookDocument";
    		      mask     = (BookMask*) mask;
    		      document = (BookDocument*) document;
    
    		      break;
    		      
    		case RECIPE:
    		      qDebug()<<"\nRecipeDocument";
    		      mask     = (RecipeMask*) mask;
    		      document = (RecipeDocument*) document;
    		      
    		      break;
    		      
    		default:
    		      qDebug()<<"\n\nNo DocType found!";
    		      qDebug()<<"Document";
    		      mask     = mask;
    		      document = document;
    		      
    		      break;
    	}
    		
    	
    	qDebug()<<"document="<<document<<"|\tmask="<<mask;
    }
    anwenden, und man erkennt an qDebug() das der Typ gleich geblieben ist. Wenn man in der Applikation auf Dokument->Bearbeiten drückt, müssten auch nur die Elemente der Basisklasse Mask und nicht der abgeleiteten Klasse BookMask, RecipeMask angezeigit werden.

    Ein Risiko bleibt aber noch, da die Verbindung während des disconnect() Befehles noch aktiv ist. Ich glaube das es funktioniert Da es sonst noch die Methode diconnectLater() geben würde.
    Geändert von dml (26-01-2014 um 22:03 Uhr)

  5. #20
    Administrator Avatar von anda_skoa
    Registriert seit
    17.11.2001
    Ort
    Graz, Österreich
    Beiträge
    5.477
    Das ist in dem Fall kein Beispiel das ich meinte, hier wird nichts mit den Objekten gemacht.

    Ich meinte ein Beispiel, wo du je nach Typ das Mask oder Document Objekt anders behandelst.
    D.h. entweder anderen Code durchläufst oder Methoden der Objekte aufrufst, die nicht in der Basisklasse deklariert sind.

    Ciao,
    _
    Qt/KDE Entwickler
    Debian Benutzer

  6. #21
    Registrierter Benutzer
    Registriert seit
    02.08.2008
    Beiträge
    177
    Code:
    void MainWidget::loadDocument( QString absolute_document_path ){
    
            ...
    	setDocumentClass();
            ...
    		mask->fill(document->getFileContent(), document->getFileInfo());
            ...
    }
    
    void RecipeMask::fill( QString file_content, QFileInfo fileinfo ){
            ...
    	//////////////////////
    	// get preparation  //
    	//////////////////////
            ...
    	        this->preparation_te->setText( exp.cap(1) );
            ...
    }
    Preparation ist nur in der abgeleiteten Klasse RecipeMask enthalten. Vom Methodenaufruf unterscheidet sich der Code nicht.

    Natürlich kann man jetzt sagen, das man eine SuperMaske hätte erstellen können und je nach Anwendungsfall die Members darstellen und auswerten.
    Code:
    class BookMask: public Mask{
            ...  
    	QTextEdit              *description_te;
            ...
    }
    
    class RecipeMask: public Mask{
            ...  
    	QTextEdit              *description_te;
            ...
    }
    Doch finde ich es so viel übersichtlicher.
    Geändert von dml (27-01-2014 um 11:16 Uhr)

  7. #22
    Administrator Avatar von anda_skoa
    Registriert seit
    17.11.2001
    Ort
    Graz, Österreich
    Beiträge
    5.477
    Aber die Methode fill() ist doch bereits in Mask deklariert (virtual) und wird von den Subklassen nur anders implementiert.

    D.h. der Aufruf in MainWidget::loadDocument() wird bereits durch den Polymorphismus auf den jeweiligen Code umgeleitet.

    Code:
    Mask *mask = new RecipeMask;
    
    mask->fill(...); // Aufruf von RecipeMask::fill();
    
    mask = new BookMask;
    
    mask->fill(...); // Aufruf von BookMask::fill();
    Für den Aufruf von fill() ist es bedeutungslos auf welche Subklasse mask gerade veweißt, zur Laufzeit wird die Implementierung der jeweiligen Subklasse ausgeführt.

    Wenn wir diesen Codeschnippsel kurz umschreiben
    Code:
    switch (doctype) {
        case RECIPE:
            mask = new RecipeMask;
            break;
    
        case BOOK:
            mask = new BookMask;
            break;
    }
    
    mask->fill(...);
    dann ist im Falle von doctype == RECIPE in der letzten Zeile ein Aufruf von RecipeMask::fill(), im Falle von doctype == BOOK ein Aufruf von BookMask::fill(), usw.

    Ciao,
    _
    Qt/KDE Entwickler
    Debian Benutzer

  8. #23
    Registrierter Benutzer
    Registriert seit
    02.08.2008
    Beiträge
    177
    In der Hoffnung Dich nicht inzwischen zu Nerven.
    Du hattest Recht der QDebug Befehl gibt Mask( , name="RecipeMask") herraus, ist also weiterhin vom Typ Mask.
    Das bringt mich zu dem Problem:
    Code:
    Document::saveDocument( Mask *mask );
    RecipeDocument::saveDocument( RecipeMask *rmask );
    aufgrund seines Parameters kann ich die Methode nicht als virtual deklarieren und so wird Document::saveDocument ausgeführt. Hast Du dafür vielleicht einen kleinen Tip für mich? Oder sollte ich dann doch besser eine große Maske erstellen die kleine Probleme mehr bereitet?
    Bzw: Wenn ich Maske wieder in Document einfüge muss ich eine Menge leerer virtueller Methoden in der Basisklasse schreiben, damit der Compiler alle Methoden der abgeleiteten Klassen akzeptiert.
    Geändert von dml (27-01-2014 um 17:45 Uhr)

  9. #24
    Administrator Avatar von anda_skoa
    Registriert seit
    17.11.2001
    Ort
    Graz, Österreich
    Beiträge
    5.477
    Zitat Zitat von dml Beitrag anzeigen
    In der Hoffnung Dich nicht inzwischen zu Nerven.
    Nope, find ich cool

    Zitat Zitat von dml Beitrag anzeigen
    Das bringt mich zu dem Problem:
    Code:
    Document::saveDocument( Mask *mask );
    RecipeDocument::saveDocument( RecipeMask *rmask );
    Am ehesten so
    Code:
    class Document
    {
    public:
        virtual void saveDocument( Mask *mask ) = 0;
    };
    Code:
    class RecipeDocument : public Document
    {
    public:
        void saveDocument( Mask *mask ) = 0;
    };
    
    void RecipeDocument::saveDocument( Mask *mask )
    {
        RecipeMask *ourMask = dynamic_cast<RecipeMask*>( mask );
        if ( ourMask == 0 ) {
            // mask not of our type, log error or tell user, etc.
            return;
        }
    
        // use ourMask
    }
    Wenn Mask direkt oder indirekt von QQbject abgeleitet ist, kann man statt dynamic_cast auch qobject_cast benutzen.

    Zitat Zitat von dml Beitrag anzeigen
    Bzw: Wenn ich Maske wieder in Document einfüge muss ich eine Menge leerer virtueller Methoden in der Basisklasse schreiben, damit der Compiler alle Methoden der abgeleiteten Klassen akzeptiert.
    Ich kann mich jetzt nicht mehr so gut an den vollständigen Code erinnern, aber wenn Document die jeweiligen Daten enthält bzw bearbeitet und Mask die entsprechende GUI ist, könnte es eventuell andersrum besser gehen.

    D.h. wenn die jeweilige Mask sich ein entsprechendes Document erzeugt und hält.

    Generell müssen Methoden in der Basisklasse nicht "leer" implementiert werden, wenn sie von allen Subklassen ohnehin verschieden implementiert werden müssen. In diesen Fällen reicht es, die Methoden als "pure virtual" zu deklarieren, also virtual vorne und = 0 hinten.

    Das Ziel dabei ist, dass im Rest des Programms möglichst wenig, idealweise gar nicht, zwischen den Subklassen unterschieden werden muss, sondern das durch den Mechanismus des Methodenüberschreibens intern gehandhabt wird,

    Ciao,
    _
    Qt/KDE Entwickler
    Debian Benutzer

  10. #25
    Registrierter Benutzer
    Registriert seit
    02.08.2008
    Beiträge
    177
    Ich kann nur sagen Perfekt, vielen Dank! Jetzt wo es da steht ist es so logisch, doch bin ich nicht darauf gekommen. Ich hätte leere virutal methode() in die Basisklasse geschrieben, was nicht dem Sinn der Ableitung entstprochen hätte.
    = 0; funktioniert hierbei nicht, denn die Methode der Basisklasse garantiert, das Objekte (Name, Buchtitlel, Kategorien,...) die für das Funktionieren des Documentes unabdingbar sind, enthalten sind.

Lesezeichen

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •