Hallo zusammen, ich schreibe grade an einem Programm, und da ich langsam aber sicher von C weg will übe ich mich dabei in C++. Die Syntax und das Objektorientierte Paradigma sind mir schon klar. Was mir eher fehlt sind Konzepte, wie man bestimmte Problemstellungen löst, die ich in "plain-C" wohl ganz anders lösen würde als es ein erfahrener C++ Programmierer macht. Konkret geht es darum: Ein "Unterprogramm" soll eine Textdatei parsen, dabei geht es um Datensätze (> 500 Stück, aber unbekannte Anzahl), diese bestehen aus einem symbolischen Name (= String) und ein paar Zahlen. Mir ist nicht ganz klar, wie ich das am geschicktesten anstelle. Wahrscheinlich eine Klasse anlegen, die einen "Parser" representiert. Dem Konstruktor würd ich den Dateinamen der Textdatei mitgeben. Ich denke, ich lege mir für einen Datensatz eine Struktur (oder interne Klasse) an, un sammle diese in einem std::vektor. Dann wirds aber schwammig. Soll der Konstruktor dann das ganze Parsen gleich in einem Rutsch erledigen? Wie komm ich an die Datein ran? Grundsätzlich wohl über einen Getter, aber dazu muss der Benutzer der Klasse erstmal wissen, wieviele Datensätze eingelesen wurden. Denkbar wäre auch, dem Getter einen String (also den Bezeichner des Datensatzes) zu übergeben, aber dann müsste ich im Getter jeweils alle Datensätze auf diesen String vergleichen. Erscheint mir recht aufwendig. Joa, also die technische Umsetzung würd ich schon hinkriegen, allerdings solls schon irgendwie C++ Style sein, sonst könnt ich auch gleich bei C bleiben... Wie würdet ihr das lösen?
Ein Parser ist wohl denkbar ungünstig als Einstiegsprojekt mit objektorientiertem Ansatz, den wie du schon merkst würgst du Dir irgendwas zu recht was dann deine verschiedenen Objekte rechtfertigt. Mach das mit c und such warte auf ein besseres Projekt für deine Metamorphose... gruß jibi
Was ich bei deinen Ausführungen vermisse, ist eine Klasse, um deine eigentlichen Daten zu repräsentieren. Dann weißt du auch, wie du an die Daten kommst. Die Klasse kapselt die Daten und bietet nach außen eine Schnittstelle zum Zugriff. Nach dem Parsen mußt du mit den Daten ja auch irgendwas machen. Der Parser könnte nun eine eigene Klasse sein. Dem übergibst du ein Datenobjekt, das dann vom Parser befüllt wird. So kannst du auch sehr leicht mehrere verschiedene Parser schreiben, von denen du dann per Polymorphie zur Laufzeit einen auswählst, z.B. um mehrere verschiedene Dateiformate zu unterstüzen. Hans schrieb: > Wie komm ich an die Datein ran? > Grundsätzlich wohl über einen Getter, aber dazu muss der Benutzer der > Klasse erstmal wissen, wieviele Datensätze eingelesen wurden. Warum? > Denkbar wäre auch, dem Getter einen String (also den Bezeichner des > Datensatzes) zu übergeben, aber dann müsste ich im Getter jeweils alle > Datensätze auf diesen String vergleichen. Erscheint mir recht aufwendig. Dafür gibt's std::map. Das scheint mir für deinen Anwendungsfall eh besser geeignet als ein std::vector.
Hans schrieb: Also erst mal stimme ich jibi zu: Ein Parser ist kein so gut geeignetes Beispiel. Denn die eigentlichen Parsing-Techniken sind da wie dort dieselben. > Ein "Unterprogramm" soll eine Textdatei parsen, dabei geht es um > Datensätze (> 500 Stück, aber unbekannte Anzahl), diese bestehen aus > einem symbolischen Name (= String) und ein paar Zahlen. > Mir ist nicht ganz klar, wie ich das am geschicktesten anstelle. Du hast prinzipiell erst mal eine Entscheidung zu treffen: willst du die Datei in einem Rutsch parsen und in späterer Folge dir das erkannte Stück für Stück abholen, oder willst du sukzessive Parsen, während du ausserhalb das Gelesene vom Parser abholst. Der Unterschied ist wie bei den XML-Parsern. Da gibt es 2 grundsätzlich verschiedene Ansätze. Das eine sind die DOM Parser, die die XML Datei in einem Rutsch in eine interne Baumrepräsentierung bringen, die dann ein weiterer Folge mit Operationen auf dem Baum abgefragt, behandelt, verändert und auch von dort wieder geschrieben werden können. Oder willst du XML mit SAX parsen, bei dem du den Parser auf die Datei löslässt und jedesmal, wenn der wieder einen Teil erkannt hat, macht der Parser Callbacks in deinen Code um dir mitzuteilen, dass (und was) er etwas erkannt hat. Das sind erst mal die beiden grundsätzlichen Möglichkeiten, die du hast. Beide haben ihre Vor- und Nachteile, die sich hauptsächlich (aus meiner Sicht) um den Speicherverbrauch drehen. Bei einem DOM-Parser baut nun mal der Parser selbst eine interne Datenstruktur auf, aus der du dir dann möglicherweise Ausschnitte holst, um sie dann in deiner eigenen Datenstruktur zu duplizieren. Ein SAX-Parser hingegen speichert selbst keine bzw. nur kaum Daten zwischen, sondern es obliegt dem Aufrufer dafür zu sorgen, dass er die Teile speichert, die er haben will. Das kann durchaus in der Form sein, dass du eine Klasse (bzw. ein paar Klassen) hast, die gemeinsam die Datenstruktur repräsentieren, die es zu füllen gilt. Das wäre deine erste Entscheidung, die du treffen musst. > Wahrscheinlich eine Klasse anlegen, die einen "Parser" representiert. > Dem Konstruktor würd ich den Dateinamen der Textdatei mitgeben. Kann man machen. Wird sogar sinnvoll sein. > Ich denke, ich lege mir für einen Datensatz eine Struktur (oder interne > Klasse) an, un sammle diese in einem std::vektor. Das kommt drauf an, was deine Daten Repräsentieren. Sind die alle vom gleichen Typ? > Dann wirds aber schwammig. Soll der Konstruktor dann das ganze Parsen > gleich in einem Rutsch erledigen? Finde ich keine so gute Idee. Wenn du dem Parser einen Filenamen mitgibst, dann ok - kann man so machen. Aber ich würde auf jeden Fall eine Funktion vorsehen, mit der ich explizit dem Parser auffordern kann: Jetzt File parsen. > Wie komm ich an die Datein ran? Das hängt jetzt davon ab, für welches Modell du dich entschieden hast. Baut der Parser selbst eine Datenstruktur auf oder nicht? Im ersten Fall: Wie ist deine Datenhierarchie? Ist das eine lineare Hierarchie oder ist das eine Baumstruktur? Welche Zugriffe benötigst du? Wie identifizierst du einen Datensatz? Hast du etwas, das als Schlüssel fungieren kann? > Grundsätzlich wohl über einen Getter Na ja. Das ist jetzt aber sehr plakativ vereinfacht. Ich will ein Auto bauen - was brauch ich? 4 Räder. No, na. PS: du solltest dir angewöhnen, in Klassen nicht in Form von Gettern und Settern zu denken. Die Gefahr ist groß, dass du dann nicht objektorientiert arbeitest sondern "C mit Klassen" erzeugst. Letzteres hat mit Objektorientierheit wenig zu tun. > aber dazu muss der Benutzer der > Klasse erstmal wissen, wieviele Datensätze eingelesen wurden. Muss er das? Warum kann er nicht einen 'Iterator' haben, der beim ersten Datensatz beginnt und den er laufend 'erhöht' um sich einen Datensatz nach dem anderen zu holen. Wenn er überhaupt sequentiell auf die Daten zugreifen können muss - was ja noch lange nicht gesagt ist. Denn: wer sagt dass der Parser sich darum kümmern muss? In erster Linie ist es ja eine Datenstruktur, die du um Werte befragst, wo ich immer diese Datenstruktur dann auch ihre Ergüsse her hat. Selbstverständlich kann bei einem DOM Ansatz der Parser selber so eine Datenstruktur beinhalten - muss aber nicht. Die kann genausogut auch vom Verwender kommen. > Denkbar wäre auch, dem Getter einen String (also den Bezeichner des > Datensatzes) zu übergeben, aber dann müsste ich im Getter jeweils alle > Datensätze auf diesen String vergleichen. Wieso musst du das? Das ist ja wohl ein Implementierungsdetail in der Datenhaltung des Parsers, wie der seine Daten speichert. Von aussen interessiert dich das herzlich wenig. Im Parser spricht auch nichts dagegen (wenn deine Strings eindeutig sind), dass die Datensätze als std::map gespeichert sind. > Wie würdet ihr das lösen? Erst mal, indem ich mich frage: Komme ich damit durch, dass der Parser die Daten hält? Kann ich den Parser an sich, gleich als meine 'Datenhaltung' verwenden? Ist DOM für mich das richtige, oder ist der SAX Ansatz das richtige, weil ich den Parser zb von einer Klasse 'ConfigData' benutze und es da völlig ausreicht, wenn der Parser dieser Klasse bescheid gibt, dass er einen Satz von der Datei gelesen hat und ConfigData sich dann die Werte dieses einen Satzes holt und in seiner eigenen Datenstruktur aufhebt.
:
Bearbeitet durch User
Rolf Magnus schrieb: > Was ich bei deinen Ausführungen vermisse, ist eine Klasse, um deine > eigentlichen Daten zu repräsentieren. Dann weißt du auch, wie du an die > Daten kommst. Die Klasse kapselt die Daten und bietet nach außen eine > Schnittstelle zum Zugriff. Nach dem Parsen mußt du mit den Daten ja auch > irgendwas machen. Stimmt, da hast du recht. Ich hätte bisher schon auch eine Klasse zur Datenrepresentation angelegt. Allerdings hätt ich diese intern im Parser verwendet, und dort auch einen Vektor (oder eben Map) dieser Klasse angelegt. Aber stimmt, macht natürlich mehr Sinn die geparsten Datensätze nach außen hin verfügbar zu machen. Nur stellt sich dann die frage, wem "gehören" die Datensätze dann? Der main()? Wenn Parser und Datensatz zwei getrennte Klassen sind, was liefert dann der Parser zurück? Einen Vektor?
>Finde ich keine so gute Idee. >Wenn du dem Parser einen Filenamen mitgibst, dann ok - kann man so >machen. Aber ich würde auf jeden Fall eine Funktion vorsehen, mit der >ich explizit dem Parser auffordern kann: Jetzt File parsen. Sogar sehr schlechte Idee, den du musst zum Parsen dann jedesmal ein Objekt erzeugen, was (mal ein bisschen großräumiger gedacht) den Speicher voll müllt. Rufst du das Ganze dann in einer Schleife auf, kann es dir sogar den Speicher dicht machen. Geb zu vielleicht ein bisschen viel Schwarzmalerei, aber wenn man es gleich richtig lernen will... Gruß Jonas
Hans schrieb: > Stimmt, da hast du recht. > Ich hätte bisher schon auch eine Klasse zur Datenrepresentation > angelegt. Allerdings hätt ich diese intern im Parser verwendet, und dort > auch einen Vektor (oder eben Map) dieser Klasse angelegt. > Aber stimmt, macht natürlich mehr Sinn die geparsten Datensätze nach > außen hin verfügbar zu machen. Nur stellt sich dann die frage, wem > "gehören" die Datensätze dann? Der main()? Demjenigen, der sie anlegt :-) > Wenn Parser und Datensatz zwei getrennte Klassen sind, was liefert dann > der Parser zurück? Einen Vektor? Ich würde in dem Fall sogar soweit gehen, dass der Parser da überhaupt nichts 'liefert'. Der Parser kriegt einen Filenamen und eine Datenstruktur und sein Job ist es diese Datenstruktur zu befüllen. Punkt. Aus. Egal wer dann auch immer den Parser benutzt, egal wie oft der Parser in einem Programm benutzt wird, derjenige der den Parser benutzen will, hat ein Datenstruktur-Objekt zur Verfügung zu stellen. Der Parser schreibt da hinein, so wie ein Promi auf das Papier schreibt, welches ihm der Fan unter die Nase hält. Der Fan ist für Papier und Bleistift zuständig, der Promi benutzt das nur.
:
Bearbeitet durch User
jibi schrieb: >>Finde ich keine so gute Idee. >>Wenn du dem Parser einen Filenamen mitgibst, dann ok - kann man so >>machen. Aber ich würde auf jeden Fall eine Funktion vorsehen, mit der >>ich explizit dem Parser auffordern kann: Jetzt File parsen. > > Sogar sehr schlechte Idee, den du musst zum Parsen dann jedesmal ein > Objekt erzeugen, was (mal ein bisschen großräumiger gedacht) den > Speicher voll müllt. Rufst du das Ganze dann in einer Schleife auf, kann > es dir sogar den Speicher dicht machen. > > Geb zu vielleicht ein bisschen viel Schwarzmalerei, aber wenn man es > gleich richtig lernen will... Im Prinzip ja. Wie schlimm das wirklich ist, hängt von den Details des Einsatzes ab. Ein Objekt in einer Schleife immer wieder zu erzeugen hat auch den Vorteil, dass du garantiert immer mit einem frischen Objekt anfängst. Bei Wiederverwendung von Objekten ist das immer so eine Sache. Ich würde mich da an den fstream Klassen orientieren. Die können auch beides: Sowohl Dateiname im Konstruktor als auch explizite open calls.
Hans schrieb: > Ich hätte bisher schon auch eine Klasse zur Datenrepresentation > angelegt. Allerdings hätt ich diese intern im Parser verwendet, und dort > auch einen Vektor (oder eben Map) dieser Klasse angelegt. Ich meinte nicht für einen einzelnen Datensatz, sondern für die gesamten Daten. Dieses Objekt hält dann intern eine Map aus Datensätzen. Ob ein Datensatz dann eine einfache Struktur oder wieder eine eigene Klasse sein soll, hängt von dessen Komplexität ab und davon, ob es typische Aktionen gibt, die man mit einem Datensatz immer wieder erledigt, und die man enstprechend als Memberfunktionen einbauen kann. > Aber stimmt, macht natürlich mehr Sinn die geparsten Datensätze nach > außen hin verfügbar zu machen. Nur stellt sich dann die frage, wem > "gehören" die Datensätze dann? Der main()? Wem hätten sie in deinem C-Programm gehört? > Wenn Parser und Datensatz zwei getrennte Klassen sind, was liefert dann > der Parser zurück? Einen Vektor? Er bekommt ein Datenobjekt, das er befüllt. Zurückliefern muß er dann gar nichts. Das Datenobjekt legt dann intern die Datensätze auf Anweisung des Parsers an. Da gibt's dann irgendein addEntry(...) in der Datenklasse, die vom Parser benutzt wird. Der greift dann also nie direkt auf die Daten zu.
Aber ich glaube viel wichtiger wäre ein theoretischer Ansatz wie man an ein Objektmodel kommt. Ich mach das meistens so: 1. Anwendungsfälle Ich sammle Anwendungsfälle die im Zusammenhang mit dem Programm stehen. In deinem Fall: Ich will eine Datei parsen. 2. Objektdiagram Ich überlege welche Objekte sich aus den Anwendungsfällen ergeben und überlege welche "Aufgaben" sie haben. Stift und Zettel sind gefragt. Nicht jede Überlegung ist gleich sinnvoll, da is Übung gefragt. In deinem Fall: Es muss eine Datei geparst werden, das macht ein Objekt, nennen wir es "Parser". Die Aufgabe des parsens übernimmt die Methode "parsen". Das wärs dann laut deinen Anforderungen soweit auch schon, was genau für Daten zu parsen sind (und da vielleicht eine Klassifizierung sinnvoll) hast du ja noch nicht verraten. Aber mit diesem, auch erstmal sehr einfach beschriebenen Ansatz kommt man an gute Ansätze, weil die Objektorientierung ein natürlicher Ansatz ist Probleme zu beschreiben und eigentlich eher für Menschen zu verstehen und zu modellieren als ein funktionaller. Man muss halt die Syntax verstehen, ist aber mit C# (nur als Beispiel!!!) einfacher als mit good old c++. Wünsch dir viel Erfolg! Gruß Jibi
So, danke euch erstmal für eure Beiträge. Vor allem natürlich Karl-Heinz, wie immer sehr informativ :-) Das mit dem Promi-Autogramm find ich grad gut. Denke so werd ichs erstmal probieren. Das hat grad noch einen Vorteil: hab ich zwei Dateien geb ich den beiden Parsern nacheinander dieselbe Datenstruktur (einen Vektor) und habe danach die Datensätze aus zwei Dateien in einer einzigen Datenbasis zur Verfügung. Der Anwender muss sich nicht mehr darum kümmern, woher denn die Daten nun stammen.
Rolf Magnus schrieb: > Hans schrieb: >> Ich hätte bisher schon auch eine Klasse zur Datenrepresentation >> angelegt. Allerdings hätt ich diese intern im Parser verwendet, und dort >> auch einen Vektor (oder eben Map) dieser Klasse angelegt. > > Ich meinte nicht für einen einzelnen Datensatz, sondern für die gesamten > Daten. Dieses Objekt hält dann intern eine Map aus Datensätzen. Ob ein > Datensatz dann eine einfache Struktur oder wieder eine eigene Klasse > sein soll, hängt von dessen Komplexität ab und davon, ob es typische > Aktionen gibt, die man mit einem Datensatz immer wieder erledigt, und > die man enstprechend als Memberfunktionen einbauen kann. > >> Aber stimmt, macht natürlich mehr Sinn die geparsten Datensätze nach >> außen hin verfügbar zu machen. Nur stellt sich dann die frage, wem >> "gehören" die Datensätze dann? Der main()? > > Wem hätten sie in deinem C-Programm gehört? > >> Wenn Parser und Datensatz zwei getrennte Klassen sind, was liefert dann >> der Parser zurück? Einen Vektor? > > Er bekommt ein Datenobjekt, das er befüllt. Zurückliefern muß er dann > gar nichts. Das Datenobjekt legt dann intern die Datensätze auf > Anweisung des Parsers an. Da gibt's dann irgendein addEntry(...) in der > Datenklasse, die vom Parser benutzt wird. Der greift dann also nie > direkt auf die Daten zu. >den wie du schon merkst würgst du Dir >irgendwas zu recht was dann deine verschiedenen Objekte rechtfertigt. Macht doch kein Sinn. >Mach das mit c und such warte auf ein besseres Projekt für deine >Metamorphose... >Bei Wiederverwendung von Objekten ist das immer so eine Sache. Klingt so nach Resten im Objekt nach Funktionsaufrufen und unsaubere Initialisierung vor dem Aufruf...das ist aber immer Schlamperei und ganz ehrlich, wer erwartet bei einem Parser, das ich ihn jedesmal erzeugen muss zum Parsen, mal abgesehen von dem Overhead der massiv ist wenn es um Performance geht...da muss ja Hauptspeicher angefordert werden, das kostet doch massiv cpu-Zeit... Ich kann nicht aufhören, muss mal andere Farben suchen. Gruß Jibi
jibi schrieb: > Aber ich glaube viel wichtiger wäre ein theoretischer Ansatz wie man an > ein Objektmodel kommt. Ich mach das meistens so: > > 1. Anwendungsfälle > > Ich sammle Anwendungsfälle die im Zusammenhang mit dem Programm stehen. > In deinem Fall: > > Ich will eine Datei parsen. > > 2. Objektdiagram > > Ich überlege welche Objekte sich aus den Anwendungsfällen ergeben und > überlege welche "Aufgaben" sie haben. Tip von mir: Die Aufgabe ohne Ansehen von Implementierungsdetails durchdenken. Alles was dabei an Hauptwörtern auftaucht, ist sehr oft schon mal ein heißer Kandidat für eine Klasse. Welche Hauptwörter kommen in der Aufgabenstellung vor: Parser Daten Datensatz ... Das sind alles schon mal heiße Kandidaten für eigene Klassen. Der nächste Schritt ist, Zuständigkeiten zu definieren. Welche Klasse ist wofür zuständig, was ist ihre Aufgabe? Dann überlegt man sich, wie die einzelnen Klassen zusammenspielen können, wenn sie 'Beamte' wären (keine Beleidigung beabsichtigt): Jede Klasse macht das und nur das, wofür sie zuständig ist. Fällt ein Teilbereich nicht in ihr definiertes Aufgabengebiet, dann gibt sie die Zuständigkeit an geeigneter Stelle ab. Ein Parser kann und will sich nicht um die Details kümmern, WIE die Daten gespeichert werden. Das ist Aufgabe der Datenklasse. Aufgrund dieser Überlegungen kristallisiert sich dann meist recht schnell ein erstes Klassenschema heraus, samt zugehörigen notwendigen Member-Funktionen. Betrachte die Member-Funktionen als 'Befehle' an eine Klasse etwas zu tun. Genau da liegt der Unterschied zur traditionellen Methode: die Einheit von Daten und Funktionen ist viel stärker betont. Wenn du nur in 'Gettern' und 'Settern' denkst, dann bist du noch nicht in der OOP Welt angekommen.
jibi schrieb: > Klingt so nach Resten im Objekt nach Funktionsaufrufen und unsaubere > Initialisierung vor dem Aufruf...das ist aber immer Schlamperei und ganz > ehrlich, wer erwartet bei einem Parser, das ich ihn jedesmal erzeugen > muss zum Parsen, mal abgesehen von dem Overhead der massiv ist wenn es > um Performance geht...da muss ja Hauptspeicher angefordert werden, das > kostet doch massiv cpu-Zeit... So schlimm ist das auch wieder nicht. Die Compiler sind so dämlich dann auch wieder nicht. Ob du jetzt auf einem Speicher deine eigene Init-Funktion aufrufst, oder ob der Compiler in einer Schleife auf immer demselben Speicherbereich einen Konstruktor Aufruf legt, schenkt sich nicht viel. Nur mit dem Unterschied, dass du einen Konstruktor/Destruktor-Aufruf nicht vergessen kannst.
:
Bearbeitet durch User
Hans schrieb: > Das mit dem Promi-Autogramm find ich grad gut. Anderes Beispiel. Aus der MFC Dort gibt es eine View Klasse, deren Job es ist, die Darstellung 'seiner Daten' durchzuführen. Dazu wird vom Framework an geeigneter Stelle eine Paint Methode aufgerufen. Die Paint Methode kriegt einen Device Context mit. Dieser Device Context steht für die Ausgabefläche. Die Paint Methode setzt also nicht selbst Pixel im Video-Speicher, sondern sie beauftragt den Device Context eine Linie zu zeichnen. Ob dieser Device Context in ein Fenster führt, ob das vielleicht am Drucker landet, am Plotter oder gar über Netzwerk auf einen ganz anderen Rechner führt, das braucht die Paint Methode nicht zu kümmern. Sie kann den Device Context nach seinen Eigenschaften befragen (wie groß er ist) und kann ganz abstrakt dem DC den Auftrag erteilen: zeichne Linie von-bis. Das eigentliche (physikalische) Zeichnen wird vom DC erledigt. Die Paint-Methode vom View wird auch vielleicht nicht alles selbst zeichnen. Vielleicht malt sie selber nur Dinge wie ein Koordinatenkreuz hin und eine Beschriftung und beauftragt dann die Datenstruktur (wieder unter Angabe des DC) sich dort auszugeben. Vielleicht gibt es aber auch ein eigenes Darstellungsobjekt, welches dieselben Daten einmal als Liniengrafik und malen kann, während ein anderes Methodenobjekt dieselben Daten als Tortengrafik malen kann. Vielleicht ist aber auch Koordinatenkreus und Beschriftung selbst wieder in einer Klasse gekapselt, so dass der View im Grunde nur die 'Arbeitsaufträge' an weitere Objekte weiterreicht, sich zu zeichnen. All das interessiert aber zb das Framework wieder nicht. Wenn es gilt etwas zu malen, dann wendet sie sich mit einem DC an den View mit dem Auftrag "da rein malen". Jede Klasse hat ihre Zuständigkeit. Was kann sie, welche Aufgabe hat sie? Und ja, das kann durchaus sein, dass es dann auch Klassen gibt, deren Aufgabe darin besteht, nach 'aussen hin' als Ansprechpartner zu fungieren und dann die Arbeitsaufträge intern zu verteilen. So wie in einer Firma: Als Mechaniker interessiert dich nicht, wie die Werkzeugausgabe intern funktioniert. Der Mann an der Ausgabe ist dein Ansprechpartner. Brauchst du etwas, wendest du dich an ihn. Ob der dann den Azubi ins Lager schickt oder selber geht; nach welchem System er das gesuchte Werkzeug findet, ist nicht dein Bier. Dafür bist du nicht zuständig. Genauso wie es den Mann am Schalter nicht interessiert, wie du das Werkzeug dann einsetzt. Natürlich hat man sich beim 'Design' der Werkzeugausgabe daran orientiert, wie die Werkzeugausgabe wohl meistens benutzt werden wird - man will ja schliesslich auch niemandem Prügel zwischen die Beine werfen. Aber im Grunde hast du mit der Theke in der Ausgabe eine ganz klare Trennlinie zwischen den Zuständigkeiten. Und das ist gut so, denn es erlaubt dir jeden der beiden Bereiche zu verändern, ohne das dieses den anderen Bereich großartig verändert.
:
Bearbeitet durch User
jibi schrieb: > Klingt so nach Resten im Objekt nach Funktionsaufrufen und unsaubere > Initialisierung vor dem Aufruf...das ist aber immer Schlamperei und ganz > ehrlich, wer erwartet bei einem Parser, das ich ihn jedesmal erzeugen > muss zum Parsen, mal abgesehen von dem Overhead der massiv ist wenn es > um Performance geht... Was für ein "massiver" Overhead soll denn da anfallen? > da muss ja Hauptspeicher angefordert werden, das kostet doch massiv cpu- > Zeit... So ein Unsinn. Selbst wenn man es dynamisch allokikert, kostet das kaum signifkiant Zeit. Abgesehen davon ist nicht damit zu rechnen, daß mehrere Millionen Dateien pro Sekunde geparst werden müssen.
Hallo zusammen, erstmal Danke für die vielen Vorschläge. Momentan arbeite ich an einer Lösung wie von KHB vorgeschlagen (Parser, Datensatz, Datenbasis). Klappt ganz gut. Nur Hab ich jetzt noch eine Schwierigkeit: ein Datensatz kann (aber muss nicht) einen untergeordneten Datensatz beinhalten, unter Umständen sogar mehrere. Wenn ich so drüber nachdenke, kann man sich das resultierende Gebilde fast wie eine Struktur in C vorstellen:
1 | class Datensatz{ |
2 | private:
|
3 | int a; |
4 | int b; |
5 | std::vector<UnterDatensatz> inner; |
6 | |
7 | public:
|
8 | void foo(); |
9 | }
|
10 | |
11 | class UnterDatensatz{ |
12 | public:
|
13 | void bar(); |
14 | }
|
15 | |
16 | void UnterDatensatz::bar(){ |
17 | Datensatz::foo(); |
18 | }
|
Mir ist klar, dass diese Lösung nicht klappt. Das Beispiel soll eher verdeutlichen, was ich vorhabe. Ein UnterDatensatz muss in der Lage sein, z.B. auf "a" oder "b" des Datensatzes zuzugreifen, der ihn beinhaltet. Ich denke, dass ist nur möglich wenn ich einem UnterDatensatz beim Erstellen (im Konstruktor?) eine Referenz auf den "Besitzer" übergebe. Oder gibts da eine andere Möglichkeit? Viele Grüße
Hans schrieb: > Mir ist klar, dass diese Lösung nicht klappt. Das Beispiel soll eher > verdeutlichen, was ich vorhabe. Ein UnterDatensatz muss in der Lage > sein, z.B. auf "a" oder "b" des Datensatzes zuzugreifen, der ihn > beinhaltet. > Ich denke, dass ist nur möglich wenn ich einem UnterDatensatz beim > Erstellen (im Konstruktor?) eine Referenz auf den "Besitzer" übergebe. Das wäre der normale Weg. Du baust damit eine Art Objekthierarchie auf. Jeder UnterDatensatz bekommt einen Zeiger auf seinen "parent", also den Datensatz, dem er gehört. Eine Referenz wird nicht gehen, da std::vector Zuweisbarkeit vorraussetzt, und das funktioniert (ohne schmutzige Tricks) nicht für Objekte, die Referenzen beinhalten. Aufpassen mußt du aber, wenn du den Datensatz irgendwo kopierst. Denn wenn du keine entsprechenden Vorkehrungen triffst, zeigen sonst die Parent-Zeiger der UnterDatensätze in der Kopie nicht auf diese, sondern aufs Original, also nicht mehr auf das Objekt, zu dem sie gehören. Also entweder nie einen Datensatz kopieren (und sicherstellen, daß du es auch nicht versehentlich tust) oder einen Kopierkonstruktor und einen Zuweisungsoperator implementieren, der die Zeiger aller UnterDatensätze anpasst.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.