Es geht um die Wurst
-
RHBaum schrieb:
von mir selber schrieb:
Wieso sollte ich einen neuen Operator< schreiben? Generell ist mir egal, wo ich in meiner Menge einfuege? Vielleicht ein Operator= waere noch sinnvoll, aber mir gerade zu kompliziziert... Oder muss ich das aus irgendwelchen obskuren Gruenden machen ?!
Du arbeitest mit der STL, dazu sollte man die STL kennen. Lerne auch, Informationen ueber die verwendeten Libs zu bekommen !!!
Ja und wie bekomme ich an die Info's ...??
Habe bis dato bei www.cplusplus.com und www.c-plusplus.net immer nach Informationen gesucht, jedoch mit unterschiedlichem Erfolg.
Gibt es eine bessere Quelle ??weiter von vorhin ... schrieb:
zum std::set:
Anforderung: CopyConstructor , Lesser-Comperator (Kleiner-Vergleichsfunktion), per standard wird der < Operator der klasse genommen, kann aber im template ueberschrieben werden ....Habe nachgelesen, das in 'map' automatisch geordnet eingfuegt wird. Habe deswegen den operator < überschrieben, jedoch reicht das nicht.
Muss ich jetzt alle Relationen überschreiben ?!etwas weiter tiefer im Text : schrieb:
Wie wuerde ich dein problem loesen ....
- wrapperklasse fuer deinen generischen Wurstzeiger (normal als template, wird aber echt zu kompliziert. ), der alle Informationen / Methoden die deine generischen Wurste (oder lebensmittel) gemeinsam haben inklusive CCtor und lesser operator, so dass das ding in jedes set / map packen kannst ....
- ne Factory funktion, die die den richtigen Wurstzeigerwrapper(inklusive dynamisches erstelltes Wurst object) gleich erstellt, in abhaengigkeit der Eingangswerte .... eventuell als statische funktion gleich am Wrapper object.
- und voiala, schon iss nen std:set total easy zu benutzen ...Das waer "gutes c++"
Das glaube ich gerne nur habe ich allein bei ca. 36,94% Deines Textes ( fast gar ) keine Ahnung, was Du von mir möchtest ?!

Werde versuchen folgendes mehr zu verinnerlichen :
- wrapperklasse
- Generischer Wurstzeiger
- template
- CCtor ( sollte das eine Abkürzung für Konstruktor sein )
- lesser Operator ( oder nur ein Verschreiber für besser, auch wenn bei mir das b und das l auf der Tastatur ziemlich weit voneinander entfernt sind
- die Bedeutung des Operators ( wie kann ich erreichen, dass etwas sowohl in jedes set als auch in jede map kommen kann ?? )
- Factory Function
- Wurstzeigerwrapper ( vielleicht hilft mir die wrapperklasse dabei
)nette aufmunternde Worte: schrieb:
Viel Spass noch beim lernen !

Die Hoffnung stirbt zuletzt!
Trotzdem erst ein Mal ein Danke!
#50
-
Raute50 schrieb:
Ja und wie bekomme ich an die Info's ...??
Habe bis dato bei www.cplusplus.com und www.c-plusplus.net immer nach Informationen gesucht, jedoch mit unterschiedlichem Erfolg.
Gibt es eine bessere Quelle ??Ich benutze das hier als referenz.
-
Ja und wie bekomme ich an die Info's ...??
STL:
http://www.sgi.com/tech/stl/Naja, aber ehrlich, immer im Inet danach suchen ist laestig umstaendlich etc.
Wenn wirklich an C++ intressiert bist, wirst frueher oder spaeter Literatur in handfester Form bevorzugen ..."Die C++ Standard Biblothek" 3. Auflage mitlerweile, iss ne gute Sache da
Nen gutes C++ Buch was auf Neueinsteiger eingeht ist auch goldwert, allein um den EInstieg zu finden und paar vernuenftige Beispielprogramm-Ideen zu bekommen(nen Gorssteil der Einsteiger will gleich mit nem 3D SPiel loslegen)
Habe nachgelesen, das in 'map' automatisch geordnet eingfuegt wird. Habe deswegen den operator < überschrieben, jedoch reicht das nicht.
Muss ich jetzt alle Relationen überschreiben ?!map hat nen key, und Body ...
Key muss CCtor haben und lesser Operator (oder du ueberschreibst das am Template)
Body muss nur CCtor haben (wird auch nicht Sortiert)
CCtor = Copy Constructorclass C { public: C(); // Standard Ctor C(const C & rx); // CCTor mit constanter referenz ~C(); //Destruktor, gibts immer nur einen, zum deleten brauch ma keine parameter :p virtual ~C(); // Bei klasse von denen du ableiten(vererben) erlauben willst, solltest du den virtual machen ... }Warum brauch die map den CCTor ? weil sie in Ihrem Allokierten Speicher eine Kopie deiner klasse anlegt ... auch wichtig zu wissen ! Dazu nimmt sie prinzipiell den CCTor
nur habe ich allein bei ca. 36,94% Deines Textes ( fast gar ) keine Ahnung, was Du von mir möchtest ?!
wird schon noch
iss nich so schlimm, wenn du nich alles gleich verstehst ...wrapperklasse - Wrapper - Umhuellung. in C++ eine Klasse, die dir den Zugriff auf was anderes Kapselt, und deinen speziellen beduernissen anpasst. Bei den DesignPatterns(Sammlung von konzeptionellen Musterloesungen) auch manchmal unter Adapter zu finden.
In deinem Fall eine klasse, die intern einen Rohen zeiger auf ein Wurst-Object haelt, (du erinnerst dich, rohe zeiger allein sind nich so gut in C++) sich um dessen erzeugung und zerstoerung kuemmert, und den Lesser operator sowei CCtor implementiert. Da der lesser operator spezifisch ist bei dir ( was kleiner iss bestimmt dein inhalt), ist das nicht nur nen smartpointer sondern nen Wrapper / Adapter ...Generischer Wurstzeiger - zeiger auf ein Wurst Object. Da in deinen Anforderungen die rede von Vererbung ist :
class Wurst { public: Wurst(); virtual ~Wurst(); // richtig du willst vererben ,also destrukur immer virtual }; class LeberWurst : public Wurst { LeberWurst (); virtual ~LeberWurst (); } // irgendwo in den tiefen einer implementation LeberWurst * pLeberWurst = new LeberWurst; // erzeugen eines speziellen Leberwurst-Zeigers Wurst * pWurst = pLeberWurst; // hier zuweisung deines Speziellen Zeigers an nen generischeren WurstZeiger ...Das meinte ich mit generischen Wurstzeiger

template - Programiertechnik unter C++. Zum schreiben von generischen klassen, ohne vererbung. Template deshalb, weil du keine spezielle klasse in dem moment schreibst sondern nur eine Vorlage (Template) und darin ein oder mehrere platzhalter fuer Datentypen / Werte hasst. erst wenn du die klasse mit nem dem speziellen Datentypen / werten benutzt, ist der compiler in der lage, deinen code zu generieren ...
Der groesste teil der STL (Standard Template Library) sind templates ....CCtor - Copy Construktor - siehe oben
lesser Operator - < Operator (kleiner (lesser) Operator)
wird so meist deklariert:class C { // Konstrukor destruktor usw lass ich mal weg bool operator < (const C & rx); } // und schon kann man den verwenden ... C x1; C x2; if(x1 < x2) // usw usf ...Factory Function - Aus den Design patterns ... Funktionen die nur dafuer da sind, anhand von unterschiedlichen eingangsparametern, unterschiedliche klassen(Leberwurst, Blutwurst, Salami) mit gemeinsamer Basisklasse (Vererbung) zu generieren , dir dann einen Zeiger auf die gemeinsame Basisklasse zurueckgeben (Wurst)
Wurstzeigerwrapper - ein Wrapper fuern Wurstzeiger

Hoffe das hilft bisserl weiter, und du bekommst nen Gefuehl, auf was du dich hier einlaesst

Tamplates und Designpatters solltest im moment eh nich beachten, das kommt eigentlich ganz ganz spaet ... da braucht man halt schon die anderen grundlagen zu ...
Ciao ...
-
Sollte der op< nicht besser als binärer friend implementiert werden ?
-
prolog schrieb:
cd ~/pub
more beerfinde ich cool ( auch wenn ich nicht in jedem Ordner ein pub-Verzeichnis habe, deswegen das ~ ) !
Mache mir als Gag mal so ein Verzeichnis mit so einer Datei.....
Und nun wieder zu C++ ...
#50
-
Sollte der op< nicht besser als binärer friend implementiert werden ?
Jo eigentlich schon ...
macht aber auch wirklich nur nen Unterschied, wenn du impliziete Konvertierungen zulaesst. Auf der anderen Seite macht ne externe Implementierung nix kapput ... von daher ok, lieber extern als friend.Ciao ...
-
Hellas,
habe diese Version mal versucht zu implementieren:
Werner Salomon schrieb:
so sollte das erstmal gehen
wurst make_wurst( std::istream* die_datei ) { // std::istream reicht wurst wurst_neu; lesen( die_datei, &wurst_neu ); wurst_neu.print(); // es wird ein Wurst-Objekt korrekt generiert und eingelesen return wurst_neu; // mit der ganzen Wurst raus } int main() { std::ifstream datei("dateiname"); std::set< wurst > wuerste; wuerste.insert( *make_wurst( &datei ) ); // Im Original ohne * funktioniert aber nur mit * return 0; }dazu benötigst Du aber für wurst einen operator< oder einen eigenen Comperator, um zwei Würste miteinander zu vergleichen. Wenn die Würste in einer Ableitungshierarchie stehen und in dem set auch Objekte anderer klassen untergebracht werden sollen, wird's aufwendiger.
Gruß
WernerPS.: Klassen, die wurst heißen sind

Den Operator< habe ich so geschrieben :
friend bool operator< ( wurst w,wurst v) { return ( w.qualitaet > v.qualitaet); }und die abgeänderte Version von make_wurst() sieht so aus
wurst* make_wurst2( std::ifstream *datei) { wurst* neu = new wurst; lesen(datei, neu); neu->print(); return neu; }meine main() sieht so aus :
int main(int argc, char *argv[]) set<wurst> Wuerste; std::ifstream datei("dateiname"); char c; datei.get(c); datei.get(); datei.get(); switch (c) { case 'K' : break; case 'W' : cout <<"es ist eine WURST\n\n"; Wuerste.insert(*make_wurst2(&datei)); Wuerste.insert(*make_wurst2(&datei)); Wuerste.insert(*make_wurst2(&datei)); cout << "Ich habe nun " << Wuerste.size() << "Wuerste am Start!"; //make_wurst(&datei,w,wurst_pointer); break; default : cout <<" Wieso komme ich in dieses besch... default rein ?! "; } cout <<" \n\n\nFertig!\n\n"; system("PAUSE"); return EXIT_SUCCESS; }In dateiname steht folgendes:
W::wurst,2.22,3,4,5,6,77,TIER W::wurstchen,2.34,5,6,7,8,90,jaja W::wuerste,1.23,9,8,7,6,574,nein K::CH,2.2,3,4,5,6,7,gauda NName,2.99,4,5, leb,1,3,4,55,6,777,Und es tut was es soll ... ( nicht das mich das stört ) !!!
Nur leider habe ich das immer noch nicht so ganz verstanden ...



Wenn ich in der lokalen Funktion ein Wurst-Objekt generiere und den Zeiger darauf zurück gebe, wieso klappt das ????
Hoffe ihr verzweifelt nicht an mir, wie ich an C++!
Mache dies nicht ganz freiweillig... Ist ein Projekt, welches meinen Namen mit tragen soll... nur sehe ich schwarz bei meinen Erfolgen ( zu denen ich auch 'Hallo Welt' zähle ) ...
Bis bald vielleicht !!!
#50
-
Ok, dann schritt fuer schritt

mach dir mal paar gedanken wie man so beim design vorgeht:
Design ist so die wichtigste phase, die implementation dann eigentlich nur noch fleissarbeit (wennd as design gut war
).1. Frage
W::wurst,2.22,3,4,5,6,77,TIER
W::wurstchen,2.34,5,6,7,8,90,jaja
W::wuerste,1.23,9,8,7,6,574,nein
K::CH,2.2,3,4,5,6,7,gauda
NName,2.99,4,5,
leb,1,3,4,55,6,777,Die Dateistructur ist vorgegeben, kannst also nicht selbst was drann drehen ?
wenn ja, wie fest(stabil gegen veraenderungen) ist die ?Dein Code in bezug auf den zugriff auf den stream sieht .. argl .. aus

muss man 3 mal hinschauen, um zu sehen was da machst.
Intuitiver (und damit besser wartbar) waere sowas wie :std::ifstream datei("dateiname"); while (!datei.eof()) { Datensatz dat; datei >> dat; // und hier irgendwas mit dem Datensatz tun ... }Deswegen frage ich, wie die datei aussieht, und ob du das drehen kannst, bzw. wie sicher die vor veraenderungen ist ....
Dann kann man die Datensatz klasse modellieren ....Weiterer Prinzipieller Weg:
deine Spaeteren Objecte (leider willst ja unbedingt polymorphie haben
)
muessen ja alle geminsam zu irgendwas nuetze sein
diese Gemeinsamkeiten must herausfinden, und in ner Schnittstelle definieren ....sowas wie:
class ILebensMittel { protected: ILebensMittel(){}; virtual ~ILebensMittel(){}; public: float getPreis() const = 0; const std::string & getName() const = 0; // was weiss ich nich noch alles :-) };Ok, weiter gehts dann in der naechsten Folge

Ciao ..
-
Raute50 schrieb:
Wenn ich in der lokalen Funktion ein Wurst-Objekt generiere und den Zeiger darauf zurück gebe, wieso klappt das ????
Denke du sprichst die Funktion make_wurst2 an.
Warum sollte das nicht funktionieren. Du generierst die wurst ja am heap. Niemand überschreibt deine Wurst. Anders würde es aussehen wenn make_wurst2 so gecodet wäre:wurst * make_wurst( std::istream* die_datei ) { wurst wurst_neu; lesen( die_datei, &wurst_neu ); wurst_neu.print(); return & wurst_neu; }In diesem Fall liegt wurst_neu am Stack. Sie wird zwar wahrscheinlich dort noch immer sein wenn make_wurst zum Aufrufer zurückkehrt aber die nächste Funktion die aufgerufen wird verwendet den speicher von wurst_neu und wurst_neu wird überschrieben. Du kannst also die wurst nicht mal kopieren ohne sie zu zerstören.
Kurt
-
RHBaum schrieb:
Ok, dann schritt fuer schritt

1. FrageW::wurst,2.22,3,4,5,6,77,TIER
W::wurstchen,2.34,5,6,7,8,90,jaja
W::wuerste,1.23,9,8,7,6,574,nein
K::CH,2.2,3,4,5,6,7,gauda
NName,2.99,4,5, < hier fehlt eine Zahl, ist mir gerade aufgefallen
>
leb,1,3,4,55,6,777,Die Dateistructur ist vorgegeben, kannst also nicht selbst was drann drehen ?
wenn ja, wie fest(stabil gegen veraenderungen) ist die ?Die Datenstruktur haben wir uns selber ausgedacht ...
Vererbungshierarchie: Produkt->Lebensmittel->Wurst, Kaese, ...
Eigenschaften:
- Produkt ( char* name,double preis,unsigned int bestand,unsigned int kapazitaet),
- Lebensmittel ( zusätzlich unsigned int gewicht,int haltbarkeit,unsigned int qualitaet),
- Wurst ( zusätzlich nur char* tier)
- Kaese ( adäquate andere 'Zusätze')Oben ist NName ein Produkt und leb ein Lebensmittel, die Kommata stehen in der Ausgabe nur, weil wir die einzelnen print() Methoden ausprobiert haben.
Lieber eine Debug Ausgabe zuviel, als einen Tag Fehlersuche.Dein Code in bezug auf den zugriff auf den stream sieht .. argl .. aus

muss man 3 mal hinschauen, um zu sehen was da machst.
Intuitiver (und damit besser wartbar) waere sowas wie :std::ifstream datei("dateiname"); while (!datei.eof()) { Datensatz dat; datei >> dat; // und hier irgendwas mit dem Datensatz tun ... }Die main() sieht nur so aus, weil wir am Anfang stehen, also ist der weitere detaillierte Weg noch offen.
Wir haben uns nur überlegt, wie wir die sets mit den nötigen Objekten füttern...
In der main() kann man gut sehen, dass wir es an einer Wurst überprüfen wollten, bevor wir weiter in's Detail gehen.Deswegen frage ich, wie die datei aussieht, und ob du das drehen kannst, bzw. wie sicher die vor veraenderungen ist ....
Dann kann man die Datensatz klasse modellieren ....Weiterer Prinzipieller Weg:
deine Spaeteren Objecte (leider willst ja unbedingt polymorphie haben
)
muessen ja alle geminsam zu irgendwas nuetze sein
diese Gemeinsamkeiten must herausfinden, und in ner Schnittstelle definieren ....sowas wie:
class ILebensMittel { protected: ILebensMittel(){}; virtual ~ILebensMittel(){}; public: float getPreis() const = 0; const std::string & getName() const = 0; // was weiss ich nich noch alles :-) };Ok, weiter gehts dann in der naechsten Folge

Ciao ..
den letzten Teil verstehe ich nicht, bzw. ich sehe nicht ein, was mir das im Moment bringt?!
P.S.:
Muss leider gleich zur Arbeit....
Werde dieses Mal wohl ein wenig zögern, bevor die nächste Frage ( auch wenn sie für euch noch so dumm aussehen mag ) stelle, die mich zum VErzweifeln bringt!In diesem Sinne ....
#50
-
den letzten Teil verstehe ich nicht, bzw. ich sehe nicht ein, was mir das im Moment bringt?!
Was willst du alles mit den Lebensmitteln machen ???
Du willst doch vielleicht irgendwelche preise ausgeben, listen drucken etc ...Dazu gibt es 2 Wege ...
1. du kreirst eine klasse die alles kann ...
Also eine lebensmittel-klasse die z.B nen Typ ausgeben, Name und so ...
Felder fuer alle möglichen Daten hat, und bei den typen wo man bestimmte daten nicht braucht, setzt man überall platzhalter ein ...Vorteil: du brauchst keine Polymorphie
Nachteil: wenn irgendwo mal nen neuer lebensmitteltyp hinzukommt, aenderst alle funktionen die in irgend einer weisse mit der Lebensmittleklasse ueberhaupt zu tun haben ... vergisst irgendwas -> undefiniertes / unlogisches verhalten ...
2. du ueberlegst Dir ne schnittstelle, was alle lebensmittel koennen muessen fuer die speziellen aufgaben ....
Du kannst dir schoene Aufgaben.Objecte(klassen) anlegen, zum drucken, preise berechnen etc, die nur deine Schnittstelle kennen muss.Nachteil: Du brauchst Polymorphie, du bracuhst zumindest ne rudimentaere Objectfabrik
Vorteil: wenn neue Lebensmitteltypen hinzukommen / bzw sich das von verhalten von anderen anedert ... uberschreibst nur an der betroffenen klasse die virtuellen funktionen ... du brauchst die anderen klassen nich mehr anzufassen.
Bei ganz kleinen projekten, kann man drueber streiten, welcher weg besser ist ... wenn man alles im Blick hat, nie niemals nen anderer den code uebernehmen muss, grosse erweiterungen so gut wie ausgeschlossen sind , dann kann man durchaus mal weg 1 nehmen ...
Bei richtigen projekten -> weg 2
An der Stelle entscheidest du ... zwischen super design, aber unendlich aufwand, projekt wird masslos teuer schon vor der ersten version ... und schneller Implementation, schnelle ergebnisse, aber wenn der programmierer wegfaellt, bzw aenderungen ins haus stehen, artet alles aus ... mal schnell "wir bekommen fisch dazu, der hat nen parameter 'Graeten pro kg' " iss dann nich mehr trivial ....
Aber so wie ich sehe, wuerdest du lieber weg 1 nehmen ? erstmal ...
dann Fang mal an die lebensmittelklasse zu modellieren ...
Ohne polymorphie "erstmal" ne klasse nur fuer "wurst" zu modellieren, wird nicht funktionieren .... weil spaeter aenderst du dann wirklich alles wieder, wenn du was erweiterst ...wobei mir wirklich der hintergrund fehlt, was genau du alles machen willst ...
getrennte Listen fuer Wurst, Kaese Fisch ... etc machen vielleicht sinn, wenn du nicht viele operationen hasst, die auf alle typen angewand werden muessen ...Ciao ...
-
RHBaum schrieb:
Aber so wie ich sehe, wuerdest du lieber weg 1 nehmen ? erstmal ...
dann Fang mal an die lebensmittelklasse zu modellieren ...
Ohne polymorphie "erstmal" ne klasse nur fuer "wurst" zu modellieren, wird nicht funktionieren .... weil spaeter aenderst du dann wirklich alles wieder, wenn du was erweiterst ...Falls ich das richtig verstanden habe, haben wir Weg 2 gewählt.
Wir haben ( nur in Ansätzen ) versucht so viel wie möglich so früh wie möglich zu implementieren. Sprich gibName(), gibPreis(), ... an der Stelle zu implementieren, wo die Eigenschaften auftreteten ( in diesem Falle schon in Produkt ). Und jedes Mal wenn geerbt wird, wird nur das neu implementiert, was auch neu dazugekommen ist, z.B. bei Lebensmittel Haltbarkeit ( denn nicht automatisch jedes Produkt hat ein MindesHaltbarkeitsDatum ).
Also ich denke, wir haben den Weg 2 eingeschlagen....Dieses soll ja auch nur eine Gewöhnung sein, damit wir ( wenn die wirkliche Arbeit kommt ) auch nach 'gutem Stil' programmieren ( so fern es uns möglich ist ).
der Herr RHBaum mal wieder schrieb:
wobei mir wirklich der hintergrund fehlt, was genau du alles machen willst ...
getrennte Listen fuer Wurst, Kaese Fisch ... etc machen vielleicht sinn, wenn du nicht viele operationen hasst, die auf alle typen angewand werden muessen ...Na gut dann kommt hier Der Hintergrund:
Wir wollten uns im Detail mit Vererbung ( und alles was dazu gehört, sprich Überladen von Funktionen, ... ) beschäftigen und haben uns deswegen Produkte.h überlegt, von denen wir erben, nämlich Lebensmittel und Getränke als erstes. Davon wollten wir weiter erben, unter anderem kommt hier die Wurst.
So weit ging alles gut.Dann haben wir versucht uns mit Schreiben in und Lesen aus einer Datei zu beschäftigen, und wollten uns eine sinnvolle Aufgabe dazu ausdenken.
An dieser Stelle kommt der Verwalter in das Spiel, der alle Bestände kontrollieren soll.
Er soll ( in unserem Beispiel ) aus einer korrekt gespeicherten Datei lesen und so die Regale füllen, was bedeutet, dass er für jede Ware ( Wurst, Käse, ... ) einen set erzeugt und diesen befüllt.Also wie im Film: Alles frei erfunden!
Hoffe bei euch sind jetzt ein paar Unklarheiten verschwunden!?
P.S.:
Wenn ihr mir sagt wie, poste ich euch mal die gesammelten Werke, die wir haben ..#50