06 - Klassen II.fm

Modul Programmieren mit C++
Kapitel Klassen
Doku
Fachhochschule Nordwestschweiz
Prof. H. Veitschegger
2r2
Seite
6.1
6 Klassen II
Default-Methoden
Der Compiler erzeugt verschiedene Methoden automatisch, wenn Sie sie nicht selbst deklarieren:
• Standard-Konstruktor (nur wenn Sie keinen eigenen Konstruktor definieren) C::C()
• Destruktor
C::~C()
• Operation für tiefe Kopie (nur wenn Sie keinen eigenen Verschiebe-Konstruktor definieren)
- Kopier-Konstruktor: C::C(const C&)
- Zuweisungsoperator: C& operator=(const C&)
• Verschiebende Kopie (seit C++ 11, nur wenn Sie keine Kopier-Operation definieren)
- Verschiebekonstruktor: C::C(C&&)
- Verschiebeoperator: C& operator=(C&&)
• Unerwünschte Elemente können neu mit "= delete" ausgeschaltet werden:
Fraction::Fraction(const Fraction&) = delete;
// Kopierkonstruktor deaktiviert
Konstruktoren zur Typenkonvertierung
Dieser spezielle Konstruktor wird automatisch bei impliziter Typenwandlung benutzt und besitzt genau einen
Parameter. Wenn Sie seine Nutzung bei impliziter Typenwandlung ausschliessen möchten, müssen Sie ihn als
explicit definieren.
Beispiel für eine Deklaration:
Punkt(double d[4]) : m_x(d[0]), m_y(d[1]), m_z(d[2]), m_color(static_cast<int>(d[3])) {}
Beispiel für die Nutzung:
double array[4] = { 4.4, 3.3, 2.2, 5.0 };
Punkt p = array;
Verschiebe-Semantik
Der bisherige C++ Standard sah nur das Kopieren von Objekten vor, welches im Kopier-Konstruktor und im
Zuweisungs-Operator zu realisieren war. Seit C++ 11 ist es nun auch möglich, Verschiebe-Konstruktoren und
eine Verschiebe-Zuweisung zu definieren. Dies bietet zweierlei Vorteile:
• Es ist einfacher, unnötige Kopieroperationen zu vermeiden (Verbesserung der Performance).
• Es kann einfacher sichergestellt werden, dass von einem Objekt keine Kopie angelegt wird.
Diese neuen Möglichkeiten werden in C++ 11 realisiert, indem neu sogenannte RValue-Referenzen angeboten
werden. Die Begriffe LValue und RValue stammen aus der Zuweisungstechnik. Betrachten wir folgende Beispiele:
i = j;
i = 517;
i = (3 + 4) * (7 - 8);
a[j++] = 0;
*pi = 12;
//
//
//
//
//
1
2
3
4
5
Jede Zuweisung hat eine linke und eine rechte Seite. Auf der linken Seite muss immer ein Ausdruck stehen,
welcher letztlich eine Speicherstelle spezifiziert, während auf der rechten Seite auch ein Ausdruck stehen kann,
bei welchem es sich weder um eine Variable noch um eine an einer Speicherstelle abgelegten Konstante handelt.
Die Beispiele 2 bis 5 besitzen auf der rechten Seite einen solchen Ausdruck. Eine Zuweisung der folgenden Arten
sind aus offensichtlichen Gründen verboten bzw. sinnlos:
5 = i;
string{} = "hallo";
// wohin soll i gespeichert werden?
// links ein temporäres Objekt. Zuweisung sinnlos
Modul Programmieren mit C++
Kapitel Klassen
Doku
Fachhochschule Nordwestschweiz
Prof. H. Veitschegger
2r2
Seite
6.2
Als RValues werden in C++ temporäre Werte bezeichnet, wie sie auf der rechten Seite von Zuweisungen auftreten können. Seit C++ 11 können RValue-Referenzen als Parameter oder Rückgabewerte verwendet werden.
Schauen wir uns als Beispiel eine Funktion an, welche einen Vektor mit irgendwelchen Werten erzeugt und
zurückgibt:
Vector createVector(int[] array, size_t size){
Vector v;
for (int i=0; i<size; ++i)
v.add(array[i]);
return v;
}
Der Vektor v ist nur innerhalb der Funktion gültig (Instanzierung auf dem Stack). Bei der Rückgabe muss er
kopiert werden, da v selbst vom Stack verschwinden wird. Hier wäre es schön, wenn man die Kopie vermeiden
könnte:
Vector createVector(int[] array, size_t size){
Vector v;
for (int i=0; i<size; ++i)
v.add(array[i]);
return v;
// hier wird verschoben anstatt kopiert, da der Compiler sieht, dass dies ein
// Temporary ist. es ist aber dafür Vector Verschiebe-Operationen nötig.
}
Die Klasse Vector mit Verschiebe-Operationen:
class Vector{
int* m_contents;
size_t m_length;
public:
Vector(Vector&& v): m_contents(v.m_contents), m_size(v.m_size){
v.m_contents = nullptr;
v.m_size = 0;
}
Vector& operator=(Vector&& v){
m_size = v.m_size;
v.m_size = 0;
delete[] m_contents;
m_contents = v.m_contents;
v.m_contents = nullptr;
return *this;
}
};
Nutzung (z.B. in main() ):
Vector a(createVector(...));
Vector b = (createVector(...));
Vector c;
c = createVector(...);
//
//
//
//
Verschiebekostruktor, falls vorhanden
Verschiebekostruktor, falls vorhanden
Standard-Konstruktor
Verschiebekostruktor, falls vorhanden
Anonyme Objekte
Namenlose temporäre Objekte, welche nur kurzfristig benutzt werden. Beispiele:
Fraction(3,4).show();
(new Fraction(0,1))->show();
// anonym
// erzeugt Speicherleiche