Basisklasse in eine abgeleitete Templateklasse casten
-
Schön nun wird aber entschieden in der Klasse A B oder C. Meine Entscheidung sollte aber in der Klasse erfolgen in der die Methode X implementiert ist.
-
Ich verstehe Dich nicht?
Du hast nun die Möglichkkeit je Typ eine unterschiedliche Behandlung zu ermöglichen, oder zu fragen was für ein Typ es sit (geht auch über eine virtuelle Funktion).
Du kannst also jetzt jederzeit die Infos nutzen, auch in Deiner Funktion X.
Obwohl genau dies mieser Stil ist... Jede Typenerweiterung muß auch genau in dieser Funktion nachgezogen werden... Vergisst Du es muss es zu einer Fehlfunktion kommmen.
-
oder zu fragen was für ein Typ es ist (geht auch über eine virtuelle Funktion)
Meine Fallunterscheidung betrifft eigentlich nur die Klasse B und C. Es gibt keinen Unterschied in den speziellen Klassen. Nun wäre eigentlich der einfache Fall ein if elseif construct. Sprich ich sollte nur wissen ob es sich bei der übergebenen um Klasse B oder C handelt. Das ist ja kein Problem.
Dann will ich Methode X aufrufen die ein Objekt der Klasse B aufnimmt und eine Methode X die ein Objekt der Klasse C aufnimmt. Dazu müss ich das Objekt je nach Unterscheidung Casten in C oder B. In C ist kein Problem. Aber wie schaffe ich es in Klasse B zu casten. Hier liegt ja das Problem. Nicht beim herausbekommen um welche Klasse es sich handelt.
-
Und warum willst du nicht folgendes:
class A { public: virtual void DoSomething()=0; }; class B { public: virtual void DoSomething() { // ... } }; template <typename T> class C { public: virtual void DoSomething() { // ... } };
Da brauchst du keine Fallunterscheidung.
Generell ist es einfach nicht gut den Code für eine solche Unterscheidung in irgendeine Methode reinzumachen. Vererbung löst das meist besser.
Ohne mehr zu wissen was die Klassen machen und was die Klasse mit der Methode X macht lässt sich schwer mehr sagen. Du wirst dann leider immer solche ungenauen Antworten bekommen - die dich wohl nicht weiterbringen.
Du wirst wohl oder über kaum drum herum kommen in der Template Klasse eine Unterscheidung einzuführen um welchen Typ es sich handelt. Diese Unterscheidung kannst du natürlich auch z.B. in eine externe Klassen auslagern.
template <typename T,class Helper> class C { public: virtual void DoSomething() { helper.DoSomething(); } private: Helper helper; }; typedef C<int,IntHelper> CInt; typedef C<int,DoubleHelper> CDouble;
Somit kannst du dir auch ein "ich-muss-alles-wissen" if sparen.
Aber ich denke es ist schwer mehr zu sagen/helfen ohne die Klassen (bzw deren Aufgabe) genau zu kennen.
-
Also.
Die Klasse B ist eine Klasse die einen elementaren Datentyp enthält. Klasse C ist eine Klasse die ein Container darstellt der Objekte von Klasse B aber auch wiederum Objekte von Klasse C aufnimmt. Deshalb beinhaltet der Container Objekte vom Typ der Klasse A.
So nun würde ich gerne eine Methode aufrufen. Dieser übergebe ich ein Objekt also entweder von der Klasse B oder C. Demzufolge A.
Nun würde ich gerne in dieser Methode X den Wert vom Objekt B mit einer gespeicherten Variable vergleichen. Nun könnte ich einfach den Wert von B abfragen. Dazu müsste ich in B casten. Geht aber nicht. Eine virtuelle Funktion geht nicht da Klasse C keinen Wert hat nur einen Container mit mehreren Werten. Für den Klasse C fall müsste ich eine Schleife durchlaufen.
Verständlich?
-
Wenn die Methode x ein Objekt A bekommt, aber gerne Objekte B verarbeiten möchte ist was falsch. Es hört sich alles danach an, als ob Methode X viele Sachen macht die speziell für eine Klasse sind.
Entweder du sagst
1)
Objekte vom Typ A können mit Werten verglichen werden (-> virtuelle Methode)
2)
Nur Objekte vom Typ B können mit Werten verglichen werden (z.B. für Typ C machts keinen Sinn).
Es macht hier wenig Sinn einer einer Methode 'Tier' zu übergeben, wenn sie eine 'Maus' will.Ich sehe den Sinn noch nicht warum du A übergeben musst.
Du hast natürlich die Möglichkeit eine solche Methode zu machen und z.B. für Klassen bei denen ein Vergleich keinen Sinn macht einen Sinnvollen default Wert zurückzugeben.
-
Ich sehe den Sinn noch nicht warum du A übergeben musst.
Es macht Sinn. Klasse B sagen wir mal ist eine Variable. Klasse C ist eine Gruppe von Variablen aber nicht nur von B sondern kann auch wieder von sich selber.
Das ist ja das Compositemuster.
So jetzt übergebe ich der Funktion die Basisklasse.
Bei Klasse B wird ein Wert mit einem alten verglichen. Wenn ungleich dann liefert diese true zurück.
Bei Klasse C werden alle Werte mit einer alten Liste von werten verglichen. Sobald sich ein Wert geändert hat liefert die Methode true zurück.
-
Dann mach dir eine virtuelle Funktion in Klasse A, die den Inhalt eine A (egal ob es nun in Wirklichkeit ein B oder C oder Spaghettimonster ist) z.B. als String zurückgibt. Irgendwie serialisiert eben.
Du kannst leicht einzelne Werte serialisieren, aber auch ganze Bäume.
Dann musst du in Funktion X nurmehr sagen "if (oldValue == a->Serialize())" und alles ist gut.
Alternativ könntest du die Klasse klonbar machen. Du verpasst der Klasse A also eine (virtuelle) Clone() Methode, die einen A* zurückgibt, der auf ein B oder C oder Spaghettimonster zeigt. Einen Klon eben.
Zusätzlich bekommt die Klasse A eine virtuelle Compare() Methode.
Damit kannst du wieder in Funktion X überprüfen, ob der "gespeicherte" Klon mit dem aktuellen "A" übereinstimmt.
-
Alternativ könntest du die Klasse klonbar machen. Du verpasst der Klasse A also eine (virtuelle) Clone() Methode, die einen A* zurückgibt, der auf ein B oder C oder Spaghettimonster zeigt. Einen Klon eben.
Und was gebe ich dann in den Klassen zurück. Return this.
Und dann! Woher weiß der Aufrufer was er zurückbekommt. Dann muss ich ja auch wieder casten.void ClassX::MethodX(CClassA *classA) { if(classA->GetType() == TYPE_B) { CClassB *b = classA->Clone(); this->MethodX(b); } else if(classA->GetType() == TYPE_C) { CClassC *c = classA->Clone(); this->MethdoX(c); } }
Jetzt bekomme ich ja wieder den Fehler bei CClassB: Für die Verwendung der template-Klasse ist eine template-Argumentliste erforderlich.
-
Denn so wie du ja schreibst:
die einen A* zurückgibt,
Dann habe ich ja wieder nur ein A*. Denn habe ich ja aber schon. Ich sollte ja einen B* oder C* haben.
-
Damit kannst du wieder in Funktion X überprüfen, ob der "gespeicherte" Klon mit dem aktuellen "A" übereinstimmt.
Und es ist nicht erlaubt einen Klon von A herzustellen. Deshalb wird nur der Wert von A gespeichert. Und nun sollte ich Klasse B mit einem Wert vergleichen und Klasse C mit einer Liste.
Also so
classB->GetValue == m_value;
und nicht
classB->GetValue == m_classb->GetValue;
-
Das tolle an dem Vererbungskram ist ja das es dir egal ist, ob es ein B oder C oder D oder... ist.
Die Klasse A hat eine virtuelle Methode Equal die einen Wert entgegen nimmt und dir sagt, ob das Objekt mit dem Wert übereinstimmt.Ich würde das so lösen - ist je nach Fall vielleich oversized oder wegen der Schnittstellen Anforderung (siehe class Wert, protected) nicht geeignet:
// ich würde den Wert in eine eigene Klasse packen und kein template aus B machen // Grund: es soll ja dynamisch zur Laufzeit sein // ist aber natürlich Geschmackssache // // kein template; Wert kann aber int,double,char,oder sonst was sein // diese Lösung hat den Nachteil, dass Wert für alle möglichen Werte eine Schnittstelle hat (protected) // ? evtl reicht es protected Equal-Methoden für die einfachen Datentypen zu haben ? class Wert { public: virtual bool Equal(const Wert& w)=0; protected; virtual bool Equal(int i) {return false;} virtual bool Equal(double d) {return false;} virtual bool Equal(char c) {return false;} }; class WertInt : public Wert // genauso für double,char, etc { public: virtual bool Equal(const Wert& w) { return w.Equal(myIntValue); } protected; virtual bool Equal(int i) {return myIntValue==i;} virtual bool Equal(double d) {return myIntValue==d;} virtual bool Equal(char c) {return myIntValue==c;} private: int myIntValue; }; class A { public: virtual bool Equal(const Wert& w)=0; }; class B : public A { public: virtual bool Equal(const Wert& w)=0 { return wert.Equal(w); } private: Wert wert; }; class C : public A { public: virtual bool Equal(const Wert& w)=0 { für alle werte bool ret = wert.Compare(w) Entscheidung auf Basis von ret } private: std::list<Wert> werte; };
-
Hei klar. Danke euch allen für eure Bemühungen und dass ihr so viel Geduld mit mir hattet.
Nicht die Klasse X entscheidet ob richtig oder falsch sondern Klass A B C entscheidet ob richtig oder falsch!!
DANKE!!!!
-
Hei. Zu früh gefreut. Denn jetzt habe ich das Problem anders rum. Da meine Klasse "Wert" genau den gleiche Aufbau hat wie die Klasse A B C. Aber ich habe noch ne andere Idee. Mal sehen.