namespace cpp {}

C++ lernen, kennen, anwenden

Benutzer-Werkzeuge

Webseiten-Werkzeuge


kennen:operator

Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen angezeigt.


kennen:operator [2020-07-27 09:55] (aktuell) – angelegt - Externe Bearbeitung 127.0.0.1
Zeile 1: Zeile 1:
 +====== Operatoren ======
  
 +> Ausdrücke sind eine Folge von Operatoren und Operanden, die eine Berechnung bewirken. Ausdrücke können einen Wert ergeben und können Nebenwirkungen (Seiteneffekte) hervorrufen.
 +>>---  C++ Standard ISO 14882, Kap. 5 Abs. 1
 +
 +
 +C und C++ sind reich an Operatoren.
 +Nicht alle sind unmittelbar einsichtig, u.a. deshalb wirkt C so kryptisch auf Sprachfremde.
 +Operatoren werden auf Operanden (Variablen, Konstanten, Ausdrücke) angewendet und verknüpfen diese: 
 +<code cpp>
 +punkte[mannschaft] += gewonnen ? 2 : 0
 +(c=getchar()) != EOF
 +(*d++ = *s++) != 0
 +</code>
 +
 +Die [[#Wirkung]] von Operatoren wird unten erklärt.
 +Die Verknüpfung geschieht in einer bestimmten
 +[[#Rangfolge]].
 +Bei arithmetischen Ausdrücken wie 
 +<code cpp>
 +  x = 2.1 + 3*4/5 - 6.3
 +</code>
 +erfolgt Punktrechnung vor Strichrechnung (höherer Rang). 
 +Die rechte Seite (Rechtswert, rvalue) der [[#Zuweisung]]
 +muss vollständig berechnet sein,
 +ehe deren Wert an die links stehende Variable (Linkswert, lvalue) zugewiesen wird.
 +Die Änderung eines gespeicherten Variablenwertes ist ein Seiteneffekt.
 +Leerzeichen neben den Operatoren ändern die Bedeutung nicht,
 +können aber die Lesbarkeit des Quelltextes erhöhen.
 +
 +Der Wert von 12/5 ist nicht 2.4, sondern die Ganzzahl 2, da beide Operanden ganzzahlig sind.
 +12.0/5 ergibt hingegen 2.4 als gebrochene Zahl, genauer: einen Wert, der dieser Zahl möglichst nahe kommt.
 +
 +Die [[#Rangfolge|Bindungsrichtung]] 
 +(Assoziativität) legt fest,
 +in welcher Richtung gleichrangige Operationen gruppiert werden:
 +<code cpp>
 +  z = 10 - 5 - 2
 +</code>
 +wird linksassoziativ als ''(10-5)-2'' behandelt und nicht als ''10-(5-2)''.
 +Klammern erzwingen eine Gruppierung:
 +<code cpp>
 +  std::cout << (2<=3) << '\n';
 +</code>
 +Nur wenige Operatoren erzwingen eine bestimmte Auswertefolge der Teilausdrücke.
 +Compiler können
 +<code cpp>
 +  i = 1;
 +  a[i] = i++; 
 +</code>
 +unterschiedlich auswerten: Es kann ''a[1] = 1'' oder ''a[2] = 1'' bedeuten.((
 +Ab [[begriffe#C++17]] ist die Reihenfolge festgelegt:
 +Alle Seiteneffekte rechts der Zuweisung werden zuerst ausgewertet.
 +Dennoch ist es schlechter Programmierstil, wenn man darüber nachdenken muss.
 +))
 + 
 +
 +===== Rangfolge =====
 +> Effektives Schreiben (oder Lesen) in C ohne Kenntnis dieser Regeln ist unmöglich. Bitte studiere die Rangtabelle jeden Abend beim Zähneputzen.
 +>> ---[ [[http://www.nr.com|Numerical Recipes]]]
 +
 +|Rang|Bindung|Zweck|Operator|
 +|1|L| [[#Bereichsauflösung]] | ''::''|
 +|2|L| Postfix-Inkrement/-Dekrement \\ Funktionsaufruf,Typumwandlung \\ [[#Zugriff]] auf Element| ''%%++ --%% \\ //Typ//() \\ %%[] . ->%%''|
 +|3|R| Präfix-Inkrement/-Dekrement\\ [[#Einstellige Operatoren]]\\ Typecast, Speicher | ''%%++ --%% \\ ! ~ - + * &\\ (//Typ//) new delete''|
 +|4|L| [[#Elementzeiger]]| ''.* %%->*%%''|
 +|5|L| [[#arithmetische Operatoren|Punktrechnung]]  | ''* / %''|
 +|6|L| [[#arithmetische Operatoren|Strichrechnung]] | ''+ -''|
 +|7|L| [[#Bitweise Operatoren|Bitschieben]]| ''%%<< >>%%''|
 +|8|L| [[kennen:include:compare|3-Wege-Vergleich]] ([[:modern_cpp#C++20]])| ''%%<=>%%''|
 +|9\\ 10|L| [[#Vergleiche]]| ''%%< <= >= >%% \\ == !=''|
 +|11\\ 12\\ 13|L| [[#Bitweise Operatoren|bitweise]] UND \\ XOR \\ ODER | ''& \\ ^ \\ |''|
 +|14\\ 15|L| [[#Logische Operatoren|logisch]] UND \\ ODER| ''&& \\ ||''|
 +|16|R| [[#Entscheidungsoperator]], [[#Zuweisung]]\\ mit ''+ - * / % %%<< >>%% & | ^'' als //op// | ''? :   =''  //op//''=''  |
 +|17|L| [[#Listenoperator|Liste]]| '',''|
 +
 +| L | linksassoziativ: Gruppierung von links nach rechts |''a+b+c'' bedeutet ''(a+b)+c''|
 +| R | rechtsassoziativ: Gruppierung von rechts nach links |''a=b=c'' bedeutet ''a=(b=c)''|
 +
 +
 +===== Wirkung =====
 +==== Bereichsauflösung ====
 +Manche Bezeichner gehören einem bestimmten [[.:keywords#namespace|Namensbereich]] 
 +oder einer Klasse an.
 +Sie werden mit ''Namensbereich::Bezeichner'' qualifiziert.
 +<code cpp>
 +std::cout
 +std::istream::get()
 +::globaler_Name
 +</code>
 +Alle Funktionen, Klassen, Objekte der Standard-Bibiothek sind im Namensbereich ''std'' definiert.
 +Der globale Namensbereich enthält die Bezeichner außerhalb von geschweiften Klammern.
 +Der Bereichsauflösungsoperator vor ''::globaler_name'' 
 +erlaubt auf den globalen Namen zuzugreifen selbst dann, 
 +wenn er durch einen lokalen gleichnamigen Bezeichner verdeckt ist.
 +
 +==== Zugriff ====
 +Komponenten, Attribute und Methoden von [[.:keywords#struct|Strukturvariablen]] 
 +und Objekten werden mit ''variable.komponente'' ausgewählt.\\
 +
 +Der Ausdruck ''%%zeiger->komponente%%'' mit dem Pfeiloperator ist zu ''(*zeiger).komponente'' gleichwertig.
 +<code cpp>
 +  struct Punkt { int x, y; };
 +  Punkt a; 
 +  a.x = 2;
 +
 +  Punkt* a_ptr = &a;
 +  a_ptr->y = 3;
 +</code>
 +Mit eckigen Klammern wird auf das Element ''feld[index]'' 
 +eines Feldes zugegriffen (Feldoperator oder Indexoperator). 
 +<code cpp>
 +  int feld[10];
 +  feld[0] = 1;
 +</code>
 +Runde Klammern ''()'' gruppieren den eingeschlossenen Ausdruck. 
 +Als Funktionsoperator schliessen sie die Funktionsparameter (Argumente) ein.
 +<code cpp>
 +  x = (2+3)*5;
 +  y = sqrt(2); 
 +</code>
 +Die Postfix-Operatoren ''%%++ --%%'' sind
 +[[#arithmetische Operatoren]]. 
 +Sie haben höheren Rang als andere [[#einstellige Operatoren]]:
 +<code cpp>
 +  *d++ = *s++;  // erhöht Zeiger s, nicht Wert *s
 +</code>
 +Der Schlüsselwort-Operator [[.:keywords#typeid|typeid]] 
 +ist zur Laufzeit-Typabfrage vorgesehen.
 +<code cpp>
 +  std::cout << typeid(Punkt).name() << '\n';  // #include <typeinfo>
 +</code>
 +==== Einstellige Operatoren ====
 +Die Präfix-Operatoren ''%%++ --%%'' und die Vorzeichen ''+ -''
 +sind [[#arithmetische Operatoren]]. 
 +Negieren kann je nach Ziel mit
 +  * dem Minus-Vorzeichen ''-'' [[#arithmetische Operatoren|arithmetisch]],
 +  * dem Nicht-Operator ''!'' [[#Logische Operatoren|logisch]]
 +  *  oder dem Komplement-Operator ''~'' [[#Bitweise Operatoren|bitweise]] ausgeführt werden. 
 +
 +Der Adressoperator ''&'' ermittelt die Adresse der folgenden Variable.
 +Der Inhaltsoperator ''*'' greift auf den Wert zu, 
 +auf dessen Adresse der dahinterstehende Zeiger-Ausdruck verweist.
 +Er dereferenziert den Zeiger.
 +Adressoperator und Inhaltsoperator sind zueinander invers.
 +<code cpp>
 +  int  i = 1;
 +  int* p = &i;
 +  *p = 2; 
 +</code>
 +Mit den Operatoren [[.:keywords#new|new]] 
 +und [[.:keywords#delete|delete]]
 +wird dynamischer Speicher angefordert bzw. wieder freigegeben.
 +<code cpp>
 +  p = new int[n];
 +  // ... benutze Feld mit n int-Werten
 +  delete [] p;
 +</code>
 +Werden mit ''new'' mehrere Objekte als Feld angefordert,
 +muss die Freigabe mit ''delete []'' gekennzeichnet werden.
 +Bei Einzelobjekten ist weder bei ''new'' 
 +noch bei ''delete'' eine eckige Klammer zu setzen.
 +Die Nichtbeachtung führt zu schwerwiegenden Fehlern in der Speicherverwaltung.
 +
 +Der Ausdruck ''sizeof(x)'' gibt den Speicherbedarf des Operanden in Byte an.
 +
 +==== Elementzeiger ====
 +Methodenzeiger (und Attributzeiger) ''Typ::*methode''
 +auf Methoden (Attribute) einer Struktur / Klasse 
 +sind eigentlich keine Zeiger, sondern "nur" Adress-Offsets innerhalb eines Objekttyps.
 +Sie ermöglichen die Auswahl einer Methode (eines Attributs), 
 +ohne schon ein Objekt benennen zu müssen.
 +Die Ausführung (der Zugriff) erfolgt dann über ''variable.*methode''
 +bzw. ''%%objektzeiger->*methode%%''
 +([[.:beispiel:memptr|Beispiel]]).
 +
 +Das Eintragen (Registrieren) von Zeigern auf Funktionen 
 +für einen späteren Aufruf wird als Callback-Technik bezeichnet.
 +Diese Technik findet sich häufig in ereignisgesteuerten Programmen (Bedienoberflächen).
 +Ein solcher Callback-Mechanismus für Objekte
 +wird in der ''[[.:lib:mem_fn]]''-Schablone der Standardbibliothek ausgenutzt.
 +
 +==== Arithmetische Operatoren ====
 +Die arithmetischen Operatoren ''+ - * /'' 
 +haben die gewohnte mathematische Bedeutung, mit einer Ausnahme:
 +Das Ergebnis von ''13/5'' ist die Ganzzahl 2, da beide Operanden ganzzahlig sind, nicht 2.6.
 +Operationen führen nicht aus dem Zahlbereich der Operanden heraus.
 +Ist mindestens ein Operand von einem gebrochenzahligen Typ (''float'' oder ''double''), 
 +so ist es auch das Ergebnis.
 +Sind die Typen der Operanden unterschiedlich, 
 +wird vor der Operation der "kleinere" Typ an den "größeren" Typ angeglichen.
 +<code cpp>
 +  std::cout << 13/5   << '\n';  // 13 div 5 ergibt 2
 +  std::cout << 13.0/5 << '\n';  // 13.0/5.0 ergibt 2.3
 +</code>
 +Der Divisionsrest lässt sich mit dem Modulo-Operator ''%'' nur bilden, 
 +wenn beide Operanden ganzzahlig sind:
 +<code cpp>
 + std::cout << 13%5 << '\n';  // 13 mod 5 ergibt Rest 3
 +</code>
 +Die Operatoren ''+'' und ''-'' finden sich
 +  *  [[#Einstellige Operatoren|einstellig]] als Vorzeichen von Zahlwerten, 
 +  * zweistellig für Summen- und Differenzen von Zahlen und
 +  * zur Adressrechnung in der Zeigerarithmetik.
 +<code cpp>
 +  int feld[10];
 +  int* p = feld+5;             // Adresse von feld[5]
 +  std::cout << p-feld << '\n'; // 5 Elemente Distanz
 +</code>
 +Inkrement ''++'' und Dekrement ''--'' erhöhen bzw. 
 +senken einen Variablenwert (Zahl oder Zeiger) um eins.
 +In zusammengesetzten Ausdrücken ist die Stellung der Operatoren 
 +''++'' und ''--'' zum Operanden wichtig:
 +
 +| Postfix  | ''j = i++;'' | j erhält den alten Wert von i|
 +| Präfix   | ''j = ++i;'' | j erhält den neuen Wert von i|
 +
 +==== Bitweise Operatoren ====
 +Bitoperationen erfordern ganzzahlige Typen 
 +(''char'', ''short'', ''int'', ''long'', ''unsigned'').
 +Zur Illustration werden 8 bit dargestellt
 +mit dem höchstwertigen Bit (Wert 128) auf der linken Seite.
 +
 +Das Bit-Komplement (Einer-Komplement, ''compl'') kehrt alle Bits um:
 +<code cpp>
 +  10001011   i
 +  01110100  ~i
 +</code>
 +Schiebeoperationen verrücken die Bits vom Wert des linken Operanden 
 +um die rechts angegebene Anzahl von Stellen in der angegebenen Pfeilrichtung.
 +In Schieberichtung fallen Bits heraus, auf der anderen Seite werden Nullen aufgefüllt.
 +Achtung! Bei vorzeichenbehafteten Typen ist das Rechtsschieben maschinenabhängig.
 +Einige Maschinen füllen Einsen auf, wenn das Vorzeichenbit gesetzt ist.
 +<code cpp>
 +  10001011   i
 +  01011000   i << 3 (Linksschieben)
 +  00100010   i >> 2 (Rechtsschieben)
 +</code>
 +Bitweises UND (''bit_and'') setzt im Ergebnis nur die Stellen auf 1,
 +die in beiden Operanden auf 1 stehen:
 +<code cpp>
 +  00001111   i
 +  10101010   j
 +  00001010   i & j  
 +</code>
 +Bitweises ausschließendes ODER (Exklusiv-ODER, ''xor'') setzt im Ergebnis jene Stellen auf 1,
 +deren Wert sich in beiden Operanden unterscheidet:
 +<code cpp>
 +  00001111   i
 +  10101010   j
 +  10100101   i ^ j  
 +</code>
 +Bitweises ODER (''bit_or'') setzt im Ergebnis all die Stellen auf 1,
 +die in mindestens einem Operanden auf 1 stehen:
 +<code cpp>
 +  00001111   i
 +  10101010   j
 +  10101111   i | j  
 +</code>
 +
 +==== Vergleiche ====
 +Vergleiche liefern einen Wahrheitswert. 
 +Die Ordnungsrelationen ''%%< <= >= >%%'' sind selbsterklärend.
 +Der Test auf Gleichheit muss mit zwei Gleichheitszeichen ''=='' geschrieben werden,
 +weil das einzelne ''='' eine [[#Zuweisung]]
 +auslöst.
 +Ungleichheit ''!='' kann als "nicht gleich" gelesen werden.
 +
 +==== Logische Operatoren ====
 +Wahrheitswerte ([[.:keywords#bool|bool]]) 
 +und Ausdrücke lassen sich logisch verknüpfen
 +und liefern wieder einen Wahrheitswert.
 +Ausdrücke ungleich Null gelten als wahr (), nur 0 als falsch ().
 +Weitere logische Verknüpfungen lassen sich durch Kombination bilden 
 +([[.:beispiel:bool]]).
 +
 +|      ''not'' |  ''and'' |  ''or'' ||  Antivalenz (XOR) | Äquivalenz |  Folge A => B|
 +| ''A'' | ''B'' | ''!B'' | ''A&&B'' | ''A||B'' || ''bool(A)!=bool(B)''| ''bool(A)==bool(B)''| ''B||!A''|
 +| 0 | 0 |  1  |  0    |  0 ||  0 |  1 |  1|
 +| 0 | 1 |  0  |  0    |  1 ||  1 |  0 |  1|
 +| 1 | 0 |      0    |  1 ||  1 |  0 |  0|
 +| 1 | 1 |      1    |  1 ||  0 |  1 |  1|
 +
 +Logische Ausdrücke werden garantiert von links nach rechts ausgewertet,
 +jedoch nur soweit, bis das Ergebnis feststeht (Kurzschlussverfahren, engl. short circuit evaluation):
 +<code cpp>
 +  if (n && z/n == 0) // bei Nenner 0 wird Division nicht ausgeführt
 +  {
 +    std::cout << "echter Bruch" << '\n';
 +  }
 +</code>
 +
 +==== Entscheidungsoperator ====
 +Der bedingte Ausdruck ''bedingung ? dann : sonst'' enthält den einzigen dreistelligen Operator.
 +Ist der erste Teilausdruck wahr, wird der folgende Dann-Zweig ausgewertet, 
 +andernfalls der Wert des Sonst-Zweiges berechnet.
 +Der Dann-Ausdruck und der Sonst-Ausdruck müssen typgleich sein.
 +Das Ergebnis kann (sollte) weiterverwendet werden:
 +<code cpp>
 +  x = a < b ? a : b;  // Minimum von a und b
 +</code>
 +
 +==== Zuweisung  ====
 +Zuweisungen ändern Variablenwerte. 
 +Die linke Seite der Zuweisung (das Ziel der Zuweisung) muss nicht unbedingt eine Variable sein.
 +Es kann ein komplizierterer Ausdruck sein, 
 +der einen veränderbaren Speicherplatz (lvalue) anspricht.
 +Die Zuweisung erfolgt von rechts nach links. 
 +<code cpp>
 +  punkte[mannschaft] = 0;  
 +  x = y = z = 1;
 +</code>
 +In Zuweisungsketten haben am Ende haben alle links stehenden Variablen den rechten Wert angenommen.
 +Das ist möglich, weil eine Zuweisung einen Wert erhält, nämlich den neuen Wert der linken Variable.
 +Zuweisungen können Teil eines Ausdrucks sein.
 +<code cpp>
 +  while ((c = std::cin.get()) != '\n' && std::cin) // bis zum Zeilenende auffressen
 +  {
 +  }
 +</code>
 +Statt ''x = x op (y)'' kann '' x op= y'' geschrieben werden,
 +wenn ''op'' ein zweistelliger arithmetischer (''+ - * / %''
 +oder Bit-Operator (''%%<< >>%% & ^ |'') ist.
 +<code cpp>
 +  x += 10;  // x wird um 10 erhöht,
 +  x -= 5;   // x wird um 5 vermindert,
 +  x *= 2;   // x wird verdoppelt,
 +  x /= y+2; // x wird geteilt durch y+2, usw.
 +</code>
 +Die Kurzschrift (Verbundzuweisung) kommt der natürlichen Sprechweise näher als
 +<code cpp>
 +  x = x+10; // Ermittle die Summe von x und 10 und weise diese x zu !:-)
 +</code>
 +
 +==== Listenoperator ====
 +Das Komma steht zwischen Parametern / Argumenten von Funktionen, 
 +auch zwischen den Werten einer Aufzählungs- oder Initialisiererliste.
 +Hinter einem Typ können mehrere Bezeichner vereinbart werden, durch Komma getrennt.
 +(Aber: Jedem Funktionsparameter muss sein eigener Typ vorangestellt sein.)
 +<code cpp>
 +  int i = 1, j = 2;
 +  int folge[] = { 1, 2, 3 };
 +  double winkel = atan2(y, x);
 +</code>
 +In Zählschleifen mit mehreren Steuervariablen kann das Komma auftreten:
 +<code cpp>
 +  for (i = 0, j = strlen(s)-1; i < j; i++, j--) // Zeichenkette s umdrehen
 +  {
 +    swap(s[i], s[j]);
 +  }
 +</code>
 +Ausdrücke, durch Komma getrennt, werden streng von links nach rechts ausgewertet.
 +Ergebnistyp und Ergebniswert sind die des rechtesten Operanden.
 +Alle vorhergehenden Ergebnisse werden bis auf Seiteneffekte verworfen.
 +Aber: In welcher Reihenfolge die Argumentwerte vor einem Funktionsaufruf 
 +berechnet und abgelegt werden, ist nicht definiert, sprich: compilerabhängig.
 +
 +===== Operatoren überladen =====
 +Für die eingebauten Typen ist die [[#Wirkung]]
 +der Operatoren festgelegt.
 +Im Zusammenhang mit neuen Typen (Aufzählungen, Klassen und Strukturen)
 +kann für Operatoren eine neue Bedeutung eingeführt (überladen) werden.
 +Als Einschränkungen gelten:
 +  * Es können keine neuen Operatoren "erfunden" werden. 
 +  * Rangfolge und Parameteranzahl der Operatoren sind nicht änderbar.
 +  * Die Operatoren '':: . .* ?:'' sind gar nicht, ''%%= [] () ->%%'' nur als [[#Operatormethoden|Operatormethode]] überladbar.
 +  * Überladene Operatoren sollten ihrer gewohnten Bedeutung nahekommen (Etikette: keine Überraschungen), z.B.:
 +    * Der Zeigeroperator ''%%->%%'' sollte einen Zeiger auf einen zusammengesetzten Typ liefern.
 +    * Vergleiche sollten Wahrheitswerte liefern.
 +
 +Überladene Operatoren werden als Funktionen mit besonderen Namen definiert.
 +Ihr Name beginnt mit dem Schlüsselwort [[.:keywords#operator|operator]],
 +gefolgt von dem Operator-Symbol.
 +Operatoren können als [[#Operatorfunktionen|globale Funktion]] 
 +oder als [[#Operatormethoden|Methoden]] überladen werden
 +(s. [[.:beispiel:operator]]).
 +
 +Besondere Regeln gilt es zu beachten bei
 +[[#Ein- und Ausgabeoperatoren]],
 +[[#Zuweisungsoperator]],
 +[[#Inkrement und Dekrement]],
 +[[#Feldindex]],
 +[[#Funktionsklammern]] und
 +[[#Typumwandlung]].
 +
 +==== Operatormethoden ====
 +<code cpp>
 +  u += v;     // nutzt Methode  u.operator+=(v);
 +</code>
 +Operatormethoden erhalten den ersten Operanden implizit (*this).
 +Bei zweistelligen Operanden muss nur das rechte Argument ("right hand side" = rhs) 
 +als Parameter übergeben werden.
 +<code cpp>
 +struct Vec
 +{
 +  Vec& Vec::operator+=(Vec const& rhs);
 +  // ...
 +};
 +</code>
 +Operatoren, die Zugriff auf (private) Daten eines Linkswertes (lvalue) benötigen,
 +z.B. ''+='', lassen sich meist besser als Methode implementieren.
 +
 +==== Operatorfunktionen ====
 +<code cpp>
 +  w = u + v;  // nutzt Funktion w = operator+(u, v);
 +</code>
 +Operatorfunktionen werden außerhalb von Klassen (global) vereinbart.
 +Benötigt eine Operatorfunktion Zugriff auf private Klassenbereiche, 
 +muss die Klasse sie zur Freundin erklären
 +(Schlüsselwort [[.:keywords#friend|friend]]).
 +In diesem Beispiel ist das unnötig, da ein zugehöriger Verbundoperator exisiert:
 +<code cpp>
 +Vec operator+(Vec const& u, Vec const& v)
 +{                        // ausführlich: Vec sum = u;
 +  return Vec(u) += v;    //              sum += v;  
 +}                        //              return sum;
 +</code>
 +Die paarweise Bereitstellung von Operatorfunktion ''+'' und Verbundoperator ''+='' 
 +führt zu effizientem Code und
 +gehört zur Etikette (Prinzip der minimierten Überraschung).
 +Interessanterweise basiert die Operatorfunktion auf dem Verbundoperator und nicht umgekehrt!
 +
 +Zweistellige Operatorfunktionen arbeiten mit gleichberechtigten linken und rechten Operanden.
 +Damit sind implizite Konversionen des linken Operanden möglich:
 +<code cpp>
 +  std::string s  = "def";
 +  std::string s2 = "abc" + s;  // Konversion std::string("abc");
 +</code>
 +
 +==== Ein- und Ausgabeoperatoren ====
 +C++ nutzt die Schiebeoperatoren ''%%>>%%'' und ''%%<<%%'' als Ein- und Ausgabeoperatoren
 +für die Datenströme ''istream'' und ''ostream''.
 +Für nutzerdefinierte Typen müssen diese als [[#Operatorfunktionen]]
 +mit dem Datenstrom als linksseitigem Argument
 +überladen werden und den Strom nach der Operation zurückliefern,
 +damit die Verkettung möglich ist.
 +
 +<code cpp>
 +std::ostream& operator<<(std::ostream& os, Vec const& v)
 +{
 +  return os << v.x << ' ' << v.y << ' ' << v.z;
 +}
 +
 +std::istream& operator>>(std::istream& is, Vec& v)
 +
 +  // ... sinngemäß
 +  return is;
 +}
 +</code>
 +Der Eingabeoperator muss das Objekt als Referenz übernehmen (Rückgabe!).
 +Er sollte dessen Werte erst ändern, 
 +wenn alle notwendigen Daten erfolgreich gelesen wurden (Etikette),
 +damit Objekte nicht nicht in einen ungültigen Zustand geraten.
 +
 +==== Zuweisungsoperator ====
 +Wenn nicht anders definiert, 
 +wird jeder zusammengesetzte Typ als Byte-Block kopiert (sog. flache Kopie).
 +Zuweisungsoperatoren sollten dann definiert werden, 
 +wenn auch ein [[.:begriffe#Kopierkonstruktor]]
 +erforderlich ist. 
 +Zumeist verwaltet eine Klasse dann dynamischen Speicher oder andere Ressourcen
 +([[.:beispiel:deepcopy|Beispiel tiefes Kopieren]]).
 +Zuweisungsoperatoren werden nicht vererbt.
 +
 +==== Inkrement und Dekrement ====
 +Erhöhen und Absenken können als vorangestellte (Präfix-) 
 +und nachgestellte (Postfix-) Operatoren definiert werden:
 +
 +<code cpp>
 +class Date
 +{
 +public:
 +  Date& operator++();    // Präfix:  ++date;
 +  Date  operator++(int); // Postfix: date++;
 +
 +  // ...
 +};
 +</code>
 +Durch ein "überflüssiges" ''int''-Argument wird der Operator
 +als Postfixvariante markiert.
 +Üblicherweise sollte diese den alten Wert als Kopie (rvalue) liefern.
 +
 +==== Feldindex ====
 +<code cpp>
 +  Element& Klasse::operator[](Typ index);
 +</code>
 +Eckige Klammern machen eine Klasse konzeptionell zu etwas Feldähnlichem
 +([[.:beispiel:template|Beispiel: Array-Schablone]]). 
 +Die Feldindex-Operatormethode kann nur einen Parameter aufnehmen.
 +Allerdings muss der Index keine Ganzzahl sein. 
 +Auch sogenannte "assoziative" (zuordnende) Felder sind möglich:
 +<code cpp>
 +  std::map<std::string, int> telefonbuch;
 +  telefonbuch["Richter"] = 19;                 // merken: Richter TelNr. 19
 +  std::cout << telefonbuch["Richter"] << '\n'; // 19
 +</code>
 +
 +==== Funktionsklammern ====
 +<code cpp>
 +  Ergebnistyp Klasse::operator()( Parameterliste );
 +</code>
 +Die Funktionsklammer kann beliebig viele Parameter aufnehmen 
 +und lässt Objekte wie Funktionen agieren 
 +([[.:beispiel:funktor]]).
 +Im mathematischen Sinne ist das Funktionsobjekt
 +ein Operator, der auf die Argumente angewendet wird.
 +
 +Funktionsobjekte (Funktoren) können sich Einstellungen (Parameter) merken
 +und können ihren inneren Zustand ändern.
 +Dadurch wird die Anzahl der Argumente beim Aufruf verringert.
 +Funktoren kommen u.a. in den [[.:stl#Algorithmen]] der Standard-Bibliothek zum Einsatz.
 +
 +==== Typumwandlung ====
 +Umwandlungsmethoden erlauben, Objekte in einen anderen 
 +zuvor definierten Typ zu konvertieren. 
 +Der Ergebnistyp steht hinter dem Schlüsselwort ''operator''.
 +Der Typkonverter ''Bruch::operator double()''
 +ist das Gegenstück (die Umkehrung) zu einem Konstruktor ''Bruch::Bruch(double x)''.
 +<code cpp>
 +struct Bruch
 +{
 +  long z, n;
 +
 +  operator double() const // umwandeln in Dezimalbruch
 +  { 
 +    return double(z)/n; 
 +  }
 +};
 +
 +Bruch  b = { 1, 2 };
 +double x = b;             // 0.5, implizite Typumwandlung
 +double y = double(b);     //      explizite Typumwandlung 
 +</code>
 +Vorsicht!
 +Durch ein Zuviel schlägt die beabsichtigte Vereinfachung ins Gegenteil um.
 +Durch Konstruktoren und Typkonverter sollten keine Zyklen
 +im Typ-Umwandlungs-Graphen entstehen.
 +Auch mehrere Typumwandlungen in ähnliche Typen schaffen Probleme.
 +Der Compiler versucht bei Nichtübereinstimmung 
 +automatisch (implizit) Typen zu konvertieren,
 +indem er Konstruktoren und Typkonverter untersucht.
 +Findet der Compiler Mehrdeutigkeiten, 
 +muss der Anwender durch ausdrückliche (explizite) Typkonversion nachhelfen.
 +
 +==== Benutzerdefinierte Konstanten ====
 +Im C++-Standard 2011 als "user-defined literals" bezeichnete Konstanten mit frei wählbaren Anhängseln werden durch Definition besonderer Operatoren festlegt, 
 +deren Ergebnis einen beliebigen, vom Nutzer definierten Typ haben kann.
 +Auf diese Weise werden einfach lesbare Anfangswerte z.B. für Maßeinheiten möglich:
 +<code cpp>
 +struct Length { double value; };
 +
 +Length operator "" _cm (long double x)
 +{
 +  return { double(x) / 100 };
 +}
 +
 +Length length = 1.0_cm;
 +</code>
 +Als Argumente sind nur die Typen ''unsigned long long int'', ''long double'', ''char'' und ''(char const*, size_t)'' zugelassen.
 +Bei Zeichenkettenliteralen gibt der zweite Parameter die Anzahl der Zeichen ohne Ende-Null an.
 +Literal-Anhängsel ohne Unterstrich sind für künftige Standardisierungen reserviert.
 +
 +Vorsicht: Wie bei jedem neuen Merkmal besteht die Gefahr des übermäßigen Gebrauchs.
kennen/operator.txt · Zuletzt geändert: 2020-07-27 09:55 von 127.0.0.1

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki