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
© Copyright 2024 ExpyDoc