Schreiben Benutzerdefinierte I / O

Manche Programmierer sind keine großen Fans von der Standard-Operatoren für benutzerdefinierte C ++ Klassen zu überlasten. Dazu gehören benutzerdefinierte Kuvertiermaschinen und Extraktoren zu schreiben. Allerdings bevorzugen die Schaffung viele Programmierer ihre eigenen Eingabe / Ausgabe-Operatoren. Diese Einführung führt Sie durch die Schritte zur Erstellung zunächst eine benutzerdefinierte Inserter (Output) und dann eine benutzerdefinierte Extraktor (Eingang).

Erstellen Sie die Klasse

Erstellen Sie Ihre Klasse ohne Bezug auf den Eingang oder Ausgang. Einschließen bekommen und Set Funktionen zum Abrufen und einzelne Werte innerhalb eines Objekts zu setzen. Das folgende Beispiel ist ein Schüler Klasse für die Verwendung in diesem Abschnitt. Hier sind die Inhalte der Student.h Include-Datei:

Klasse Schüler {public: explicit Student (const char * pszName = "", long id = 0, Doppel gpa = 0,0) - // die get und set-Funktionen für diese classstring getName () const-Lücke setName (string e) -long getID () const-Lücke setId (long NID) -double getGPA () const-Leere setGPA (Doppel DGPA) -} -

Dieser Code enthält nicht die Umsetzung des bekommen und Set Methoden. Ihre Angaben sollten keinen Einfluss auf die Erstellung des Inserter und Extraktor.

Die Annahme, oben ist, dass die Set Funktionen ausführen, irgendeine Art von Kontrollen ungültige Eingabe zu erkennen - zum Beispiel, setGPA () sollten nicht zulassen, dass Sie außerhalb des Bereichs 0,0 bis 4,0 den Notendurchschnitt auf einen Wert zu setzen.

Erstellen Sie eine einfache Inserter

Der Inserter ausgeben soll ein Schüler Objekt entweder zur Anzeige oder in einer Datei. Die folgenden Prototyp würde dem hinzugefügt werden Student.h Include-Datei:

ostream operatorlt; lt; (Ostream out, Konst Studenten e) -

Der Inserter ist mit einem Rückgabetyp deklariert von ostream und gibt das Objekt an sie übergeben. Andernfalls würde ein Befehl wie die folgende nicht funktionieren:

cout lt; lt; "Mein Schüler ist" lt; lt; mein Schüler lt; lt; endl-

Das zurückgegebene Objekt aus lt; lt; mein Schüler wird in der verwendete lt; lt; endl dass folgt.

Die Umsetzung dieser Inserter ist unkompliziert (dies normalerweise in der erscheinen würde, Student.cpp Datei):

ostream operatorlt; lt; (Ostream out, Konst Studenten e) {int vorh = out.precision (2) -out lt; lt; s.getName () lt; lt; "(" lt; lt; s.getID () lt; lt; ")" Lt; lt; "/" lt; lt; (S.getGPA) - Rückkehr Ausgang}

Probieren Sie es aus und Anpassungen vornehmen

Also mal sehen, wie diese Inserter funktioniert:

// CustomIO - Entwicklung eines benutzerdefinierten Inserter und Extraktor # include #einschließen #einschließen #include "student.h" using namespace std-int main (int argc, char * pargs []) {Schüler s1 ( "Davis", 123456, 3.5) -cout lt; lt; s1 lt; lt; Endl-Schüler s2 ( "Eddins", 1, 3) -cout lt; lt; s2 lt; lt; Endl-cout lt; lt; "Drücken Sie die Eingabetaste, um fortzufahren ..." lt; lt; Endl-cin.ignore (10, ' n') - cin.get () - Rückkehr 0-}

Dies erzeugt die folgende Ausgabe:

Davis (123456) /3.5Eddins (1) / 3

Ist diese in Ordnung ist, dann sind Sie fertig. Doch für professionelle Anwendungen, möchten Sie wahrscheinlich ein paar Ausgaberegeln wie die folgenden (dies sind nur Beispiele) zu implementieren:

  • # 42-A der Schule, bei der Schüler-IDs sind sechs Ziffern lang. Wenn die Anzahl weniger als eine volle sechs Ziffern ist, dann sollte die Anzahl auf der linken Seite mit Nullen aufgefüllt werden.

  • # 42-Notendurchschnitt werden in der Regel mit zwei Stellen nach dem Komma angezeigt.

Glücklicherweise gibt es Kontrollen, die diese Funktionen implementieren. Seien Sie jedoch ein wenig vorsichtig, bevor die Ausgabeformatierung anpassen. Angenommen, der Inserter Sie zur Ausgabe eines Integer-Parameter ist in Hexadezimal-Format wollte. Die Nutzer der Inserter wäre sehr überrascht, wenn alle daran anschließende Ausgabe in hex erschien eher als dezimal. Deshalb ist es wichtig, zu notieren, was die vorherigen Einstellungen sind und sie wiederherstellen, bevor aus unserem Inserter zurück.

Eine Version der gussied Inserter sieht wie folgt aus:

ostream operatorlt; lt; (Ostream out, Konst Studenten e) {out lt; lt; s.getName () lt; lt; "(" - // Die ID erzwingen eine sechsstellige fieldchar prevFill = out.fill ( '0') zu sein - out.width (6) -out lt; lt; s.getID () - out.fill (prevFill) - // jetzt Ausgang der Rest des Studentint prevPrec = out.precision (3) -ios_base :: fmtflags prev = out.setf (ios_base :: showpoint) -out lt; lt; ")" lt; lt; "/" lt; lt; s.getGPA () - out.precision (prevPrec) -out.setf (i) -return Ausgang}

Sie können sehen, dass der Inserter den Namen des Teilnehmers gibt nach wie vor. Doch bevor der Schüler-ID ausgibt, setzt sie die Feldbreite auf sechs Zeichen und setzt den linken Füllzeichen zu 0.

Die Feldbreite gilt nur für die nächsten Ausgang, so dass es wichtig ist, diesen Wert zu setzen unmittelbar vor dem Feld, das Sie beeinflussen wollen. Weil es nur ein Feld dauert, ist es nicht notwendig, zu erfassen und die Feldbreite wiederherzustellen.

Sobald das Feld ausgegeben worden ist, wird das Füllzeichen wieder hergestellt, was es vorher war. Ebenso ist die Genauigkeit auf drei Stellen eingestellt und der Dezimalpunkt ist gezwungen, auf, bevor Sie den Notendurchschnitt angezeigt werden. Dies zwingt ein 3 anzuzeigen, wie 3.00.

Die resultierende Ausgabe sieht wie folgt aus:

Davis (123456) /3.50Eddins (000001) /3.00

Der Extraktor

Die Arbeit bei der Erstellung der Extraktor beginnt eigentlich mit dem Inserter. Beachten Sie, dass das Ausgabeformat bei der Erstellung für meine Student Objekt, fügte der Programmierer bestimmte spezielle Markierungen, die ein Extraktor, um sicherzustellen, erlauben würde, dass das, was er liest eigentlich ein Schüler.Zum Beispiel enthalten sie Klammern um die Schüler-ID und einen Schrägstrich zwischen ihm und dem GPA.

Meine Extraktor können diese Felder lesen, um sicherzustellen, dass es mit dem Datenstrom synchron ist zu bleiben. Im Überblick, muss der Extraktor die folgenden Aufgaben zu erfüllen:

  • # 42-Lesen Sie den Namen (eine Zeichenkette).

  • # 42-Lesen eine öffnende Klammer.

  • # 42-Lesen eine ID (eine ganze Zahl).

  • # 42-Lesen eine geschlossene Klammer.

  • # 42-Lesen einen Schrägstrich.

  • # 42-Lesen Sie die GPA (eine Fließkommazahl).

Es müssen dies tun, während die ganze Zeit bewusst zu sein, ob ein Formatierungsproblem auftritt. Was folgt, ist meine Version des Studenten Extraktor:

Istream Operator >> (istream in, Studenten s) {// lesen Werte (ignorieren Extra Leerzeichen) string name-long nID-double DGPA-char openParen = 0, closedParen = 0, Schrägstrich = 0-ios_base :: fmtflags prev = in.setf (ios_base :: skipws) -in >> Name >> openParen >> nID >> closedParen >> Slash >> DGPA-in.setf (i) - // wenn die Markierungen nicht übereinstimmen ... if (openParen = '(' || closedParen = ')' || Schrägstrich = '/') {// ... dann ist dies kein Rechts Studentin.setstate (ios_base :: failbit) -return in -}! // versuchen, die Schüler valuestry einstellen {s.setName (name) -s.setID (nID) -s.setGPA (DGPA) -} catch (...) {// etwas nicht stimmt - Flagge der failurein.setstate (ios_base :: failbit) -throw- } return Ein-}

Diese Inserter beginnt von jedem der erwarteten Felder zu lesen, wie zuvor in dem Flussdiagramm dargestellt. Wenn einer der Markierungszeichen fehlt (die offene Klammer, geschlossene Klammer und Schrägstrich), dann ist dies kein Rechts Schüler Objekt. Die zugelassene Möglichkeit für einen solchen Fehler anzuzeigen gesetzt, um die failbit in dem Eingabeobjekt. Damit werden alle weiteren Eingang zu stoppen, bis der Fehler behoben ist.

Der nächste Schritt ist, um tatsächlich diese Werte in die zu speichern versucht Schüler. Zum Beispiel sind alle der Marker sind vorhanden, aber der Wert für den Notendurchschnitt lesen kann 8,0 gewesen sein, ein Wert, der deutlich aus unserem vordefinierten Bereich von 0 bis 4,0 ist. Unter der Annahme, dass die Schüler Objekt enthält Kontrollen für Out-of-Range-Werte in seiner Set-Methoden, um den Anruf zu setGPA () wird eine Ausnahme werfen, die in den Extraktor gefangen wird. Auch hier setzt der Extraktor die failbit. Ob der Extraktor die Ausnahme rethrows obliegt dem Programmierer.

Es ist viel besser, auf die verlassen setGPA () Verfahren den Bereich Problem zu erkennen als eine solche Prüfung im Extraktor zu implementieren selbst. Besser lassen Sie die Schüler Objekt seinen eigenen Zustand zu schützen, als auf externe Funktionen zu verlassen.

Mit diesen neuen Methoden

Die folgende (sehr) einfach CustomIO Programm zeigt, wie diese Inserter und Dunstabzugsverfahren verwendet werden. In diesem Beispiel wird zusammen mit dem benutzerdefinierten Schüler Inserter und Extraktor sind verfügbar unter Dummies.com:

int main (int argc, char * pargs []) {Schüler s1 ( "Davis", 123456, 3.5) -cout lt; lt; s1 lt; lt; Endl-Schüler s2 ( "Eddins", 1, 3) -cout lt; lt; s2 lt; lt; Endl-cout lt; lt; "Input ein Student-Objekt:" - cin >> s1-if (cin.fail ()) {cout lt; lt; "Fehler beim Lesen der Schüler" lt; lt; Endl-cin.clear () -} else {cout lt; lt; "Lesen: " lt; lt; s1 lt; lt; endl-} cout lt; lt; "Drücken Sie die Eingabetaste, um fortzufahren ..." lt; lt; Endl-cin.ignore (10, ' n') - cin.get () - Rückkehr 0-}

Die Ausgabe von diesem Programm (bei Verwendung eines Rechts Student als Eingabe sieht wie folgt aus:

Davis (123456) /3.50Eddins (000001) /3.00Input Student Objekt: Laskowski (234567) /3.75Read: Laskowski (234567) /3.75Press eingeben, um fortzufahren ...

Beachten Sie, dass der Davis Student zeigt nur, wie wir wollten: die Schüler-ID ist umgeben von Klammern und der Notendurchschnitt erscheint mit zwei Stellen hinter dem Komma. Diese letztere gilt auch für die etwas problematisch Eddins Student.

Der Laskowski Student wird als Eingabe akzeptiert, weil sie alle die richtigen Zeichen eines gültigen Studenten hat: Klammern und Schrägstriche in den richtigen Stellen. Lesen Sie, was passiert, wenn wir etwas weglassen, aber:

Geben Sie ein Student Objekt: Laskowski 234567 / 3.75Error Lesung Student

Dieses kleine Programm zeigt zwei sehr wichtige Gewohnheiten, die Sie entwickeln sollten:

  • Überprüfen Sie die Flagge nicht durch den Aufruf fehlschlagen() nach jeder Eingabe.

  • Deaktivieren Sie die Fail-Flag, sobald Sie den Fehler durch den Aufruf anerkannt haben klar().

    Alle nachfolgenden Anforderungen für die Eingabe wird ignoriert, bis Sie die Flagge nicht gelöscht haben.

Menü