C++-Programmierung: Operatoren
Vorzeichen
BearbeitenGibt einem numerischen Wert ein negatives Vorzeichen, bzw. kehrt das Vorzeichen um.
int i = -5; // i erhält den Wert -5
int n = -i; // n erhält den Wert 5
Dient zur expliziten Angabe des Vorzeichens. Da Zahlen ohne explizites Vorzeichen immer positiv sind, kann dieser Operator weggelassen werden. Manchmal sinnvoll, um explizit auf das Vorzeichen hinzuweisen.
int i = 5; // i erhält den Wert 5
int n = +i; // n erhält den Wert 5
Arithmetische Operatoren
BearbeitenAuf Operanden, die einen arithmetischen Typ tragen, werden die usual arithmetic conversions angewendet, um die Typen einander anzugleichen und den Typ des Resultats zu bestimmen.
+ (Addition)
BearbeitenAddiert die Werte seiner Operanden und gibt das Ergebnis zurück.
int i = 3;
int n = i + 5;
- (Subtraktion)
BearbeitenSubtrahiert die Werte seiner Operanden und gibt das Ergebnis zurück.
int i = 5;
int n = i - 3;
* (Multiplikation)
BearbeitenMultipliziert die Werte seiner Operanden und gibt das Ergebnis zurück.
int i = 5;
int n = 2 * i;
/ (Division)
BearbeitenDividiert die Werte seiner Operanden und gibt das Ergebnis zurück. Bei der Division von Ganzzahlen fällt ein eventueller Rest weg, es wird also nicht gerundet.
int i = 10 / 3; // i erhält den Wert 3
% (Modulo)
BearbeitenDividiert die Werte seiner Operanden und gibt den Divisionsrest zurück. Kann nur auf ganzzahlige Operanden angewendet werden. Ist mindestens ein Operand negativ, so ist das Vorzeichen des Resultats implementationsabhängig.
int i = 10 % 3; // i erhält den Wert 1
Zuweisungen
Bearbeiten++ (Inkrement)
BearbeitenErhöht den Wert seines Operanden um 1.
i = 2;
i++; // i hat den Wert 3
Bezüglich der Priorität unterscheidet man zwischen Postfix- und Präfix-Notation. Die Postfix-Notation (i++) hat eine höhere Priorität als die Präfix-Notation (++i).
-- (Dekrement)
BearbeitenVermindert den Wert seines Operanden um 1.
i = 2;
i--; // i hat den Wert 1
Bezüglich der Priorität unterscheidet man zwischen Postfix- und Präfix-Notation. Die Postfix-Notation (i--) hat eine höhere Priorität als die Präfix-Notation (--i).
= (Zuweisung)
BearbeitenWeist seinem linken Operanden den Wert des rechten Operanden zu.
i = 3; // i hat den Wert 3
Kombinierte Zuweisungsoperatoren
BearbeitenDie kombinierten Zuweisungsoperatoren kombinieren den Zuweisungsoperator (=) mit einem anderen Operator:
- +=
- -=
- *=
- /=
- %=
- &=
- <<=
- >>=
- ^=
- |=
Dabei wird der linke Operand sowohl als linker Operand für die Zuweisung als auch für den anderen Operator verwendet.
a += b;
bedeutet also
a = a + (b);
Die Klammer um b soll verdeutlichen, dass gesamte rechte Ausdruck zuerst berechnet wird.
Vergleiche
Bearbeiten== (Gleichheit)
BearbeitenErgibt den boolschen Wert true, wenn die beiden Operanden gleich sind, sonst false.
!= (Ungleichheit)
BearbeitenErgibt den boolschen Wert true, wenn die beiden Operanden ungleich sind, sonst false.
<= (kleiner oder gleich)
BearbeitenErgibt den boolschen Wert true, wenn der linke Operand kleiner oder gleich dem rechten ist, sonst false.
>= (größer oder gleich)
BearbeitenErgibt den boolschen Wert true, wenn der linke Operand größer oder gleich dem rechten ist, sonst false.
< (kleiner)
BearbeitenErgibt den boolschen Wert true, wenn der linke Operand kleiner als der rechte ist, sonst false.
> (größer)
BearbeitenErgibt den boolschen Wert true, wenn der linke Operand größer als der rechte ist, sonst false.
<=> (Drei-Wege Vergleich)
BearbeitenSeit C++20: Ergibt ein Objekt, das
- kleiner 0 ist, falls der linke Operand kleiner als der rechte ist
- gleich 0 ist, falls der linke Operand gleich dem rechten ist
- größer 0 ist, falls der linke Operand größer als der rechte ist
Aussagenlogik
Bearbeiten&& (Und-Verknüpfung)
BearbeitenVerknüpft die beiden Operanden und gibt true zurück, wenn beide Operanden den Wert true haben, sonst false.
true true ---- true
Kann das Ergebnis bereits vorhergesagt werden, nachdem der erste Operand ausgewertet wurde (d. h., wenn dieser false ist, ist das Ergebnis sicher false), wird der zweite Operand nicht mehr ausgewertet.
|| (Oder-Verknüpfung)
BearbeitenVerknüpft die beiden Operanden und gibt true zurück, wenn mindestens einer der beiden Operanden den Wert true hat, sonst false.
true false ---- true
Kann das Ergebnis bereits vorhergesagt werden, nachdem der erste Operand ausgewertet wurde (d. h., wenn dieser true ist, ist das Ergebnis sicher true), wird der zweite Operand nicht mehr ausgewertet.
! (Negierung)
BearbeitenInvertiert den Wert seiner Operanden. Aus true wird false und umgekehrt.
true ---- false
Bitmanipulationen
Bearbeiten& (bitweise Und-Verknüpfung)
BearbeitenVerknüpft jedes Bit beider Operanden.
1100 1010 ---- 1000
| (bitweise Oder-Verknüpfung)
BearbeitenVerknüpft jedes Bit beider Operanden.
1100 1010 ---- 1110
^ (exklusive Oder-Verknüpfung)
BearbeitenVerknüpft jedes Bit beider Operanden.
1100 1010 ---- 0110
~ (bitweise Negation)
BearbeitenNegiert den Wert jedes Bits.
10 -- 01
<< (Linksverschiebung)
BearbeitenVerschiebt die Bits des linken Operanden um die durch den rechten Operanden angegebene Anzahl von Stellen nach links und füllt die Stellen rechts mit Nullen.
int i = 1; // dual: 00000001 int n = i << 1; //dual: 00000010 = 2
>> (Rechtsverschiebung)
BearbeitenVerschiebt die Bits des linken Operanden um die durch den rechten Operanden angegebene Anzahl von Stellen nach rechts. Die nach rechts verschobenen Ziffern fallen sozusagen heraus.
int i = 5; //dual: 00000101 int n = i >> 1; //dual: 00000010 = 2 int a = -2; //dual: 11111110 int b = a >> 1; //dual: 11111111 = -1
Datenzugriff
Bearbeiten* (Zeigerdereferenzierung)
BearbeitenDereferenziert einen Zeiger, damit nicht auf dessen wahren Inhalt (die Adresse) zugegriffen wird, sondern auf den Speicherbereich, auf den er verweist.
int variable = 5; int* zeiger = &variable; *zeiger = 3; //ändert den Wert von variable auf 3
. (Zugriff auf Member eines Objekts)
BearbeitenGreift auf einen Member eines Objekts zu.
obj.member = wert; obj.funktion();
-> (Zugriff auf Member eines Objekts über einen Zeiger)
BearbeitenDereferenziert einen Zeiger auf ein Objekt, der durch den linken Operanden angegeben wird, und greift auf den durch den rechten Operanden angegebenen Member zu.
obj_zeiger->member = wert; obj_zeiger->funktion();
Das ist gleichwertig zu folgender Schreibweise:
(*obj_zeiger).member = wert; (*obj_zeiger).funktion();
:: (Qualifizierung)
BearbeitenQualifizierung eines Bezeichners (Variable, Funktion, Klasse) mit seinem übergeordneten Element (Namespace, Klasse).
std::cout << "Hallo!" << std::endl;
.* (Zugriff auf und gleichzeitige Dereferenzierung eines Members)
BearbeitenZugriff auf, und gleichzeitige Dereferenzierung eines Zeiger-Members eines Objekts.
objekt.*zeiger_member = 5;
->* (Zugriff auf und gleichzeitige Dereferenzierung eines Members über einen Zeiger)
BearbeitenZugriff auf, und gleichzeitige Dereferenzierung eines Zeiger-Member eines Objekt-Zeigers
obj_zeiger->*zeiger_member = wert;
Typumwandlung
Bearbeiten() (explizites Casting)
BearbeitenWandelt den Wert des Ausdrucks rechts der Klammer in den Typ innerhalb der Klammer.
float f = 3.3;
int n = (int)f;
- Je nach Kontext benutzt der Compiler einen const_cast, static_cast oder reinterpret_cast ( siehe folgende) um die Anweisung umzusetzen.
- Diese Art der Umwandlung stammt von C und sollte in C++ möglichst vermieden werden. Es können schwerwiegende Fehler entstehen, wenn man einen static_cast erwartet, der Compiler aus dem Kontext heraus aber einen reinterpret_cast nutzt.
- Man kann nicht nur von/in eingebaute Datentypen casten.
- Konstruktorschreibweise (nur bei nicht mehrteiligen eingebauten Datentypen):
n = int(f);
- Zahlen wie 3.3 sind standardmäßig doubles. Möchte man aber ein float haben, genügt es, 3.3f zu schreiben.
static_cast
BearbeitenPendant zum C-Casting.
float f = 3.3;
int n = static_cast<int>(f);
In den spitzen Klammern steht der Zieltyp. Wenn man Zeiger auf Objekte einer Hierarchie umwandeln möchte, sollte man eher dynamic_cast benutzen.
const_cast
BearbeitenErmöglicht den Schreibzugriff auf eine konstant deklarierte Variable.
const int c = 42;
int* pc = const_cast<int*>(&c);
dynamic_cast
BearbeitenKorrekte Umwandlung eines Zeigers oder einer Referenz auf ein Objekt einer Basisklasse auf ein Objekt einer abgeleiteten Klasse. Dynamic_cast kann nur verwendet werden, wenn die Klasse mindestens eine virtuelle Methode besitzt. Üblicherweise wird der Destruktor virtuell gemacht.
class Base { /*...*/ };
class Derived : public Base { /*...*/ };
Base *bptr1 = new Derived;
Base *bptr2 = new Base;
Derived *dptr1 = dynamic_cast<Derived*>(bptr1); // i. o.
Derived *dptr2 = dynamic_cast<Derived*>(bptr2); // Fehler: bptr2 zeigt auf eine Instanz von Base
class Base { /*...*/ };
class Derived : public Base { /*...*/ };
Derived dobj;
Base bobj;
Base &bref1 = dobj;
Base &bref2 = bobj;
Derived &dref1 = dynamic_cast<Derived&>(bref1); // i. o.
Derived &dref2 = dynamic_cast<Derived&>(bref2); // Fehler: bref2 referenziert eine Instanz von Base
- Diese Art der Umwandlung funktioniert nur, wenn das umzuwandelnde Objekt wirklich eines des Zieltyps ist.
- Für die umgekehrte Richtung passiert die Umwandlung implizit, dynamic_cast wird nicht benötigt.
- Achtung: Schlägt die Umwandlung eines Zeigers fehl, gibt dynamic_cast einen Nullzeiger (0) zurück!
- Achtung: Schlägt die Umwandlung einer Referenz fehl, wirft dynamic_cast die Ausnahme std::bad_cast!
reinterpret_cast
BearbeitenGefährlichster und mächtigster Cast, der selten wirklich benötigt wird.
Mit ihm können u.a. Zeiger in Datentypen (und umgekehrt) „uminterpretiert“ werden.
int i = 25;
float *fp = reinterpret_cast<float*>(i);
In diesem Beispiel zeigt fp auf eine höchstwahrscheinlich undefinierte Float-Variable an der Speicheradresse 25.
typeid
BearbeitenMit diesem Operator können während der Laufzeit Informationen über eine Variable, eine Referenz, einen (dereferenzierten) Zeiger oder eine Klasse abgefragt werden. Der Operator liefert eine Referenz vom Typ type_info& zurück, der in der Header-Datei typeinfo definiert ist.
#include <typeinfo>
class Base { /*...*/ };
class Derived : public Base { /*...*/ };
Derived dobj;
Base &bref = dobj;
if ( typeid(bref) == typeid(Derived) )
std::cout << "bref referenziert Derived" << std::endl;
Achtung: Um diesen Operator verwenden zu können, muss bei den meisten Compilern RTTI explizit aktiviert werden (beim GNU-Compiler bspw. mit der Kommandozeilenoption -frtti), da die Introspektion sehr viel Aufwand vom Compiler erfordert.
Speicherbehandlung
Bearbeiten& (Adressermittlung)
BearbeitenErmittelt die Adresse des Operanden.
int i = 0;
int* zeiger = &i;
sizeof (Speicherbedarfsermittlung)
BearbeitenErmittelt den Speicherbedarf eines Typs oder eines Ausdrucks.
int n = sizeof(int);
int m = sizeof n;
Für einen Typ als Argument müssen Klammern gesetzt werden, für einen Ausdruck nicht.
new (Objekterstellung)
BearbeitenErstellt ein Objekt vom angegebenen Typ. Gibt einen Zeiger auf das neue Objekt zurück.
Object * p = new Object(parameter);
new[] (Anlegen eines Objekt-Arrays)
BearbeitenErstellt ein Objekt-Array.
Object * object_array = new Object[12];
delete (Objektzerstörung)
BearbeitenZerstört das angegebene Objekt.
Object * p = new Object;
// Tu irgendetwas mit dem Objekt.
delete p;
Verlangt einen Zeiger auf das Objekt als Argument. Das Objekt muss mit dem Operator new angelegt worden sein. Ausnahme: Der Zeiger darf auch NULL sein.
delete[] (Zerstörung eines Objekt-Arrays)
BearbeitenZerstört die Objekte im angegebenen Array.
delete [] objekt_array;
Die Objekte müssen mit dem Operator new[] angelegt worden sein.
Sonstige
Bearbeiten() (Funktionsaufruf)
BearbeitenAufruf einer Funktion und eventuelle Angabe von Parametern.
funktion();
objekt.funktion(1, "asdf");
, (Aufzählung)
BearbeitenDas Komma hat in C++ eine doppelte Bedeutung.
Zum einen hat es die (rein syntaktische) Aufgabe eines Trennzeichens bei Funktionsaufrufen und Initialisierungen:
int x = funktion(1, 2, 3);
int y[] = { 4, 5, 6 };
Zum anderen bezeichnet es den Sequentialoperator. Die durch Komma getrennten Ausdrücke werden von links nach rechts bewertet. Im Beispiel
for (int n=0, m=0; n < 5; n++, m=2*n)
{
// ...
}
wird bei der Reinitialisierung der Schleife zuerst n++ und dann m=2*n ausgeführt.
* Zeigerdeklaration
BearbeitenErstellt einen Zeiger auf einen bestimmten Datentypen.
int* int_zeiger;
void* void_zeiger;
?: (Bedingung)
BearbeitenDer Bedingungsoperator (übrigens der einzige ternäre Operator, also ein Operator mit drei Operanden) ist eine Verkürzung für ein if-else-Konstrukt.
int max = (a>b) ? a : b;
Ergibt der erste Operand (in diesem Fall a>b) true, ergibt der gesamte Ausdruck den zweiten Operanden, sonst den dritten. Somit könnte die obige Zeile wie folgt geschrieben werden:
int max;
if (a>b)
{
max = a;
}
else
{
max = b;
}
Der Operand, der nicht das Ergebnis darstellt, wird nicht ausgewertet.
[ ] (Indizierung)
BearbeitenZugriff auf ein bestimmtes Element eines Arrays.
array[index] = 5;
: (Vererbung)
BearbeitenErben von Variablen und Funktionen einer Klasse
class Klasse : public Basisklasse, public AndereBasisklasse
{
...
};
: (Initialisierung)
BearbeitenInitialisieren von Oberklassen und Membervariablen innerhalb der Konstruktor-Definition
Klasse::Klasse(int a, int b) // Konstruktor mit zwei Parametern
: Oberklasse(a), // Weiterleitung des Parameters 'a' an den Oberklassen-Konstruktor
_b(b) // Initialisierung der Membervariablen '_b' mit dem Wert von 'b'
{
...
}
Zeiger sollten nicht, dürfen aber in der Initialisierung mit new belegt werden. Das führt zu Speicherlecks.
Klasse::Klasse(int a, int b) // Konstruktor mit zwei Parametern
: array1_(new int[a]), // Anlegen von Speicher für a Elemente vom Typ int
array2_(new int[b]) // Anlegen von Speicher für b Elemente vom Typ int
// bekommt array2_ keinen Speicher, wird array1_ nicht freigegeben
{
...
}
Besser ist es wie folgt:
Klasse::Klasse(int a, int b) // Konstruktor mit zwei Parametern
: array1_(NULL),
array2_(NULL)
// bekommt array2_ keinen Speicher, wird array1_ nicht freigegeben
{
try
{
array1_ = new int[a]; // Anlegen von Speicher für a Elemente vom Typ int
array2_ = new int[b]; // Anlegen von Speicher für b Elemente vom Typ int
}
catch (std::bad_alloc)
{
delete[] array1_;
delete[] array2_;
throw;
// ein Destruktoraufruf erfolgt nicht, da das Objekt nicht konstruiert wurde
}
...
}
throw (Exception-Auslösung)
BearbeitenWirft die als Operand angegebene Exception.
throw exception;