Hallo!
Ich programmiere aktuell ein kleines Programm in C++ (auf Linux, falls
das wichtig ist).
Ich plane, eine Klasse "Schulklasse" anzulegen, in der es z.B. Variablen
gibt wie "klassenvorstand", "klassensprecher", "raumNummer" und eine
queue-Variable, die Objekte der Klasse "Schüler" speichert.
Die Klasse Schüler hat für sich dann wieder Variablen wie "Vorname",
"Nachname", "Alter", etc.
Nun möchte ich das ganze Konstrukt in eine Datei abspeichern.
Wenn ich nun also ein Objekt habe mit dem Namen "Klasse_1A" (der in der
queue-Variable z.B. 20 Objekte "Schüler" hat), wäre es toll, wenn es
sowas geben würde wie
1
datei << Klasse_1A;
zum Abspeichern bzw.
1
datei >> Klasse_1A;
zum Auslesen aus der Datei.
Python hat doch soetwas mit "Pickle".
Gibt es soetwas auch für C++, das ähnlich einfach geht? Ein Beispiel
wäre sehr nett!
Danke euch im Voraus!
Gibt es. Nennt sich Operatoren überladen. Du musst für die Operatoren <<
und >> eben eine Funktion schreiben, so dass genau das passiert was du
haben willst. Eventuell tut es auch eine Serialization/Deserialization
Library.
BOOMERang schrieb:> Serialization/Deserialization
Ist das richtige Stichwort.
Kann man selber schreiben falls es was spezifisches sein soll.
Ansonsten gibt es auch einige Libs die von Klasse zu z.B JSON, XML, usw
wandeln und auch wieder zurück.
Danke für die Hinweise!
Boost Serialization klingt relativ vielversprechend, was ich aber nicht
ganz verstehe: Kann ich dann einfach die Klasse „Schulklasse“
serialisieren und die Objekte Schüler eines Objekts Klasse_1A werden
gleich mit abgespeichert?
Oder muss ich das alles in jede Klasse implementieren?
Sieht irgendwie komplizierter aus, als ich es erhofft habe. Bei Python
ging das doch ziemlich einfach…
Frager schrieb:> Danke für die Hinweise!> Boost Serialization klingt relativ vielversprechend, was ich aber nicht> ganz verstehe: Kann ich dann einfach die Klasse „Schulklasse“> serialisieren und die Objekte Schüler eines Objekts Klasse_1A werden> gleich mit abgespeichert?> Oder muss ich das alles in jede Klasse implementieren?> Sieht irgendwie komplizierter aus, als ich es erhofft habe. Bei Python> ging das doch ziemlich einfach…
Müssen musst du nicht, bietet sich aber an.
Schwer ist es an sich nicht.
1. Operator überladen
2. Überlegen, was du wirklich speichern/laden willst und damit dann die
Struktur in der Datei festlegen. Z.b. zuerst Klassenvorstand, dann
Raumnummer, Anzahl Schüler, Liste mit Schülern,....
3. Evtl. Eine Version abspeichern, falls du die Dateistruktur Mal ändern
solltest. Dann kann man beim laden basierend auf der Version die Datei
unterschiedlich bearbei.
Wenn du kein Problem damit hast, einen Codegenerator zu verwenden,
könnte
Googles "Protocol Buffers" eine einfache Serialization-Lösung für dein
Problem sein.
https://developers.google.com/protocol-buffers/
nlohmann/json kann man sich auch mal ansehen, das ist relativ schmerzarm
und fühlt sich nicht ganz so nach 2003 an wie boost, und json ist zum
Rest der Welt kompatibel.
Statt da direkt mit irgendwelchen Bibliotheken loszulegen, wär's für den
Lerneffekt vmtl. besser, ganz klassisch selber die ">>" und "<<"
Operatoren zu überladen.
Auch zu Bedenken: du hast in der Klasse einen Schüler Klassensprecher
und denselben Schüler auch nochmal in der Schülerliste.
Wenn du das stupide runterserialisierst, stehen diese Daten doppelt in
der Datei. Beim Wiedereinlesen werden das dann zwei unterschiedliche
Schüler-Objekte, die nur mit identischen Werten initialisiert worden
sind.
Frager schrieb:> Hallo!>> Ich programmiere aktuell ein kleines Programm in C++ (auf Linux, falls> das wichtig ist).>> Ich plane, eine Klasse "Schulklasse" anzulegen, in der es z.B. Variablen> gibt wie "klassenvorstand", "klassensprecher", "raumNummer" und eine> queue-Variable, die Objekte der Klasse "Schüler" speichert.
Muss das wirklich eine Queue sein - ich würde eher ne std::set<Person>
vorschlagen... (wobei ein Objekte der Klasse Person halt z.B. ein
Schüler sein kann).
> Die Klasse Schüler hat für sich dann wieder Variablen wie "Vorname",> "Nachname", "Alter", etc.>> Nun möchte ich das ganze Konstrukt in eine Datei abspeichern.
std::fstream kannste nehmen, aber selbst das speichern der struct/class
mit fwrite/fread könnte gehen. Die coole Fancy-Lösung ist mit boost.
> Wenn ich nun also ein Objekt habe mit dem Namen "Klasse_1A" (der in der> queue-Variable z.B. 20 Objekte "Schüler" hat),
Was spräche dagegen, wenn du einen std::set<Schulklasse> erzeugst, indem
jedes 'Schulklasse'-Objekt eben eine std::set<Person> m_schueler
enthält.
Google mal nach Entity/Relationship Modell, vielleicht hilft dir das.
tut nix zur Sache schrieb:> Muss das wirklich eine Queue sein - ich würde eher ne std::set<Person>> vorschlagen... (wobei ein Objekte der Klasse Person halt z.B. ein> Schüler sein kann).
Ein set ist für sowas in der Regel nicht sinnvoll. Eher ein vector.
Rolf M. schrieb:> tut nix zur Sache schrieb:>> Muss das wirklich eine Queue sein - ich würde eher ne std::set<Person>>> vorschlagen... (wobei ein Objekte der Klasse Person halt z.B. ein>> Schüler sein kann).>> Ein set ist für sowas in der Regel nicht sinnvoll. Eher ein vector.
Du meinst also, dieselbe Person könnte mehr als einmal in einer Klasse
sein?
Tja, ein Python Objekt enthält zur Laufzeit alle Informationen, die
Pickle für die Serialisierung braucht. In einem kompilierten C++
Programm sind diese Informationen schlicht und einfach nicht mehr
vorhanden.
So wird das halt aufwendig. Langfristig kommst du besser bei weg, wenn
du dich in ein paar Generatoren und Frameworks einarbeitest. Im laufe
der Zeit stößt du auf hunderte von Problemen. Die Frameworks sehen zwar
unnötig komplex aus, aber die haben aber schon eine Lösung für diese
unzähligen Probleme.
Anstatt ganze Objekte in irgendwelchen Framework abhängigen
Binarformaten zu speichern, könnte man die Daten auch in SQLite ablegen.
Der Aufwand ist überschaubar und portabel ist es dann auch noch.
Noch was schrieb:> Tja, ein Python Objekt enthält zur Laufzeit alle Informationen, die> Pickle für die Serialisierung braucht.
Ja... also, fast alle Informationen. Klar, für einfache Python-Instanzen
geht es mithilfe des pickle-Moduls ganz einfach. Aber probier das mal
mit dynamischeren Sachen, beispielsweise mit Instanzen, deren Methoden
ihrerseits Funktionen oder Lambdas beinhalten. Da scheitert pickle sogar
schon an recht einfachen Fällen und verschiedenen Builtin-Typen.
Obendrein ist bei der Verwendung von pickle immer die Gefahr gegeben,
daß nichtvertrauenswürdiger Code ausgeführt werden kann, siehe die große
rot unterlegte Warnung oben in [1].
Etwas mehr als das builtin-Modul pickle kann das externe Modul dill
serialisieren, das als Dropin-Ersatz für pickle gedacht ist und daher
dieselbe API implementiert, aber ein bisschen mehr Funktionen bietet.
Aber auch wenn dill einiges serialisieren kann, das pickle nicht
schafft, ist das auch noch nicht der Weisheit letzter Schluß und
scheitert an manchen Konstrukten, beispielsweise Tracebacks und -- für
mich ganz besonders ärgerlich, wenngleich verständlich -- Generatoren.
Die Beschränkungen zur Ausführung nichtvertrauenswürdigen Codes bestehen
jedoch auch hier.
[1] https://docs.python.org/3/library/pickle.html> So wird das halt aufwendig. Langfristig kommst du besser bei weg, wenn> du dich in ein paar Generatoren und Frameworks einarbeitest.
Mein grundsätzlicher Rat wäre ein Serialisierungsformat, das auch mit
anderen Sprachen verwendet werden kann. Da gibt es ja eine ganze Menge,
JSON oder YAML beispielsweise, die sich auch gut komprimieren lassen --
oder Spezialisten wie Parquet, MessagPack, ProtoBuf oder Apache Thrift.
Die haben zwar alle ihre ganz eigenen Limitierungen, aber zumindest
kommt man auch mit anderen Sprachen an die serialisierten Inhalte,
wenngleich das für ein Übungs-Spielprojekt wie das des TO vermutlich
keine hohe Priorität hat.
Wilhelm M. schrieb:> Du meinst also, dieselbe Person könnte mehr als einmal in einer Klasse> sein?
Definiere "dieselbe Person", bzw. musst du definieren, wie man erkennt,
ob eine Person "kleiner" ist als eine andere im Sinne einer
Sortierreihenfolge. Dann kann man mit einem set effizient überprüfen, ob
eine Person, die nach genau diesen Kriterien die selbe ist, bereits
vorhanden ist. Ansonsten bietet das set aber keinen Vorteil.
> ein Serialisierungsformat, das auch mit> anderen Sprachen verwendet werden kann
Das hat sich nicht bewährt. Von Corba bis SOAP. Immer wieder das selbe
Problem. Wenn das Format alle Features bietet, wird es unbrauchbar
komplex. Bei einfachen Formaten musst du sowieso einen Mapper schreiben.
Kannst nur die Daten aus dem JSON benutzen, nicht die kompletten
Objekte.
Wenn du ein System aus mehreren Programmen in mehreren
Programmiersprachen baust, brauchst du normalerweise sowieso mehrere
Objektmodelle. In Summe hast du weniger Arbeit, wenn du traditionelle
Datenbanktabellen benutzt. Jedes Programm mappt die Daten auf seine
eigene Objektstruktur.
Noch was schrieb:> Das hat sich nicht bewährt. Von Corba bis SOAP.
Bei CORBA und SOAP geht es primär um Remote Procedure Calls in
Netzwerken, nicht um die Serialisierung von Objekten in Dateien.
CORBA und SOAP Enterprise-Umfeld sind weit verbreitet. In anderen
Bereichen werden derartige Technologien nicht so oft benötigt, häufig
stehen auch domänenspezifische Alternativen wie etwa OpenMP oder Spark
zur Verfügung.
Insofern fürchte ich, daß Dein Einwand inhaltlich nicht zutrifft und
obendrein leider am Thema dieses Threads vorbeigeht.
Rolf M. schrieb:> Wilhelm M. schrieb:>> Du meinst also, dieselbe Person könnte mehr als einmal in einer Klasse>> sein?>> Definiere "dieselbe Person",
Das muss der TO machen, nicht ich.
> bzw. musst du definieren, wie man erkennt,> ob eine Person "kleiner" ist als eine andere im Sinne einer> Sortierreihenfolge.
Der operator< wird wie üblich verwendet, um zu prüfen, ob Gleichheit
oder Ungleichheit vorliegt. Im Sinne des TO ggf. einfach so etwas wie
eine "Schülernummer".
> Dann kann man mit einem set effizient überprüfen, ob> eine Person, die nach genau diesen Kriterien die selbe ist, bereits> vorhanden ist. Ansonsten bietet das set aber keinen Vorteil.
Das set (Menge) ist eben genau die Datenstruktur, um den Sachverhalt
einer Klasse (eine Menge von Personen - im mathematischen Sinne) zu
modellieren. Datenstrukturen sind so zu wählen, dass sie den zu
modellierenden Sachverhalt (möglichst) genau abbilden.
Wilhelm M. schrieb:> Rolf M. schrieb:>> Wilhelm M. schrieb:>>> Du meinst also, dieselbe Person könnte mehr als einmal in einer Klasse>>> sein?>>>> Definiere "dieselbe Person",>> Das muss der TO machen, nicht ich.
Nun, du hast den Begriff ins Spiel gebracht.
>> bzw. musst du definieren, wie man erkennt,>> ob eine Person "kleiner" ist als eine andere im Sinne einer>> Sortierreihenfolge.>> Der operator< wird wie üblich verwendet, um zu prüfen, ob Gleichheit> oder Ungleichheit vorliegt. Im Sinne des TO ggf. einfach so etwas wie eine> "Schülernummer".
Oder ein dem set übergebener Funktor. Ich würde da auch keinen
programmweiten Vergleichsoperator für so eine Struktur haben wollen, der
nur ein einzelnes Element davon vergleicht. Da diese Operation so nur
für das set benötigt wird und ansonsten eher wenig sinnvoll ist, gehört
sie auch nur dort hin.
>> Dann kann man mit einem set effizient überprüfen, ob>> eine Person, die nach genau diesen Kriterien die selbe ist, bereits>> vorhanden ist. Ansonsten bietet das set aber keinen Vorteil.>> Das set (Menge) ist eben genau die Datenstruktur, um den Sachverhalt> einer Klasse (eine Menge von Personen - im mathematischen Sinne) zu> modellieren.
Diese Beschreibung trifft auf die meisten Standard-Container zu. Was
zeichnet da das set jetzt speziell aus?
Rolf M. schrieb:> Wilhelm M. schrieb:>> Rolf M. schrieb:>>> Wilhelm M. schrieb:>>>> Du meinst also, dieselbe Person könnte mehr als einmal in einer Klasse>>>> sein?>>>>>> Definiere "dieselbe Person",>>>> Das muss der TO machen, nicht ich.>> Nun, du hast den Begriff ins Spiel gebracht.
Ja, ich ich muss nicht die Semantik definieren!
>>>> bzw. musst du definieren, wie man erkennt,>>> ob eine Person "kleiner" ist als eine andere im Sinne einer>>> Sortierreihenfolge.>>>> Der operator< wird wie üblich verwendet, um zu prüfen, ob Gleichheit>> oder Ungleichheit vorliegt. Im Sinne des TO ggf. einfach so etwas wie eine>> "Schülernummer".>> Oder ein dem set übergebener Funktor. Ich würde da auch keinen> programmweiten Vergleichsoperator für so eine Struktur haben wollen, der> nur ein einzelnes Element davon vergleicht. Da diese Operation so nur> für das set benötigt wird und ansonsten eher wenig sinnvoll ist, gehört> sie auch nur dort hin.
Ich denke, sie wird auch sonst benötigt, z.B. um sich eben eine Liste
alle Schüler in natürlicher Reihenfolge auszugeben.
>>> Dann kann man mit einem set effizient überprüfen, ob>>> eine Person, die nach genau diesen Kriterien die selbe ist, bereits>>> vorhanden ist. Ansonsten bietet das set aber keinen Vorteil.>>>> Das set (Menge) ist eben genau die Datenstruktur, um den Sachverhalt>> einer Klasse (eine Menge von Personen - im mathematischen Sinne) zu>> modellieren.>> Diese Beschreibung trifft auf die meisten Standard-Container zu. Was> zeichnet da das set jetzt speziell aus?
Du weißt nicht, was das Kennzeichen einer Menge (mathematisch) ist?
Rolf M. schrieb:>>> Dann kann man mit einem set effizient überprüfen, ob>>> eine Person, die nach genau diesen Kriterien die selbe ist, bereits>>> vorhanden ist. Ansonsten bietet das set aber keinen Vorteil.
Na ja, ne std::set ist auch sortiert. Wenn man also den Funktor
überschreibt bzw. ein Prädikat definiert, sind alle in der set
gespeicherten Elemente (z.B. hier Schüler) gleich nach Vor- und
Nachnamen sortiert. Auswerten sollte der doch das Prädikat höchstens
beim Einfügen in die set.
>> Das set (Menge) ist eben genau die Datenstruktur, um den Sachverhalt>> einer Klasse (eine Menge von Personen - im mathematischen Sinne) zu>> modellieren.>> Diese Beschreibung trifft auf die meisten Standard-Container zu. Was> zeichnet da das set jetzt speziell aus?
Ganz schlecht ist die Idee mit der set nicht. Es wurde oben irgendwas
mit E/R Diagramm erwähnt - in der UML2 sind Multiplizitäten mehrerer
Objekte (quasi Container) definiert per default als {unordered, unique}.
Also als unordered_set - gar nicht soweit weg. Und der Nutzen der
sortierten Speicherung ist für so nen Anwendungsfall wie hier auch
irgendwo da.
Wilhelm M. schrieb:>> Nun, du hast den Begriff ins Spiel gebracht.>> Ja, ich ich muss nicht die Semantik definieren!
Nun, wenn du Begriffe in der Diskussion einbringst, wäre es schon
logisch, wenn du und nicht jemand anders definiert, was du damit meinst.
>> Oder ein dem set übergebener Funktor. Ich würde da auch keinen>> programmweiten Vergleichsoperator für so eine Struktur haben wollen, der>> nur ein einzelnes Element davon vergleicht. Da diese Operation so nur>> für das set benötigt wird und ansonsten eher wenig sinnvoll ist, gehört>> sie auch nur dort hin.>> Ich denke, sie wird auch sonst benötigt, z.B. um sich eben eine Liste> alle Schüler in natürlicher Reihenfolge auszugeben.
Auch hier wieder die Frage: Was verstehst du unter der natürlichen
Reihenfolge von Personen? Für die Ausgabe will ich in der Regel nach
verschiedenen Kriterien sortieren können und nicht eine bestimmte
Sortierreihenfolge hardcoded im Programm stehen haben.
> Du weißt nicht, was das Kennzeichen einer Menge (mathematisch) ist?
Eine Menge hat keine besondere Ordnung und braucht daher keinen
Vergleich der Elemente miteinander, wie es ein std::set benötigt.
db8fs schrieb:> Na ja, ne std::set ist auch sortiert. Wenn man also den Funktor> überschreibt bzw. ein Prädikat definiert, sind alle in der set> gespeicherten Elemente (z.B. hier Schüler) gleich nach Vor- und> Nachnamen sortiert.
Ja, das ist ein Vorteil bei Nutzung eines set. Das geht aber auch nur,
wenn einem genau eine Sortierung ausreicht. Wenn ich stattdessen nach
Alter sortiert ausgeben will, muss ich den Inhalt in einen neuen
Container kopieren und umsortieren.
Rolf M. schrieb:> Ja, das ist ein Vorteil bei Nutzung eines set. Das geht aber auch nur,> wenn einem genau eine Sortierung ausreicht. Wenn ich stattdessen nach> Alter sortiert ausgeben will, muss ich den Inhalt in einen neuen> Container kopieren und umsortieren.
Stimmt. Musste aber auch sehen, dass Datenhaltung und
Ausgabeformatierung meistens aus sehr guten Gründen getrennt gemacht
werden. Und ne set zu traversieren, da biste glaube ich bei
logarithmischen Zugriff pro Element. Bei so „Popelkram“ mit mal
höchstens 30 Elementen im Container drinne, ist das doch näherungsweise
Konstantzeit beim lesen. Auch der lexikographische Vergleich für den
überschriebenen operator<() beim insert spielt kaum ne Rolle - wie
gesagt, bei großen Multiplizitäten sieht’s anders aus.
Rolf M. schrieb:> Wilhelm M. schrieb:>>> Nun, du hast den Begriff ins Spiel gebracht.>>>> Ja, ich ich muss nicht die Semantik definieren!>> Nun, wenn du Begriffe in der Diskussion einbringst, wäre es schon> logisch, wenn du und nicht jemand anders definiert, was du damit meinst.
Ich hatte oben einen Vorschlag gemacht, den Du offensichtlich überlesen
hast: Schülernummer.
>> Ich denke, sie wird auch sonst benötigt, z.B. um sich eben eine Liste>> alle Schüler in natürlicher Reihenfolge auszugeben.>> Auch hier wieder die Frage: Was verstehst du unter der natürlichen> Reihenfolge von Personen? Für die Ausgabe will ich in der Regel nach> verschiedenen Kriterien sortieren können und nicht eine bestimmte> Sortierreihenfolge hardcoded im Programm stehen haben.
Als natürliche Ordnung wird im (C++) Jargon die Anwendung des
operator<() als Ordnungsrelation verstanden (das allg. template
std::less verwendet operator<()).
>> Du weißt nicht, was das Kennzeichen einer Menge (mathematisch) ist?>> Eine Menge hat keine besondere Ordnung und braucht daher keinen> Vergleich der Elemente miteinander, wie es ein std::set benötigt.
Du hast den Punkt leider nicht verstanden: ein std::set modelliert eine
Menge im mathematischen Sinn. Deswegen heißt der DT auch so. In einer
Menge kommen Elemente nur genau {0,1}-mal vor bzw. es geht nur darum, ob
eine Element enthalten ist oder nicht. Dazu muss man Elemente auf
Gleichheit prüfen können (Ausgedrückt im Compare-Requirement von
std::set). Und dazu wiederum verwendet std::set<> dann std::less<>()
(und damit den operator<()), wobei es bei der Anwendung primär nicht um
die Ordnung geht, sondern um Gleicheit, die auf "<" zurückgeführt wird:
!(a < b) && !(b < a). Da in der Praxis std::set mit einem Binärbaum
(RB-Tree) realisiert wird (Performance), wird std::less<>() auch dafür
gebraucht.
Im Gegensatz dazu gibt es auch die Multimenge als std::multiset. Hier
können gleiche Elemente mehrfach vorkommen.
Aber das ist eigentlich gar nicht mein Punkt von oben gewesen. Sondern
mir ging es um die Modellierung des Sachverhaltes Schulklasse. Und eine
Schulklasse ist eine echte Menge von Schülern: ein bestimmter Schüler
kommt in einer Klasse nur {0,1}-mal vor. Und die Prüfung auf Gleichheit
kann std::set in log. Zeit durchführen (bzw. alle Operationen, die auf
op<() beruhen). Daher ist std::set sowohl semantisch wie auch technisch
zunächst die richtige Datenstruktur (und wie immer: natürlich kann es
Argumente dagegen geben, jedoch sind die bisher nicht vom TO genannt
worden).
Wilhelm M. schrieb:>> Nun, wenn du Begriffe in der Diskussion einbringst, wäre es schon>> logisch, wenn du und nicht jemand anders definiert, was du damit meinst.>> Ich hatte oben einen Vorschlag gemacht, den Du offensichtlich überlesen> hast: Schülernummer.
Ich hatte es ursprünglich gesehen, aber dann nicht mehr wiedergefunden
und wusste auch nicht mehr, von wem es kam. Hab aber auch nach ID statt
nach Schülernummer gesucht, und beim schnellen drüberschauen hat mein
Parser wohl einen Schülernamen draus gemacht. :)
Wie dem auch sei, wird die Schülernummer aber auch nicht garantieren,
dass jemand den selben Schüler nicht mit einer neuen Nummer noch ein
zweites mal hinzufügt. Es garantiert nur die Einmaligkeit der Nummer,
nicht die des "kompletten" Schülers.
> Du hast den Punkt leider nicht verstanden: ein std::set modelliert eine> Menge im mathematischen Sinn. Deswegen heißt der DT auch so. In einer> Menge kommen Elemente nur genau {0,1}-mal vor bzw. es geht nur darum, ob> eine Element enthalten ist oder nicht. Dazu muss man Elemente auf> Gleichheit prüfen können (Ausgedrückt im Compare-Requirement von> std::set).
Das funktioniert für einfache Datentypen gut, weil "Gleichheit" sich
dort auf den kompletten Inhalt bezieht, aber bei Strukturtypen bezieht
sie sich in der Regel (z.B. bei deiner Schülernummer) nur auf ein
Element der Struktur. Das ist für mich dann eher eine
Key/Value-Zuordnung mit der Schülernummer als Key und dem Rest, der
nicht in den Vergleich eingeht, als Value. Das wäre dann aber eher eine
std::map.
Rolf M. schrieb:> Wie dem auch sei, wird die Schülernummer aber auch nicht garantieren,> dass jemand den selben Schüler nicht mit einer neuen Nummer noch ein> zweites mal hinzufügt. Es garantiert nur die Einmaligkeit der Nummer,> nicht die des "kompletten" Schülers.
Na ja, er hat schon irgendwo recht - die Schülernummer kannste ja als
Art Primärschlüssel sehen. Kann ja auch zwei Leute mit dem selben Namen
geben. Ich stimm dir aber zu, dass das Vergleichsprädikat eigentlich so
gebaut sein müsste, dass es alle Felder der Struktur miteinander
vergleicht.
Rolf M. schrieb:> Wilhelm M. schrieb:>>> Nun, wenn du Begriffe in der Diskussion einbringst, wäre es schon>>> logisch, wenn du und nicht jemand anders definiert, was du damit meinst.>>>> Ich hatte oben einen Vorschlag gemacht, den Du offensichtlich überlesen>> hast: Schülernummer.>> Ich hatte es ursprünglich gesehen, aber dann nicht mehr wiedergefunden> und wusste auch nicht mehr, von wem es kam. Hab aber auch nach ID statt> nach Schülernummer gesucht, und beim schnellen drüberschauen hat mein> Parser wohl einen Schülernamen draus gemacht. :)> Wie dem auch sei, wird die Schülernummer aber auch nicht garantieren,> dass jemand den selben Schüler nicht mit einer neuen Nummer noch ein> zweites mal hinzufügt. Es garantiert nur die Einmaligkeit der Nummer,> nicht die des "kompletten" Schülers.
Das ist ein grundsätzliches Problem und hat nichts damit zu tun, ob
jetzt ein std::set<> oder ein anderer Container eingesetzt wird. Der TO
muss die Gleichheit definieren, wenn er es mit den anderen Attributen
macht und/oder keine Schülernummer verwendet, ist das auch ok.
>> Du hast den Punkt leider nicht verstanden: ein std::set modelliert eine>> Menge im mathematischen Sinn. Deswegen heißt der DT auch so. In einer>> Menge kommen Elemente nur genau {0,1}-mal vor bzw. es geht nur darum, ob>> eine Element enthalten ist oder nicht. Dazu muss man Elemente auf>> Gleichheit prüfen können (Ausgedrückt im Compare-Requirement von>> std::set).>> Das funktioniert für einfache Datentypen gut, weil "Gleichheit" sich> dort auf den kompletten Inhalt bezieht, aber bei Strukturtypen bezieht> sie sich in der Regel (z.B. bei deiner Schülernummer) nur auf ein> Element der Struktur. Das ist für mich dann eher eine> Key/Value-Zuordnung mit der Schülernummer als Key und dem Rest, der> nicht in den Vergleich eingeht, als Value. Das wäre dann aber eher eine> std::map.
Von mir aus auch eine std::map. Und auch dort darf der Schlüsselwert nur
{0,1}-mal vorkommen. Der technische Unterschied zu std::set<> ist
marginal.
Wie (!) der op<() realisiert wird, ist letztlich ein Detail, was der TO
für sich sinnvoll lösen muss. Wichtig war mir zu betonen, dass ein
Klasse eben eine Menge (mathematisch) ist: derselbe Schüler kann nicht
zweimal in der Klasse sein.