Wilkening-Online Logo

C++ Operator-Überladung von A bis (fast) Z



Von Detlef Wilkening
todo
Version: 3

Hinweis - dieser Artikel ist noch nicht vollständig - Kapitel 3 fehlt teilweise und Kapitel 4 noch komplett. Ein Ausschnitt des Gesamt-Artikel entspricht dem Vortrag, den ich am 16.05.2012 auf dem C++ User-Treffen in Düsseldorf gehalten habe.

1. Einführung

1.1 Operator-Überladung

1.2 Die C++ Operatoren

Dieses Kapitel ist eine Übersicht über alle C++ Operatoren. Die Tabelle ist dabei nach der Priorität der Operatoren geordnet. Außerdem enthält sie noch die Informationen über die Auswertungs-Reihenfolge (L=>R oder R=>L), und ob sich der Operator überladen läßt (+) oder nicht (-) - und wenn, ob nur als Element-Funktion (EF), oder als freie Funktion und Klassen-Funktion (+, (KF)).

Prio. Operator Beschreibung Ausw. Überl.
1 :: Bereichszuordnung L=>R -
. Komponenten-Zugriff L=>R -
2 .-> Element-Zugriff, bzw. Punkt-Pfeil-Operator L=>R +
-> Element-Zugriff, bzw. Pfeil-Operator L=>R EF
[ ] Index (Array-Zugriff) L=>R EF
( ) Funktions-Aufruf L=>R EF
++ Post-Increment (n++) L=>R +
-- Post-Decrement (n--) L=>R +
typeid Typ (RTTI) L=>R -
const_cast Const-Cast L=>R -
static_cast Static-Cast L=>R -
reinterpret_cast Reinterpret-Cast L=>R -
dynamic_cast Dynamic-Cast L=>R -
3 sizeof Primäre Objekt-Größe R=>L -
++ Prä-Increment (++n) R=>L +
-- Prä-Decrement (--n) R=>L +
~ 1-er Komplement R=>L +
! Logisches Not R=>L +
+ Unäres + (Vorzeichen) R=>L +
- Unäres - (Vorzeichen) R=>L +
& Unäres & (Adresse) R=>L +
* Unäres * (Dereferenzierung) R=>L +
new, delete, new[], ... Dynamische Speicherverwaltung R=>L + (KF)
( ) Klassischer C-Cast R=>L -
4 .* Zeiger auf Element L=>R -
->* Zeiger auf Element L=>R +
5 * Multiplikation (binäres *) L=>R +
/ Division L=>R +
% Modulo L=>R +
6 + Addition (binäres +) L=>R +
- Subtraktion (binäres -) L=>R +
7 << Ausgabe bzw. Bit-Links-Schiebe L=>R +
>> Eingabe bzw. Bit-Rechts-Schiebe L=>R +
8 < Kleiner L=>R +
> Größer L=>R +
<= Kleiner-Gleich L=>R +
>= Größer-Gleich L=>R +
9 == Gleich L=>R +
!= Ungleich L=>R +
10 & Bitweises Und (binäres &) L=>R +
11 ^ Bitweises XOR L=>R +
12 | Bitweises Oder L=>R +
13 && Logisches Und L=>R +
14 || Logisches Oder L=>R +
15 ? : Bedingung R=>L -
16 = Zuweisung R=>L EF
*= Multiplikations-Zuweisung R=>L EF
/= Divisions-Zuweisung R=>L EF
%= Modulo-Zuweisung R=>L EF
+= Additions-Zuweisung R=>L EF
-= Subtraktions-Zuweisung R=>L EF
<<= Links-Bit-Schiebe-Zuweisung R=>L EF
>>= Rechts-Bit-Schiebe-Zuweisung R=>L EF
&= Bitweise-Und-Zuweisung R=>L EF
|= Bitweise-Oder-Zuweisung R=>L EF
^= Bitweise-Xor-Zuweisung R=>L EF
17 , Komma L=>R +

2. Grundlagen

2.1 Operatoren als Element-Funktionen


#include <iostream>
using namespace std;

class Rational
{
public:
   Rational(int n=0, int d=1) : nume(n), deno(d) {}

   Rational mul(const Rational&) const;

   void print() const { cout << nume << '/' << deno << endl; }

private:
   int nume, deno;
};

Rational Rational::mul(const Rational& rhs) const
{
   return Rational(nume*rhs.nume, deno*rhs.deno);
}

int main()
{
   Rational r0, r1(2, 3), r2(5, 7);
   r0.print();
   r1.print();
   r2.print();

   r0 = r1.mul(r2);
   r0.print();
}


Ausgabe:

0/1
2/3
5/7
10/21


#include <iostream>
using namespace std;

class Rational
{
public:
   Rational(int n=0, int d=1) : nume(n), deno(d) {}

   Rational mul(const Rational&) const;

   Rational operator*(const Rational&) const;

   void print() const { cout << nume << '/' << deno << endl; }

private:
   int nume, deno;
};

Rational Rational::mul(const Rational& rhs) const
{
   return Rational(nume*rhs.nume, deno*rhs.deno);
}

Rational Rational::operator*(const Rational& rhs) const
{
   return Rational(nume*rhs.nume, deno*rhs.deno);
}

int main()
{
   Rational r0, r1(2, 3), r2(5, 7);
   r0.print();
   r1.print();
   r2.print();

   r0 = r1.mul(r2);
   r0.print();

   r0 = r1.operator*(r2);           // Funktions-Schreibweise
   r0.print();

   r0 = r1*r2;                      // Operator-Schreibweise
   r0.print();
}


Ausgabe:

0/1
2/3
5/7
10/21
10/21
10/21

2.1.1 Vergessen Sie nicht das implizite "this"


#include <iostream>
using namespace std;

class Rational
{
public:
   Rational(int n=0, int d=1) : nume(n), deno(d) {}

   // Richtig, dies ist der Operator "*" mit 2 Parametern (binärer Operator):
   // - ein impliziter Parameter ("this"), da Element-Funktion
   // - und der exlizite Parameter "rhs"
   Rational operator*(const Rational& rhs) const;

   // Compiler-Fehler - dies wäre ein Operator "*" mit 3 Parametern:
   // - ein impliziter Parameter ("this"), da Element-Funktion
   // - und zwei exlizite Parameter "lhs" und "rhs"
   // => Compiler-Fehler, da der Operator "*" keine 3 Parameter hat
   Rational operator*(const Rational& lhs, const Rational& rhs) const;

private:
   int nume, deno;
};

// Dies ist die Implementierung des Operator "*" als Element-Funktion
// Der Operator "*" hat hier 2 Parameter (binärer Operator):
// - ein impliziter Parameter ("this"), da der Operator eine Element-Funktion ist
//   Daher kann auch direkt auf die Attribute "nume" und "deno" zugegriffen werden
// - und der exlizite Parameter "rhs"
Rational Rational::operator*(const Rational& rhs) const
{
   return Rational(nume*rhs.nume, deno*rhs.deno);
}

int main()
{
   Rational r0, r1(2, 3), r2(5, 7), r3;

   // Als Element-Funktion hat der Operator "*" nur einen expliziten Parameter
   r0 = r1.operator*(r2);
   r0 = r1 * r2;

   // Compiler-Fehler - Aufruf mit 3 Parametern
   // Was soll das auch? Wozu sollte "r3" da sein?
   r0 = r1.operator*(r2, r3);
}

2.2 Operatoren als freie Funktionen


#include <iostream>
using namespace std;

class Rational
{
public:
   Rational(int n=0, int d=1) : nume(n), deno(d) {}

   friend Rational mul(const Rational&, const Rational&);

   void print() const { cout << nume << '/' << deno << endl; }

private:
   int nume, deno;
};

Rational mul(const Rational& lhs, const Rational& rhs)
{
   return Rational(lhs.nume*rhs.nume, lhs.deno*rhs.deno);
}

int main()
{
   Rational r0, r1(2, 3), r2(5, 7);
   r0.print();
   r1.print();
   r2.print();

   r0 = mul(r1, r2);
   r0.print();
}


Ausgabe:

0/1
2/3
5/7
10/21


#include <iostream>
using namespace std;

class Rational
{
public:
   Rational(int n=0, int d=1) : nume(n), deno(d) {}

   friend Rational mul(const Rational&, const Rational&);

   friend Rational operator*(const Rational&, const Rational&);

   void print() const { cout << nume << '/' << deno << endl; }

private:
   int nume, deno;
};

Rational mul(const Rational& lhs, const Rational& rhs)
{
   return Rational(lhs.nume*rhs.nume, lhs.deno*rhs.deno);
}

Rational operator*(const Rational& lhs, const Rational& rhs)
{
   return Rational(lhs.nume*rhs.nume, lhs.deno*rhs.deno);
}

int main()
{
   Rational r0, r1(2, 3), r2(5, 7);
   r0.print();
   r1.print();
   r2.print();

   r0 = mul(r1, r2);
   r0.print();

   r0 = operator*(r1, r2);          // Funktions-Schreibweise
   r0.print();

   r0 = r1*r2;                      // Operator-Schreibweise
   r0.print();
}


Ausgabe:

0/1
2/3
5/7
10/21
10/21
10/21

2.3 Der Vergleich zwischen beiden Varianten

2.3.1 Symmetrische Operator-Nutzung


#include <iostream>
using namespace std;

class Rational
{
public:
   Rational(int n=0, int d=1) : nume(n), deno(d) {}

   Rational operator*(const Rational&) const;

   void print() const { cout << nume << '/' << deno << endl; }

private:
   int nume, deno;
};

Rational Rational::operator*(const Rational& rhs) const
{
   return Rational(nume*rhs.nume, deno*rhs.deno);
}

int main()
{
   Rational r0, r1(2, 3), r2(5, 7);
   r0.print();
   r1.print();
   r2.print();

   r0 = r1 * 4;                     // Okay => r0 = r1 * Rational(4);
   r0.print();

   r0 = 4 * r2;                     // Compiler-Fehler - geht nicht bei Element-Funktionen
   r0.print();
}


#include <iostream>
using namespace std;

class Rational
{
public:
   Rational(int n=0, int d=1) : nume(n), deno(d) {}

   friend Rational operator*(const Rational&, const Rational&);

   void print() const { cout << nume << '/' << deno << endl; }

private:
   int nume, deno;
};

Rational operator*(const Rational& lhs, const Rational& rhs)
{
   return Rational(lhs.nume*rhs.nume, lhs.deno*rhs.deno);
}

int main()
{
   Rational r0, r1(2, 3), r2(5, 7);
   r0.print();
   r1.print();
   r2.print();

   r0 = r1 * 4;                     // Okay => r0 = r1 * Rational(4);
   r0.print();

   r0 = 4 * r2;                     // Okay => r0 = Rational(4) * r2;
   r0.print();
}


Ausgabe:

0/1
2/3
5/7
8/3
20/7

2.3.2 Operatoren für fremde Klassen


// So geht es leider nicht

namespace std
{
   template<...> class basic_ostream
   {
   public:
      ...
      ostream& operator<<(const Rational&);      // Sehr sehr unwahrscheinlich (siehe Text)
      ...
   };
}


#include <iostream>
using namespace std;

class Rational
{
public:
   Rational(int n=0, int d=1) : nume(n), deno(d) {}

   friend Rational operator*(const Rational&, const Rational&);

   friend ostream& operator<<(ostream&, const Rational&);

private:
   int nume, deno;
};

Rational operator*(const Rational& lhs, const Rational& rhs)
{
   return Rational(lhs.nume*rhs.nume, lhs.deno*rhs.deno);
}

ostream& operator<<(ostream& out, const Rational& arg)
{
   return out << arg.nume << '/' << arg.deno;
}

int main()
{
   Rational r0, r1(2, 3), r2(5, 7);
   cout << r0 << endl;
   cout << r1 << endl;
   cout << r2 << endl;

   r0 = r1 * r2;
   cout << r0 << endl;

   r0 = r1 * 4;
   cout << r0 << endl;

   r0 = 4 * r2;
   cout << r0 << endl;
}


Ausgabe:

0/1
2/3
5/7
10/21
8/3
20/7

2.3.3 Operatoren für Enums


#include <iostream>
using namespace std;

enum E { one, two, three };

inline ostream& operator<<(ostream& out, E e)
{
   static const char* text[] = { "eins", "zwei", "drei" };
   return out << text[e];
}

inline E& operator++(E& e)
{
   static E next[] = { two, three, one };
   return e=next[e];
}

int main()
{
   E e = one;
   cout << e << endl;           // => eins

   ++e;
   cout << e << endl;           // => zwei
   ++e;
   cout << e << endl;           // => drei
   ++e;
   cout << e << endl;           // => eins

   ++++e;
   cout << e << endl;           // => drei
}


Ausgabe:

eins
zwei
drei
eins
drei

2.3.4 Zusammenfassung

2.3.5 Zwei weitere Beispiele für freie Operator-Funktionen



#include <iostream>
#include <string>
#include <boost/lexical_cast.hpp>
using namespace std;

string operator+(string s, int n)
{
   s += boost::lexical_cast<string>(n);
   return s;
}

int main()
{
   string s("abc->");
   cout << s << endl;            // abc->
   s = s + 2;
   cout << s << endl;            // abc->2
   s = s + 34;
   cout << s << endl;            // abc->234
}


Ausgabe:

abc->
abc->2
abc->234


#include <iostream>
#include <string>
#include <boost/lexical_cast.hpp>
using namespace std;

string& operator<<(string& s, bool b)
{
   s += b ? "true" : "false";
   return s;
}

template<class T> string& operator<<(string& s, const T& t)
{
   s += boost::lexical_cast<string>(t);
   return s;
}

int main()
{
   string s;

   s << true << " ist: " << 1 << " + " << 2 << ' ' << "= " << 3;
   cout << s << endl;

   (s="") << 3.5 << ' ' << false;
   cout << s << endl;
}


Ausgabe:

true ist: 1 + 2 = 3
3.5 false

2.4 Sinn der funktionalen Schreibweise beim Aufruf


#include <iostream>
#include <string>
using namespace std;

class A
{
public:
   virtual string operator+(const A&) const;
};

string A::operator+(const A&) const
{
   return "A";
}

class B : public A
{
public:
   virtual string operator+(const A&) const override;
};

string B::operator+(const A& a) const
{
   string res = A::operator+(a);         // Aufruf der Basis-Klassen-Funktion geht nur so
   res += "B";
   return res;
}

int main()
{
   A a1, a2;
   string res = a1 + a2;
   cout << res << endl;

   B b;
   res = b + a2;
   cout << res << endl;
}


Ausgabe:

A
AB

2.5 Adressen von überladenen Operatoren


struct A
{
};

A operator+(const A&, const A&);

struct B
{
   B operator+(const B&) const;
};

int main()
{
   A(*plus1)(const A&, const A&) = +;               // Compiler-Fehler, so geht das nicht

   A(*plus2)(const A&, const A&) = operator+;       // Mit "operator" funktioniert es

   B(B::*plus3)(const B&) const = &B::operator+;    // Und so fuer Element-Funktionen
}

2.6 Noch eine Bemerkung zu den Parameter-Namen

3. Operator-Besonderheiten

3.1 Unäre Operatoren


#include <iostream>
using namespace std;

struct A
{
   A(int v=0) : value(v) {}

   // Element-Funktion => kein expliziter Parameter
   // Nur der implizite "this" Parameter
   bool operator!() const;

   // Als freie Funktion => natuerlich den einen expliziten Parameter
   friend A operator-(const A&);

   friend inline ostream& operator<<(ostream& out, const A& a)
   {
      return out << "A(" << a.value << ')';
   }

private:
   int value;
};

// Element-Funktion => kein expliziter Parameter
bool A::operator!() const
{
   return value==0;
}

// Als freie Funktion => natuerlich den einen expliziten Parameter
A operator-(const A& a)
{
   return A(-a.value);
}

int main()
{
   A a0;
   if (!a0)
   {
      cout << "Not a0 " << a0 << " ist true" << endl;
   }
   else
   {
      cout << "Not a0 " << a0 << " ist false" << endl;
   }

   A a1(1);
   if (!a1)
   {
      cout << "Not a1 " << a1 << " ist true" << endl;
   }
   else
   {
      cout << "Not a1 " << a1 << " ist false" << endl;
   }

   a0 = -a1;
   cout << "a0 == " << a0 << endl;
   cout << "a1 == " << a1 << endl;
}


Ausgabe:

Not a0 A(0) ist true
Not a1 A(1) ist false
a0 == A(-1)
a1 == A(1)


#include <iostream>
using namespace std;

// Klasse A - unaeres und binaeres + als Element-Funktion
struct A
{
   A& operator+();
   void operator+(const A&) const;
};

A& A::operator+()
{
   cout << "- Klasse A - unaeres +" << endl;
   return *this;
}

void A::operator+(const A&) const
{
   cout << "- Klasse A - binaeres +" << endl;
}

// Klasse B - unaeres und binaeres + als freie Funktion
struct B
{
};

B& operator+(B&);
void operator+(const B&, const B&);

B& operator+(B& b)
{
   cout << "- Klasse B - unaeres +" << endl;
   return b;
}

void operator+(const B&, const B&)
{
   cout << "- Klasse B - binaeres +" << endl;
}

int main()
{
   A a1, a2;

   cout << "+a1 =>" << endl;
   +a1;
   cout << "a1+a2 =>" << endl;
   a1+a2;
   cout << "a1 + +a2 =>" << endl;
   a1 + +a2;

   cout << endl;

   B b1, b2;

   cout << "+b1 =>" << endl;
   +b1;
   cout << "b1+b2 =>" << endl;
   b1+b2;
   cout << "b1 + +b2 =>" << endl;
   b1 + +b2;
}


Ausgabe:

+a1 =>
- Klasse A - unaeres +
a1+a2 =>
- Klasse A - binaeres +
a1 + +a2 =>
- Klasse A - unaeres +
- Klasse A - binaeres +

+b1 =>
- Klasse B - unaeres +
b1+b2 =>
- Klasse B - binaeres +
b1 + +b2 =>
- Klasse B - unaeres +
- Klasse B - binaeres +


Liste aller überladbaren unären Operatoren in C++:

Operator Siehe auch:
1-er Komplement; ~ ---
Logisches Not: ! ---
Unäres + (Vorzeichen) ---
Unäres - (Vorzeichen) ---
Unäres * (Dereferenzierung) ---
Post- bzw. Prä-Increment: n++ bzw. ++n Kapitel 3.4
Post- bzw. Prä-Decrement: n-- bzw. --n Kapitel 3.4
Unäres & (Adresse) Kapitel 3.6
Element-Zugriff: -> Kapitel 3.8
Funktions-Aufruf: () Kapitel 3.10

3.2 Zuweisungs-Operator ("=")

3.2.1 Kopier-Zuweisungs-Operator


#include <iostream>
using namespace std;

class A
{
public:
   explicit A(int arg=0) : n(arg) {}
   A& operator=(const A&);

   friend ostream& operator<<(ostream& out, const A& a)
   {
      return out <<"A(" << a.n << ')';
   }

private:
   int n;
};

A& A::operator=(const A& a)
{
   n = a.n;
   return *this;
}

int main()
{
   A a0, a1(1), a2(2);
   cout << a0 << ' ' << a1 << ' ' << a2 << endl;

   a0 = a1;

   cout << a0 << ' ' << a1 << ' ' << a2 << endl;

   a0 = a1 = a2;                                          // Mehrfach-Zuweisung

   cout << a0 << ' ' << a1 << ' ' << a2 << endl;
}


Ausgabe:

A(0) A(1) A(2)
A(1) A(1) A(2)
A(2) A(2) A(2)


// Im Prinzip der gleiche Code wie eben
// Aber nun mit "implizitem Kopier-Zuweisungs-Operator"

#include <iostream>
using namespace std;

class A
{
public:
   explicit A(int arg=0) : n(arg) {}

   // Kein expliziter Kopier-Zuweisungs-Operator mehr => impliziter

   friend ostream& operator<<(ostream& out, const A& a)
   {
      return out <<"A(" << a.n << ')';
   }

private:
   int n;
};

int main()
{
   A a0, a1(1), a2(2);
   cout << a0 << ' ' << a1 << ' ' << a2 << endl;

   a0 = a1;                                               // Funktioniert auch so

   cout << a0 << ' ' << a1 << ' ' << a2 << endl;

   a0 = a1 = a2;                                          // Funktioniert auch

   cout << a0 << ' ' << a1 << ' ' << a2 << endl;
}


Ausgabe:

A(0) A(1) A(2)
A(1) A(1) A(2)
A(2) A(2) A(2)


// Achtung - stark fehlerhafter Code
// So niemals machen

#include <cstring>
#include <iostream>
using namespace std;

class MyString
{
public:
   MyString(const char*);
   // Achtung - die impliziten Elemente sind alle fehlerhaft - so nicht machen
   // Kein expliziter Destruktor => impliziter
   // Kein expliziter Kopier-Konstruktor => impliziter
   // Kein expliziter Kopier-Zuweisungs-Operator => impliziter

   friend ostream& operator<<(ostream&, const MyString&);

private:
   char* p;
};

MyString::MyString(const char* q)
{
   p = new char[strlen(q)+1];
   strcpy(p, q);
}

ostream& operator<<(ostream& out, const MyString& str)
{
   out << str.p;
   return out;
}

int main()
{
   // Beispiel-Nutzung mit einigen Problemen...
   MyString s1("Hallo");
   cout << "s1: " << s1 << endl;

   // Fehlender korrekter Kopier-Konstruktor sorgt fuer Laufzeit-Probleme
   MyString s2(s1);
   cout << "s2: " << s2 << endl;

   // Fehlender korrekter Kopier-Zuweisungs-Operator sorgt fuer Laufzeit-Probleme
   s1 = s2;
   cout << "s1: " << s1 << endl;

   // Fehlender korrekter Destruktor sorgt fuer Speicher-Loecher
}


Ausgabe:

s1: Hallo
s2: Hallo
s1: Hallo


// Variante 1 - Kopier-Zuweisungs-Operator deklarieren, aber nicht implementieren

class A
{
private:
   // Deklaration private und ohne Implementierung, da die Klasse nicht zuweisbar sein soll
   A& operator=(const A&);
};

// Kopier-Zuweisungs-Operator von A nicht implementieren

int main()
{
   A a1, a2;
   a1 = a2;          // Compiler-Fehler, da kein Zugriff (Element private)
}                    // Bei moeglichem Zugriff (friend, ...) => Linker-Fehler



// Variante 2 - Nutzung von Boost::noncopyable

#include <boost/noncopyable.hpp>

class A : boost::noncopyable
{
};

int main()
{
   A a1, a2;
   a1 = a2;          // Compiler-Fehler
}



// Variante 3 - C++11

class A
{
public:
   A& operator=(const A&) = delete;
};

int main()
{
   A a1, a2;
   a1 = a2;          // Compiler-Fehler
}


#include <iostream>
using namespace std;

class A
{
public:
   A(int v1, int v2) : value1(v1), value2(v2) {}

   A& operator=(const A&);     // Expliziter Kopier-Zuweisungs-Operator
   A& operator=(bool);         // Weiterer anderer Zuweisungs-Operator
   A& operator=(int);          // Weiterer anderer Zuweisungs-Operator

   friend ostream& operator<<(ostream& out, const A& a)
   {
      return out << '[' << a.value1 << ',' << a.value2 << ']';
   }

private:
   int value1, value2;
};

// Expliziter Kopier-Zuweisungs-Operator
A& A::operator=(const A& a)
{
   value1 = a.value1;
   value2 = a.value2;
   return *this;
}

A& A::operator=(bool b)
{
   value1 = b;
   value2 = !b;
   return *this;
}

A& A::operator=(int v)
{
   value1 = v;
   value2 = -v;
   return *this;
}

int main()
{
   A a1(1, 11), a2(2, 22);
   cout << "a1=" << a1 << endl;
   cout << "a2=" << a2 << endl;

   a1 = a2;
   cout << "a1=" << a1 << endl;

   a1 = true;
   cout << "a1=" << a1 << endl;

   a1 = 42;
   cout << "a1=" << a1 << endl;
}


Ausgabe:

a1=[1,11]
a2=[2,22]
a1=[2,22]
a1=[1,0]
a1=[42,-42]


#include <iostream>
using namespace std;

class A
{
public:
   explicit A(int v1, int v2) : value1(v1), value2(v2) {}

   // KEIN expliziter Kopier-Zuweisungs-Operator
   A& operator=(bool);
   A& operator=(int);

   friend ostream& operator<<(ostream& out, const A& a)
   {
      return out << '[' << a.value1 << ',' << a.value2 << ']';
   }

private:
   int value1, value2;
};

A& A::operator=(bool b)
{
   value1 = b;
   value2 = !b;
   return *this;
}

A& A::operator=(int v)
{
   value1 = v;
   value2 = -v;
   return *this;
}

int main()
{
   A a1(1, 11), a2(2, 22);
   cout << "a1=" << a1 << endl;
   cout << "a2=" << a2 << endl;

   a1 = a2;                            // Impliziter Kopier-Zuweisungs-Operator
   cout << "a1=" << a1 << endl;

   a1 = true;
   cout << "a1=" << a1 << endl;

   a1 = 42;
   cout << "a1=" << a1 << endl;
}


Ausgabe:

a1=[1,11]
a2=[2,22]
a1=[2,22]
a1=[1,0]
a1=[42,-42]

3.2.2 Move-Zuweisungs-Operator


struct MyMoveableClass
{
   MyMoveableClass();
   MyMoveableClass(MyMoveableClass&&);              // Move-Konstruktor

   MyMoveableClass& operator=(MyMoveableClass&&);   // Expliziter Move-Zuweisungs-Operator

private:
   AnotherMoveableClass mMember1;
   FurtherMoveableClass mMember2;
};


MyMoveableClass& MyMoveableClass::operator=(MyMoveableClass&& arg)
{
   mMember1 = std::move(arg.mMember1);
   mMember2 = std::move(arg.mMember2);
   return *this;
}

3.3 Operative-Zuweisungs-Operatoren (z.B. "+=")


#include <iostream>
using namespace std;

struct MyInt
{
   MyInt(int v=0) : value(v) {}

   MyInt& operator+=(const MyInt&);
   MyInt operator+(const MyInt&) const;

   friend ostream& operator<<(ostream& out, const MyInt& myint)
   {
      return out << myint.value;
   }

private:
   int value;
};

// Operator "+=" auf den "Int-Werten" ausimplementiert
MyInt& MyInt::operator+=(const MyInt& arg)
{
   value += arg.value;      // Die Implementierung von "+=" basiert auf den "Int-Werten"
   return *this;
}

// Operator "+" basiert auf Operator "+="
MyInt MyInt::operator+(const MyInt& arg) const
{
   MyInt res(*this);
   res += arg;              // Die Implementierung von "+" nutzt intern "+=" von "MyInt"
   return res;
}

int main()
{
   MyInt n0, n1(1), n2(2);

   n0 = n1 + n2;
   cout << "n0 = n1 + n2  =>  n0 == " << n0 << endl;

   n0 += n2;
   cout << "n0 += n2      =>  n0 == " << n0 << endl;
}


Ausgabe:

n0 = n1 + n2  =>  n0 == 3
n0 += n2      =>  n0 == 5

3.4 Prä- und Post-Increment und Decrement Operatoren ("++" und "--")


// Achtung - die Operatoren erfuellen so noch nicht die Besonderheiten
// der Prae- und Post-Increment-Operatoren. Das folgt gleich noch.

#include <iostream>
using namespace std;

struct MyInt
{
   MyInt(int v=0) : value(v) {}

   // Bitte die Rueckgabe-Typen noch ignorieren - die bekommen wir gleich
   void operator++();                 // Ohne Dummy-Parameter => Prae-Increment, d.h. "++n"
   void operator++(int);              // Mit Dummy-Parameter => Post-Increment, d.h. "n++"

   friend ostream& operator<<(ostream& out, const MyInt& myint)
   {
      return out << myint.value;
   }

private:
   int value;
};

void MyInt::operator++()
{
   ++value;
}

void MyInt::operator++(int)
{
   ++value;
}

int main()
{
   MyInt n(2);
   cout << "n == " << n << endl;

   ++n;                                // Prae-Increment
   cout << "n == " << n << endl;

   n++;                                // Post-Increment
   cout << "n == " << n << endl;
}


Ausgabe:

n == 2
n == 3
n == 4



#include <iostream>
using namespace std;

struct MyInt
{
   MyInt(int v=0) : value(v) {}

   MyInt& operator++();                 // Prae-Increment mit Non-Const-Referenz Rueckgabe

   friend ostream& operator<<(ostream& out, const MyInt& myint)
   {
      return out << myint.value;
   }

private:
   int value;
};

MyInt& MyInt::operator++()
{
   ++value;
   return *this;
}

int main()
{
   MyInt n(2);
   cout << "n == " << n << endl;

   ++n;
   cout << "n == " << n << endl;

   ++++++n;
   cout << "n == " << n << endl;

   MyInt n2;
   n2 = ++n;
   cout << "n == " << n << endl;
   cout << "n2 == " << n2 << endl;

   cout << endl;

   int x = 11;
   cout << "x == " << x << endl;

   ++x;
   cout << "x == " << x << endl;

   ++++++x;                             // Mehrfach-Anwendung von "++n" geht auch bei "int"
   cout << "x == " << x << endl;
}


Ausgabe:

n == 2
n == 3
n == 6
n == 7
n2 == 7

x == 11
x == 12
x == 15


#include <iostream>
using namespace std;

struct MyInt
{
   MyInt(int v=0) : value(v) {}

   const MyInt operator++(int);                 // Post-Increment mit Const-Kopie Rueckgabe

   friend ostream& operator<<(ostream& out, const MyInt& myint)
   {
      return out << myint.value;
   }

private:
   int value;
};

const MyInt MyInt::operator++(int)
{
   MyInt tmp(*this);
   ++value;
   return tmp;
}

int main()
{
   MyInt n(2);
   cout << "n == " << n << endl;

   n++;
   cout << "n == " << n << endl;

   // n++++;                      // Waere Compiler-Fehler - aufgrund der "Const-Rueckgabe"

   MyInt n2;
   n2 = n++;
   cout << "n == " << n << endl;
   cout << "n2 == " << n2 << endl;

   cout << endl;

   int x = 11;
   cout << "x == " << x << endl;

   x++;
   cout << "x == " << x << endl;

   // x++++;                      // Waere Compiler-Fehler - auch bei "int"
}


Ausgabe:

n == 2
n == 3
n == 4
n2 == 3

x == 11
x == 12


#include <iostream>
#include <vector>
using namespace std;

int main()
{
   vector<int> v;
   v.push_back(11);
   v.push_back(22);
   v.push_back(33);

   // Bevorzuge Prae-Operator "++it" wenn moeglich - z.B. bei Iteratoren in Schleifen
   for (vector<int>::const_iterator it = v.begin(); it != v.end(); ++it)
   {
      cout << *it << endl;
   }
}


Ausgabe:

11
22
33

3.5 Logisches "Und" ("&&") und logisches "Oder" ("||")