Rueckgabe abgeleiteter Klassen
-
Wieder mal stossen meine C++ an Ihre grenzen, deshalb folgendes Problem:
Ich hab ne "abstrakte Klasse" namens DtAbstract. Sie ist zwar nicht wirklich abstrakt deklariert, aber alle Daten-Klassen erben von Ihr.
Jeder dieser Daten-Klassen besizte eine statische Methode in der Art:static std::list<Daten-Klasse> select();Nun hab ich eine Factory Klasse, in der ueber Schluessel gewaehlt werden kann, von welcher Klasse die oben beschriebene Funktion aufgerufen werden soll.
Nun kann ich dafuer aber nicht
std::list<DtAbstract> get_data(int key);verwenden.
Nun, ich muss zugeben, ich komme aus der Java Welt, dort sollte dieses Szenario ohne Probleme funktionieren. Aber wieso kann ich in der Methode keine std::list<Daten-Klasse> zurueck geben?
Wie implementiert man so was in C++?
Hat jemand einen Rat oder nen guten Link anzubieten, wo sowas beschrieben wird? Hab leider selbst nichts gefunden.mfg
-
Ehm davon abgesehen das sich dein Klassendesign grausig anhört, ist es durchaus möglich eine std::list<T> const& zurück zu geben

-
moe szyslak schrieb:
...
Wenn ich dich richtig verstehe, kann das garnicht funktionieren (sowohl unter C++ als auch Java, davon abgesehen). Mag sein das man dich falsch versteht da es leider kein wirklich gutes Deutsch ist.
Statische Methoden benötigen konkrete Typinformationen. Anders sieht es aus wenn du wirklich Polymorphie verwendest.
Davon abgesehen kannst du keine Listen mit den Konkreten Datentyp zurückgeben und dann auf diese als Liste auf den Basistyp zugreifen. Wenn musst du grundsätzlich Listen der Basisklasse zurückgeben (Achtung: dann aber als Zeiger oder mittels Smartpointer, sonst wird alles der Klasse was nicht auch in der Basisklasse steht abgeschnitten) und über ein polymorphes Interface darauf zugreifen.
Beispiel:
static std::list<BasisKlasse*> KonkreteKlasse::select();
static std::list<boost::shared_ptr<BasisKlasse> > KonkreteKlasse::select();In Java geht dies deshalb weil es Listen gibt die Verweise auf den untersten Basistyp (Object?) enthalten, und daher identisch sind. Ich weiß garnicht ob es in Java inzwischen die Typsicheren Listen gibt, habe mich dafür zu lange nicht mit der Javaentwicklung beschäftigt.
Was du definitig aber nicht kannst ist wie oben schon angegeben über einen Pointer auf die Basisklasse die statische Methode aufrufen (weder in C++ noch in Java sofern ich mich jetzt nicht bei letzteren irre).
a) Statt einer statischen Methode setzt du eine ganz normale virtuelle Methode ein, was aber ein konkretes Exemplar zur Erzeugung erzwingt. Dies stopfst du in eine Liste (std::vector<BasisKlasse*> oder ähnliches)wo alle Typen enthalten sind und wenn du eins erzeugen willst gehst du über diese virtuelle Funktion.
b) Du behälst die Statische Methoden der Klassen und gehst über einen Zwischenschritt:
class IFactory { virtual std::list<BasisKlasse*> select() = 0; } template<class T> class IFactory { virtual std::list<BasisKlasse*> select() { return T::select(); } }T ist deine Konkrete Datenklasse; Hier benutzt du halt statt der liste auf die Datenklassen eine Indirektion, kannst aber dein Design beibehalten...
c)... Es gibt noch viele andere Möglichkeiten
Davon abgesehen würde ich die Factorymethode niemals in die Datenklasse legen, das deutet auf unflexibles Design hin. Wenn du die Erzeugung trennst, könntest du später unterschiedliche Factories für einen Typ einsetzten, je nach dem aus welcher Quelle etc. die Daten stammen...
cu André
-
asc schrieb:
moe szyslak schrieb:
...
Wenn ich dich richtig verstehe, kann das garnicht funktionieren (sowohl unter C++ als auch Java, davon abgesehen). Mag sein das man dich falsch versteht da es leider kein wirklich gutes Deutsch ist.
Statische Methoden benötigen konkrete Typinformationen. Anders sieht es aus wenn du wirklich Polymorphie verwendest.
Davon abgesehen kannst du keine Listen mit den Konkreten Datentyp zurückgeben und dann auf diese als Liste auf den Basistyp zugreifen. Wenn musst du grundsätzlich Listen der Basisklasse zurückgeben (Achtung: dann aber als Zeiger oder mittels Smartpointer, sonst wird alles der Klasse was nicht auch in der Basisklasse steht abgeschnitten) und über ein polymorphes Interface darauf zugreifen.
Beispiel:
static std::list<BasisKlasse*> KonkreteKlasse::select();
static std::list<boost::shared_ptr<BasisKlasse> > KonkreteKlasse::select();
...Ich glaube du hast mich nicht ganz verstanden. Hier mal ein Beispiel:
std::list<Gnomeo::DtAudioCodec> get_list() // v1 //std::list<Gnomeo::DtAbstract> get_list() // v2 { std::list<Gnomeo::DtAudioCodec> list; Gnomeo::DtAudioCodec a1; a1.set_name("A1"); Gnomeo::DtAudioCodec a2; a2.set_name("A2"); Gnomeo::DtAudioCodec a3; a3.set_name("A3"); list.push_back(a1); list.push_back(a2); list.push_back(a3); return list; }Diese Funktion stellt mir eine Liste von DtAudioCodec-Instanzen zusammen. Die DtAudioCodec-Klasse erbt von DtAbstract.
Grundsaetzlich mochte ich jetzt nur wissen, ob bzw. wie den Fuktions-Header von v1 auf v2 umaendern kann.
Aber ich denke mittlerweile dass das nicht moeglich ist, da DtAbstract eine abstrakte Klasse ist und man hoechstens eine Liste von Pointern auf DtAbstract* zurueckgeben koennte, oder hab ich da noch was uebersehn?
-
Wäre vollkommen unperformant ...
void class::get_list(std::list<Gnomeo::DtAudioCodec>& dest) { dest.clear(); dest.push_back(Gnomeo::DtAudioCodec("A1")); dest.push_back(Gnomeo::DtAudioCodec("A2")); dest.push_back(Gnomeo::DtAudioCodec("A3")); }und du sparst dir ne tmp. Instanz und ein kopieren dieser.
-
moe szyslak schrieb:
Ich glaube du hast mich nicht ganz verstanden.
Mag sein, dein Posting lies sich auch nicht wirklich gut lesen. Aber wenn ich es richtig sehe habe ich dich zum Teil schon verstanden...
moe szyslak schrieb:
std::list<Gnomeo::DtAudioCodec> get_list() // v1 //std::list<Gnomeo::DtAbstract> get_list() // v2 { ...Diese Funktion stellt mir eine Liste von DtAudioCodec-Instanzen zusammen. Die DtAudioCodec-Klasse erbt von DtAbstract.
Grundsaetzlich mochte ich jetzt nur wissen, ob bzw. wie den Fuktions-Header von v1 auf v2 umaendern kann.
Aber ich denke mittlerweile dass das nicht moeglich ist, da DtAbstract eine abstrakte Klasse ist und man hoechstens eine Liste von Pointern auf DtAbstract* zurueckgeben koennte, oder hab ich da noch was uebersehn?Das ist richtig. Du musst hierbei einfach folgendes Bedenken:
Ein Objekt wird durch die Klasse definiert, soweit ist das ganz einfach. Ein Objekt einer Abgeleiteten Klasse ist wenn man es vereinfacht ein Objekt der Basisklasse + des abgeleiteten Teiles:Basisklasse a; // [Basisklasse]
AbgeleiteteKlasse b; //[[Basisklasse][AbgeleiteteKlasse]]Was du mit der Rückgabe machen willst, wäre so, als würdest du dies mit einen scharfen Messer auftrennen und von der abgeleiteten Klasse nur noch den Basisklassenteil übrig lassen. Um jetzt auf Java einzugehen: Auch wenn Java eine Objektsyntax verwendet, arbeitet es statt dessen tatsächlich mit Referenzen bzw. Zeigern, auch wenn es dem Programmierer verborgen bleibt.
Du liegst vollkommen richtig: So etwas geht nur mit Zeigern. Statt reinen Zeigern würde ich hier aber lieber Smartpointer einsetzen.
Um auch ein wenig auf (D)Evil's Einwand einzugehen, hier ein Beispiel wie ich das tun würde (ungetestet und unter berücksichtigung der Boost-Bibliothek):
#include <boost/shared_ptr.hpp> ... // Entweder (üblicherweise performanter aber gewöhnungsbegürftiger): void class::get_list(std::list<boost::shared_ptr<Gnomeo::DtAbstract> >& dest) { dest.clear(); dest.push_back(boost::shared_ptr<Base>(new Gnomeo::DtAudioCodec("A1"))); dest.push_back(boost::shared_ptr<Base>(new Gnomeo::DtAudioCodec("A2"))); dest.push_back(boost::shared_ptr<Base>(new Gnomeo::DtAudioCodec("A3"))); } ... // Oder: std::list<boost::shared_ptr<Gnomeo::DtAbstract> > class::get_list() { std::list<boost::shared_ptr<Gnomeo::DtAbstract> > list; list.push_back(boost::shared_ptr<Base>(new Gnomeo::DtAudioCodec("A1"))); list.push_back(boost::shared_ptr<Base>(new Gnomeo::DtAudioCodec("A2"))); list.push_back(boost::shared_ptr<Base>(new Gnomeo::DtAudioCodec("A3"))); return list; }cu André