Das Setzten bzw. Löschen der Formatflags kann über die beiden
Elementfunktionen setf() und unsetf() erfolgen.
Für die Einstellungen des Formatstatus stehen zudem mehrere Operatoren
zur Verfügung.
setf() zum Einstellen betsimmter Formatflags gibt es in zwei Varianten, d.h. genauer es existieren zwei überladene Instanzen für diese Funktion:
setf(long);
setf(long,long);
Das erste Argument kann dabei entweder ein Formatflag oder ein Formatbitfeld
sein, das zweite Argument ist auf jeden Fall ein Formatbitfeld, das aus
mehreren Formatflags betseht. Die zur Verfügung stehenden Formatflags
und Format-Bitfelder wurden bereits im einzelnen vorgestellt, eine Zusammenfassung
befindet sich im Anhang (#### LINK hinsetzen)
Anhang hat folgende Getsalt:
Formatflags:
Flag | Bedeutung |
ios::showbase | Zahlenbasis ausgeben |
ios::showpoint | Format für Dezimalpunkt ausgeben |
ios::dec | Dezimalschreibweise |
ios::hex | Hexadezimalschreibweise |
ios::oct | Oktalschreibweise |
ios::fixed | Dezimalschreibweise für Fließkommawerte |
ios::scientific | Exponetialschreibweise für Fließkommawerte |
Bitfeld | Bedeutung | Flags |
ios::basefield | ganzzahlige Basis | ios::hex |
ios::oct | ||
ios::dec | ||
ios::floatfield | Fließkommaausgabe | ios::fixed |
ios::scientific |
cout.setf(ios::oct, ios::basefield);
Die Elementfunktion setf(long,long) setzt zunächst das
Format-Bitfeld -- in unserem Beispiel ios::basefield -- auf 0,
d.h. alle Flags dieses Bitfeldes sind deaktiviert. Anschließend wird
das im ersten Argument -- hier: ios::oct -- angegebende
Bitfeld gesetzt.
Neben dem Setzen des Flags an sich hat diese Funktion aber noch eine
weitere Eigenschaft, die nicht unerwähnt bleiben sollte. setf()
besitzt als Rückgabewert einen long-Integerwert, dem der
vorherige Zustand entspricht. Damit kann der alte Zustand gespeichert werden
und man besitzt eine einfache Möglichkeit, den alten Zustand der gesetzten
Flags wiederherzustellen. Hierzu muß lediglich der Wert des alten
Zustandes als erstes Argument in einem Aufruf von setf(long, long)
angegeben werden:
int wert=2044;
long alte_basis;
cout.setf(ios::oct, ios::basefield);
cout << "Der Wert betraegt: " << wert <<
endl; // Ausgabe in octal
// alte Zahlenbasis speichern und Umschalten auf hexadezimale
Darstellung
alte_basis= cout.setf( ios::hex, ios::basefield);
cout << "Der Wert betraegt: " << wert <<
endl; // Ausgabe in hexadezimal
// alte Einstellugen wiederherstellen
cout.setf(alte_basis, ios::basefield);
cout << "Der Wert betraegt: " << wert <<
endl; // Ausgabe in octal
Man könnte sich nun die Frage stellen, wieso wir im obigen Beispiel für das Umschalten in den Hexadezimal-Modus nicht einfach den Befehl setf(long), also
cout.setf(ios::hex);
verwendet haben. Der Grund hierfür ist folgende: bei dieser Instanz der setf()-Funktion mit nur einem Parameter wird der alte Zustand nicht aufgehoben. Hat man z.B. wie im Beispiel die Ausgabe ganzzahliger Werte zunächst auf oktal festgesetzt, d.h. genauer: ios::basefield auf Oktalschreibweise gesetzt, so wird bei Verwendung von cout.setf(ios::hex) nun zusätzlich die Hexadezimalschreibweise eingestellt, d.h. das Bitfeld ios::basefield ist auf mehr als eine Zahlenbasis festgesetzt! In diesem Fall wird standardmäßig die Dezimalschreibweise ausgewähl, und damit entspricht das erzeilte Ergebnis auf jeden Fall nicht dem gewünschten.
Ebenso einfach wie das Zurücksetzten auf eine alte Einstellung erfolgt auch das Zurücksetzten der Einstellungen auf die Defaultwerte:
cout.setf(0, <format-bitfeld>); // Zurücksetzten auf Default
istream& operator>>(istream& str, bruch& b)
/*
Folgende Eingabeformate für einen
Bruch sollen unterstützt werden (d stehe hierbei für double)::
d
- nur Zähler angegeben
(d)
- nur Zähler angegeben, nun aber in Klammern gesetzt
(d, d)
- ausführliche/vollständige Schreibweise mit zaehler, nenner
(in Klammern gesetzt)
Ein Bruch bestehe aus Zähler
und Nenner, zudem stehe für die bruch-Klasse ein Konstruktor bruch(zaehler,
nenner) bereit.
*/
{
double zaehler = 0, nenner = 1;
// Defaultwerte des Bruchs
char c = 0;
str >> c; // Einlesen
eines Characters aus dem Stream
if (c == '(') {
str >> zaehler >> c;
// Double in Variable zaehler einlesen, danach nächstes Zeichen lesen
if (c == ',') str >> nenner
>> c;
if (c != ')') str.clear(ios::badbit);
// Fehlerstatus des Streams setzen
}
else {
str.putback(c); // stelle
das bereits eingelesenes Zeichen an den Stream zurück
str >> zaehler;
}
// Falls Streamstatus in Ordnung ist, erzeuge
den Bruch:
if (str) br = bruch(zaehler,nenner);
return br;
}
Im Beispiel wird versucht, die verschiedenen Eingabeformate zu erkennen und Zähler und Nenner den Formaten entsprechend korrekt einzulesen. Die putback()-Methode wird hier dazu eingesetzt, daß erste gelesene Zeichen in den Stream zurückzustellen, falls es nicht die erste Klammer '(' war. Dann war dieses Zeichen nämlich schon der Zähler, den man abschließend einliest. Dieses Zurückstellen in den Stream ist deshalb nötig, weil aus dem Eingabestream zunächst ein Character-Typ eingelesen wurde. Stellt sich jedoch heraus, daß es sich nicht um '(' handelt, so muß es sich -- da ein Objekt der Klasse bruch eingelesen wird -- um einen double-Typ handeln. Eine einfache Zuweisung der Art
zaehler = <eingelesenes Zeichen>;
ist daher nicht möglich, da es sich um zwei verschiedene Datentypen handelt. Folglich wird das eingelesen Zeichen wieder in den Stream gestellt, so daß es anschließend als double korrekt eingelesen werden kann.
Der implizite Zeiger this
Zugegeben: bisher haben wir keine Zeiger kennengelernt, dies wird erst im Kapitel ########?????##### nachgeholt. An dieser Stelle kommen wir aber dennoch nicht um die Erklärung eines Zeigers herum, dem impliziten this-Zeiger.Hinter einem Zeiger verbirgt sich eine spezielle Variable, deren Wert der Adresse eines Objektes im Speicher entspricht. Mit Hilfe eines Zeigers kann man auf ein Objekt indirekt zugreifen. Mehr wollen wir an dieser Stelle über Zeiger gar nicht verraten; merken Sie sich nur, daß man den this-Zeiger mit *this ansprechen kann.
Wozu brauche ich den this-Zeiger: Motivation ##### sieher bereits erstelltes Kapitel!!!
Was ist der this-Zeiger?
Nachdem wir anhand des Beispiels einen ersten Einsatzbereich des this-Zeigers
gesehen haben, wollen wir uns nun ein wenig ausführliche mit diesem
Konstrukt beschäftigen.
Jedes Objekt einer bestimmten Klasse verwaltet seine eigene Kopie der
Datenelemente einer Klasse. Die Elementfunktionen müssen sich die
verschiedenen Objekte einer Klasse jedoch teilen, sie rufen jeweils dieselbe
Kopie einer bestimmten Elementfunktion auf. Jede Elementfunktion einer
Klasse ist einmal vorhanden, d.h. sie ist nicht innerhalb des Klassenobjektes
gespeichert. Wäre letzteres der Fall, würde die Anzahl der Funktionskopien
mit jedem definierten Objekt enorm anwachsen.
Damit steht man vor einem Problem:
Wie ist die Elementfunktion, von der nur eine Instanz existiert, mit
den einzelnen Datenobjekten der vesrchiedenen Objekte, die diese Funktion
aufrufen, verbunden? Die Elementfunktion setze(int i, int j, double d)
der matrix-Klasse muß ja auf die Elemente der verschiedenen matrix-Objekte,
die diese Methode aufrufen, Zugriff haben, da sie diese u.U. verändern
soll.
Die Lösung dieses Problems liegt in dem Zeiger this. Jede Elementfunktion einer Klasse enthält automatisch einen Zeiger auf ihren Klassentyp mit dem Namen this.Der this-Zeiger enthält dabei die Adresse desjenigen Klassenobjektes, über welches die Elementfunktion aufgreufen wurde. Und damit hat jede Elementfunktion auch Zugang zu den speziellen Datenelementen des aufrufenden Objektes.
Um die genaueren Möglichkeiten, die sich mit dem This-Zeiger ergeben,
verstehen zu können, benötigt man das Grundwissen über
Zeigertypen (Pointer), daß aber erst in einem der nachfolgenden Kapiteln
bereitgestellt wird. Wir beschränken uns daher auf den Einsatz des
this-Zeigers, wie wir ihn im einführenden Beispiel kennengelernt haben.
Mit
return *this;
kann das aufrufende Objekt von der aufgerufenen Methode zurückgeliefert
werden.