Polymorphes Speichern
-
Hallo,
angenommen ich habe folgende Klassen:struct Human {int x;}; struct Male : public Human {unsigned char y;}; struct Female : public Human {long double z;}; struct Container { shared_ptr<Base> ptr; };und möchte den Output-Operator überladen, um Cointainer-Objekte in einer Datei zu speichern, wie löse ich dies am besten? Der Pointer ptr kann ja auf ein Human-, ein Male- oder ein Female-Objekt zeigen, die alle verschiedene Größen haben.
-
struct Human { virtual ostream& WriteTo(ostream& os) const{ return os << x; } int x; }; struct Male : public Human { ostream& WriteTo(ostream& os) const override { Human::WriteTo(os); return os << y; } unsigned char y; }; //und so weiter... ostream& operator<<(ostream& os, const Human& human) { return human.WriteTo(os); } int main() { Container container; container.ptr = new Male(); cout << *container.ptr << endl; }
-
Okay, danke.
Was ich vergessen habe zu fragen: Wie sieht es mit dem Laden aus? Ich habe also einen ifstream und möchte aus diesem wieder den ursprünglichen Container wiederherstellen.
-
Auch wenn ich polymorphes Laden etwas komisch finde: Jetzt, wo du die Lösung fürs Speichern siehst, kommst du da nicht auf eine Möglichkeit zum Laden?
-
Das einlesen von objekten ist bei c++ so eine sache...
Ich kenn da jetzt nur manuelle umständliche Lösungen, ungefähr so:
class Human { public: enum HumanType { Male, Female }; Human(){x = 0;} Human(istream& is){is >> x; } virtual ostream& WriteTo(ostream& os) const {return os << GetType() << x;} protected: virtual HumanType GetType() const = 0; private: int x; }; class Male : public Human { public: Male(){y = 0;} Male(istream& is) : Human(is) {is >> y;} ostream& WriteTo(ostream& os) const override { Human::WriteTo(os); return os << y;} protected: virtual Human::HumanType GetType() const override {return Human::Male;} private: int y; }; //Female analog //wenn du willst, kannst du auch einen smart_ptr oder deine container-klasse zurückgeben Human * LoadHuman(istream& is) { Human::HumanType type; is >> type; switch(type) { case Human::Male: return new Male(is); case Human:Female: return new Female(is); default: throw sthg; } }Das ganze ist viel Aufwand und nicht unbedingt schön. Aber für C++ kenn ich keine anderen Lösungen, die ohne Codegeneratoren funktionieren.
Edit: vielleicht hilft dir boost::serialization. Ich bin mir aber nicht sicher, ob das sowas kann.
-
Q schrieb:
class Human { public: enum HumanType { Male, Female }; }; Human * LoadHuman(istream& is) { Human::HumanType type; is >> type; switch(type) { case Human::Male: return new Male(is); case Human:Female: return new Female(is); default: throw sthg; } }Ich würde übrigends empfehlen, bei größeren Mengen an möglichen Klassen statt einem switch ein Array von Human* (*)(std::istream&) zu verwenden, da kann man entsprechend statt einem switch einfach return Tabelle[type](is); schreiben. Prüfen, ob der Wert gültig ist, müsste man trotzdem noch über ein if, vielleicht mit Hilfe eines Maximalwerts im enum, a lá Human::max.
-
@ cooky451: Einfach eine virtuelle Funktion machen geht ja nicht, da das Programm ja nicht wissen kann, welcher Typ gerade gelesen wird.
@ Q:
As can be seen in demo.cpp, serialization of pointers to derived classes through a base clas pointer may require explicit enumeration of the derived classes to be serialized.
Die Library bietet dafür wohl ein System an. Es scheint einfacher zu sein, als das Enum-Zeug, aber leider auch nicht so einfach, wie ich es mir gewünscht hätte.
Naja, muss ich wohl mit leben, danke für den Hinweis auf boost.
-
Wurstinator schrieb:
@ cooky451: Einfach eine virtuelle Funktion machen geht ja nicht, da das Programm ja nicht wissen kann, welcher Typ gerade gelesen wird.
Doch, schon. Ich weiß was du meinst, aber das ist eine völlig andere Frage. Das hat nichts mehr mit Polymorphie zu tun. Du musst dir halt ein Format überlegeben, bei dem du beim Einlesen die verschiedenen Typen unterscheiden kannst.
-
Wurstinator schrieb:
Einfach eine virtuelle Funktion machen geht ja nicht, da das Programm ja nicht wissen kann, welcher Typ gerade gelesen wird.
Jein. Google mal nach "virtual constructor" und "factory method"