In diesem Abschnitt werden die geschützten und virtuellen Teile der Klasse streambuf beschrieben. Diese Teile sind insbesondere bei der Nutzung abgeleiteter Klassen von Interesse. #include <iostream.h> typedef long streamoff, streampos; class ios public: bin, tabexp}; // und viele weitere Deklarationen, siehe ios ... } ; class streambuf { public:
protected:
public:
}; | |||||||||||||||||||||||
streambuf-Objekte implementieren die Pufferabstraktion, die im Abschnitt zu sbufpub beschrieben wird. Die Klasse streambuf selbst enthält nur die grundlegenden Elemente zur Zeichenmanipulation; üblicherweise wird im Programm eine aus streambuf abgeleitete Klasse eingesetzt. In diesem Abschnitt wird die Schnittstelle beschrieben, die der Programmierer zum Kodieren einer abgeleiteten Klasse benötigt. Die Elementfunktionen von streambuf können - vereinfacht - in zwei Gruppen unterteilt werden. Die nicht-virtuellen Funktionen manipulieren streambuf-Objekte so, wie dies in abgeleiteten Klassen erforderlich ist. Die Beschreibungen zeigen Implementationsdetails,die in einer öffentlichen Schnittstelle nicht erscheinen sollten. Durch die virtuellen Funktionen können abgeleitete Klassen als Spezialfälle der Klasse streambuf entwickelt werden, die auf die entsprechenden Quellen und Ziele (von Zeichenübertragungen) ausgelegt sind. Die Beschreibung der virtuellen Funktionen enthält auch die "Pflichtaufgaben" der virtuellen Funktionen in den abgeleiteten Klassen. Wenn sich die virtuellen Funktionen erwartungsgemäß verhalten, ist auch das korrekte Verhalten der öffentlichen Schnittstelle von streambuf sichergestellt. Anderenfalls kann das Verhalten von streambuf allerdings Unregelmäßigkeiten aufweisen. In diesem Fall verhält sich auch ein iostream-Objekt (oder anderer Programmcode), das vom korrekten streambuf-Verhalten abhängt, anders als erwartet. Bei den folgenden Beschreibungen wird angenommen, dass:
Konstruktorenstreambuf() Ein leerer Puffer wird angelegt, der einer leeren Folge entspricht. streambuf(char * b, int len) Ein leerer Puffer wird angelegt und der Reservierungsbereich auf len Bytes, beginnend bei b, gesetzt. Die Bereiche get, put und der ReservierungsbereichDie protected-Elemente von streambuf stellen die Schnittstelle zu den abgeleiteten Klassen dar, die in drei Bereichen (Byte-Felder) strukturiert sind. Die Bereiche werden gemeinsam von Basis- und abgeleiteten Klassen verwaltet und werden get, put und Reservierungsbereich (oder Puffer) genannt. Die Bereiche get und put sind üblicherweise nicht identisch, können aber den Reservierungsbereich überlappen. Der Reservierungsbereich ist in erster Linie eine Ressource, aus der Kapazität für die Bereiche put und get belegt werden kann. Die Bereiche get und put werden beim Einfügen und Entnehmen von Zeichen in den/aus dem Puffer verändert, der Reservierungsbereich ist aber im allgemeinen festgelegt. Die Bereiche werden durch eine Reihe von char*-Werten definiert. Die Pufferabstraktion wird in Form von Zeigern beschrieben, die zwischen die Zeichenzeigen. Die char*-Werte zeigen aber auf char-Objekte (Zeichen). Das Erstellen einer Beziehung mit den char*-Werten kann man sich so vorstellen, dass der Zeiger vor das Byte gerichtet ist, auf das er eigentlich zeigt. Funktionen zur Untersuchung von Zeigernchar * ptr=sb→base() Ein Zeiger auf das erste Byte des Reservierungsbereiches wird geliefert. Der Bereich zwischen sb->base() und sb->ebuf() ist der Reservierungsbereich. char * ptr=sb->eback() Ein Zeiger auf die untere Grenze von sb->gptr() wird geliefert. Der Bereich zwischen sb->eback() und sb->gptr() ist zum "Zurückschieben" von Zeichen verfügbar. char * ptr=sb->ebuf() Ein Zeiger auf das erste Byte hinter dem Reservierungsbereich wird geliefert. char * ptr=sb->egptr() Ein Zeiger auf das erste Byte hinter dem Bereich get wird geliefert. char * ptr=sb->epptr() Ein Zeiger auf das erste Byte hinter dem Bereich put wird geliefert. char * ptr=sb->gptr() Ein Zeiger auf das erste Byte des Bereiches get wird geliefert. Die verfügbaren Zeichen befinden sich zwischen sb->gptr() und sb->egptr(). Das nächste zu entnehmende Zeichen ist *(sb->gptr()), sofern sb->egptr() nicht kleiner als oder gleichsb->gptr() ist. char * ptr=sb->pbase() Es wird ein Zeiger auf die Basis des put-Bereichs geliefert. Die Zeichen zwischen sb->pbase() und sb->pptr() sind im Puffer gespeichert und wurden noch nicht verbraucht. char * ptr=sb->pptr() Es wird ein Zeiger auf das erste Byte des Bereiches put geliefert. Der Bereich zwischen sb->pptr() und sb->epptr() ist der put-Bereich. Hier sind Zeichen gespeichert. Funktionen zum Setzen der ZeigerHinweis Als Hinweis darauf, daß ein Bereich (get, put oder Reservierungsbereich) nicht existiert, sollten alle damit verknüpften Zeiger auf Null gesetzt werden. void sb->setb(char * b, char * eb, int i) base() und ebuf() werden auf b bzw. eb gesetzt. Die Angabe i legt fest, ob der void sb->setp(char * p, char * ep) pptr() wird auf p, pbase() auf p und epptr() auf ep gesetzt. void sb->setg(char * eb, char * g, char * eg) eback() wird auf eb, gptr() auf g und egptr() auf eg gesetzt. Weitere nicht-virtuelle Elementfunktionenint i=sb->allocate() Es wird versucht, einen Reservierungsbereich anzulegen. Wenn bereits ein Reservierungsbereich existiert oder wenn sb->unbuffered() ungleich 0 ist, liefert die Funktion allocate() den Wert 0, ohne dass eine weitere Operation vorgenommen wurde. Beim erfolglosen Versuch der Speicherzuweisung liefert allocate() den Wert EOF; anderenfalls (also bei erfolgreicher Zuweisung) wird der Wert 1 zurückgegeben. Die Funktion allocate() wird von keiner der nicht-virtuellen Elementfunktionen von streambuf aufgerufen. int i=sb->blen() Die Größe (in char-Einheiten) des aktuellen Reservierungsbereichs wird geliefert. void dbp() Die Funktion schreibt Pufferstatus-Informationen im EBCDIC-Format direkt in die Einheit, die mit dem Dateideskriptor 1 verknüpft ist. Diese Funktion wurde zu Testzwecken eingefügt; es sind keine Spezifikationen zum Format der Ausgabe vorgegeben. void sb->gbump(int n) gptr() wird um den Wert n inkrementiert, wobei n positiv oder negativ sein kann. Es wird nicht geprüft, ob der neue Wert von gptr() in den erlaubten Grenzen liegt. void sb->pbump(int n) pptr() wird um den Wert n inkrementiert; n kann positiv oder negativ sein. Es wird nicht geprüft, ob der neue Wert von pptr() in den erlaubten Grenzen liegt. void sb->unbuffered(int i) Es gibt eine private-Variable, die den Status der Pufferung von sb beschreibt. sb->unbuffered(i) setzt den Wert dieser Variable auf i, während sb->unbuffered() Virtuelle ElementfunktionenVirtuelle Funktionen können in abgeleiteten Klassen erneut definiert werden, um das Verhalten von streambuf-Objekten genauer festzulegen. In diesem Abschnitt wird das Soll-Verhalten der virtuellen Funktionen in den abgeleiteten Klassen beschrieben. Im nächsten Abschnitt finden Sie eine Beschreibung des Verhaltens dieser Funktionen in der Basisklasse streambuf. int i=sb->doallocate() Die Funktion wird aufgerufen, wenn allocate() bestimmt, dass Speicherbereich benötigt wird. doallocate() wird zum Aufruf von setb() verwendet, damit ein Reservierungsbereich bereitgestellt oder - wenn dies nicht möglich ist - der Wert EOF geliefert wird.Der Aufruf erfolgt nur, wenn sb->unbuffered() und sb->base() gleich 0 sind. int i=overflow(int c) Diese Funktion "verbraucht" Zeichen. Wenn c nicht das Zeichen EOF ist, muß die Funktion overflow() c entweder sichern oder verbrauchen. Die Funktion wird üblicherweise aufgerufen, wenn der Bereich put gefüllt ist und das Speichern eines weiteren Zeichens versucht wurde. Der Aufruf ist aber auch in anderen Fällen möglich. Üblicherweise werden durch den Aufruf die Zeichen zwischen pbase() und pptr() verbraucht, setp() wird aufgerufen, um einen neuen put-Bereich anzulegen, und c wird über die Funktion sputc() gespeichert, wenn c!=EOF ist. sb->overflow() sollte zur Anzeige eines Fehlers den Wert EOF liefern, anderenfalls sollte eine andere Rückgabe erfolgen. int i=sb->pbackfail(int c) Die Funktion wird aufgerufen, wenn eback() gleich gptr() ist und versucht wurde, das Zeichen c "zurückzuschieben". Wenn diese Situation korrekt gehandhabt werden kann(beispielsweise durch Repositionieren in einer externen Datei), sollte pbackfail() das Zeichen c liefern; anderenfalls sollte EOF zurückgegeben werden. streampos pos=sb->seekoff(streamoff off, seekdir dir, int mode) seekoff() ist eine öffentliche virtuelle Elementfunktion. Eine detaillierte Beschreibung finden Sie im Abschnitt Abschnitt sbufpub. Die get- und/oder put-Zeiger werden neu positioniert. Diese Positionierung wird nicht von allen abgeleiteten Klassen unterstützt. streampos pos=sb->seekpos(streampos pos, int mode) seekpos() ist eine öffentliche virtuelle Elementfunktion. Eine detaillierte Beschreibung finden Sie im Abschnitt Abschnitt sbufpub. Die get- und/oder put-Zeiger werden neu positioniert. Diese Positionierung wird nicht von allen abgeleiteten Klassen unterstützt. streambuf * sb=sb->setbuf(char * ptr, int len) Das Feld, das bei ptr beginnt und len byte lang ist, wird als Reservierungsbereich angeboten. Üblicherweise wird ein Aufruf als Anforderung interpretiert, sb ohne Pufferung anzulegen, wenn ptr oder len gleich 0 sind. Die abgeleitete Klasse kann - muss aber nicht - diesen Bereich nutzen. Auch die Anforderung des nicht gepufferten Status kann akzeptiert oder ignoriert werden. setbuf() sollte sb liefern, wenn die Anforderung erfüllt wird; anderenfalls sollte der Wert 0 zurückgegeben werden. int i=sb→sync() sync() ist eine öffentliche virtuelle Elementfunktion. Eine detaillierte Beschreibung finden Sie im Abschnitt sbufpub. int i=sb→underflow() Die Funktion wird zur Bereitstellung von Zeichen eingesetzt. Hierdurch kann beispielsweise eine Situation geschaffen werden, in der ein Bereich get vorliegt, der nicht leer ist. Erfolgt der Aufruf, wenn Zeichen im Bereich get vorliegen, sollte die Funktion das erste verfügbare Zeichen liefern. Im Fall eines leeren Bereichs get sollte zunächst einnicht leerer Bereich get angelegt und das nächste Zeichen (das im Bereich get verbleiben sollte) geliefert werden. Wenn keine weiteren Zeichen verfügbar sind, sollte die Funktion underflow() den Wert EOF liefern und einen leeren Bereich get hinterlassen. Die Standarddefinitionen der virtuellen Funktionenint i=sb→streambuf::doallocate() Die Zuweisung von Speicherkapazität an einen Reservierungsbereich wird über den Operator new versucht. int i=sb->streambuf::overflow(int c) streambuf::overflow() sollte behandelt werden, als wäre das Funktionsverhalten nicht definiert. Das bedeutet, dass abgeleitete Klassen die Funktion immer definieren sollten. int i=sb->streambuf::pbackfail(int c) Beim Auftreten eines Fehlers wird EOF und bei erfolgreicher Ausführung wird c geliefert. streampos pos=sb->streambuf::seekpos(streampos pos, int mode) Es wird sb->seekoff(streamoff(pos),ios::beg,mode) geliefert. Zur Definition der Repositionierung in einer abgeleiteten Klasse ist es häufig nur nötig, seekoff() zu definieren und die ererbte Funktion streambuf::seekpos() einzusetzen. streampos pos=sb->streambuf::seekoff(streamoff off, seekdir dir, int mode) EOF wird geliefert. streambuf * sb=sb->streambuf::setbuf(char* ptr, int len) Die Anforderung wird erfüllt, wenn kein Reservierungsbereich vorhanden ist. int i=sb->streambuf::sync() Der Wert 0 wird geliefert, wenn der Bereich get leer ist und keine nicht verbrauchten Zeichen vorliegen. Andernfalls wird EOF geliefert. int i=sb->streambuf::underflow() streambuf::underflow() sollte behandelt werden, als wäre das Funktionsverhalten | |||||||||||||||||||||||
BEISPIEL | Das Programm gibt die Adresse des Basisbereiches einer aus streambuf abgeleiteten Klasse aus. Das Programm ist ein Beispiel zur Darstellung von Speicherinhalten. Es hätten andere trivial-Elementfunktionen, wie get_base, verwendet werden können, die die Adressen der Bereiche get und put liefern. #include <iostream.h> const int N = 20; class trivial : public streambuf { int a; /* Einige Beispieldaten in einer Klasse */ public: trivial() : streambuf(new char[ N], N) { /* trivial-Konstruktor wird durch streambuf-Konstruktor */ /*definiert*/ a = 0; }; ̃trivial() {}; /* Annahme, daß der streambuf-Destruktor den N Byte großen */ /* Reservierungsbereich löscht */ char * get_base() { /* Diese Funktion wird benötigt, da die Elementfunktion */ /* streambuf::base() geschützt ist. */ /* Qualifikation streambuf:: wird nicht benötigt, da */ /* Gültigkeitsbereich ok ist. */ return base(); }; }; int main() { trivial test_var; cout << (void *) test_var.get_base() << endl; /* Umwandlung nach void *, damit cout nicht den Inhalt des */ /* ersten Byte des Reservierungsbereiches anzeigt. */ return 0; } Das Ergebnis der Programmausführung ist: 0xc6008 % CCM0998 Verbrauchte CPU-Zeit: 0.0005 Sekunden Beachten Sie, dass der im Zeiger gespeicherte Wert variieren kann und dass cout ein Standardformat für Zeigerwerte aufweist. | ||||||||||||||||||||||
BESONDERHEITEN | |||||||||||||||||||||||
Die Konstruktoren wurden als public deklariert, um die Kompatibilität zum älteren stream-Paket zu erhalten. Sie sollten als protected deklariert sein. Die Schnittstelle für nicht gepufferte Operationen ist unhandlich. Die Entwicklung der virtuellen Funktionen underflow() und overflow() mit korrektem Verhalten für ungepufferte streambuf-Objekte ist ohne besondere Vorkehrungen kompliziert. Zudem gibt es für virtuelle Funktionen keine Möglichkeit, gesondert auf get- und put-Operationen zureagieren, die mehrere Zeichen umfassen. Obwohl die öffentliche Schnittstelle der Klasse streambuf in Zeichen und Bytes beschrieben wird, arbeitet die Schnittstelle zu abgeleiteten Klassen mit char-Objekten. Da eine Entscheidung bezüglich des Typs der tatsächlichen Datenzeiger getroffen werden musste, erschien es einfacher, dies durch die Datentypen der protected-Elemente wiederzugeben, als alle Elemente doppelt zu implementieren (in einer char- und unsigned char-Version). Vielleicht hätten auch alle Einsatzbereiche von char* über eine typedef-Anweisung realisiert werden sollen. | |||||||||||||||||||||||
SIEHE AUCH | |||||||||||||||||||||||