Mini-Kurs "Move-Semantik in C++"

C++ ist schon immer für hohe Performance und schnelle Programme bekannt gewesen, aber mit der Einführung von C++11 ist die Sprache noch einmal deutlich effizienter geworden - nicht zuletzt durch die Einführung der Move-Semantik.

Mit Hilfe der Move-Semantik können wir an vielen Stellen im Code die Erstellung von temporären Objekten verhindern sowie die Übergabe von großen Datentypen von einem Geltungsbereich in den nächsten optimieren. 
 

Das Ziel dieses Mini-Kurses ist es, die wesentlichen Ideen der Move-Semantik praxisnah und verständlich mit vielen Beispiele und Experimenten zu vermitteln.

Der Kurs richtet sich an Fortgeschrittene mit einem soliden Grundlagenwissen in C++. Durch den ausführlichen Grundlagenteil wird der Einstieg in die Welt der Move-Semantik jedoch stark erleichert.

Begleitskript zum Kurs

Alle Videos sowie die Code-Beispiele sind frei verfügbar.

Um dich beim Lernen zu unterstützen und um dir ein Nachschlagewerk für die wichtigsten Konzepte der Move-Semantik zu bieten, kannst du dir zusätzlich ein optionales

Begleitskript kaufen. 

Auf 58 Seiten findest du hierin detaillierte Erklärungen, zahlreiche Abbildungen und Code-Beispiele, die dir das Lernen und Verstehen deutlich erleichtern werden. 

Kursinhalt

 

Move-Semantik • Grundlagen

Teil 1.1 : Stack und Heap  

Damit fortgeschrittenere Konzepte wie Rvalue-Referenzen oder der Move-Konstruktor richtig verstanden werden können, werden wir uns im ersten Teil des Kurses mit einigen wichtigen Grundlagen befassen. 

 

In diesem Video geht es um den Unterschied zwischen Stack- und Heap-Speicher. Im ersten Teil schauen wir uns gemeinsam Theorie und Hintergründe zu Stack und Heap an und im zweiten Teil testen wir das ganze dann mit Beispielen und Experimenten in der Praxis.

Code-Beispiele aus dem Skript

Listing 1-1-L1

Ziel : 

Wachstumsrichtung des Stack-Speichers ermitteln

Ansatz : 

Ermittlung der Differenz zwischen den Stack-Adressen eines void-Zeigers und einer double-Variablen. Aus dem Vorzeichen der Differenz lässt sich ableiten, ob der Stack vom oberen Ende des Adressraums nach unten wächst (negatives Vorzeichen) oder in umgekehrter Richtung. 

Referenz :

Skript "Move-Semantik in C++", Seite 8 

Zurück zum Inhaltsverzeichnis
Weiter zum nächsten Teil
 
Teil 1.2 : Parameterübergabe

In diesem Video geht es um verschiedene Möglichkeiten, Parameter an Funktionen zu übergeben. Hierbei werden wir uns vor allem mit Call-By-Value und Call-By-Reference beschäftigen. Je nachdem, welche Variante umgesetzt ist, werden Kopien der Argumente beim Aufruf angelegt oder lediglich eine Referenz auf das Original erstellt.

 

Wenn wir uns schließlich in Teil 2 dieses Buches die eigentliche Move-Semantik ansehen, wird das Einsparen von Kopier-Operationen eine ganz zentrale Rolle spielen. Aus diesem Grund sehen wir uns in diesem Grundlagen-Teil die beiden wesentlichen Optionen zur Parameterübergabe im Detail an. Wie im letzten Kapitel werden wir uns zuerst mit der Theorie beschäftigen, um danach die wichtigsten Konzepte direkt in die Praxis umzusetzen.

Code-Beispiele aus dem Skript

Listing 1-2-L5

Ziel : 

Ermittlung der Speicherbelegung von Funktionen für CallByValue

Ansatz : 

Definieren einer Funktion callByValue(), die ein Argument vom Typ double aufnehmen kann. Der Rückgabetyp „void-Pointer“ dient wie in einem vorherigen Beispiel dazu, die Position des Stack-Zeigers in der Funktion zurückzugeben. Dann Berechnung der Differenz zwischen der Position vor dem Funktionsaufruf und nach dem Funktionsaufruf in main().

Referenz :

Skript "Move-Semantik in C++", Seite 17 

Zurück zum Inhaltsverzeichnis
Weiter zum nächsten Teil
 
Teil 1.3 : Copy-Semantik

In diesem Video geht es um Strategien beim Kopieren von Objekten. Wir werden sehen, wie wir das Übertragen von Daten von einem Objekt auf das andere mit Hilfe von Mechanismen wie Shallow-Copy und Deep-Copy gezielt beeinflussen können.

 

Dabei ist eine Regel sehr wichtig, die sogenannte Rule Of Three. Wir befassen uns dabei intensiv mit dem Destruktor, dem Copy-Konstruktor und dem Copy-Zuweisungsoperator und lernen, wie wir diese Komponenten an unsere Bedürfnisse anpassen können, um Heap-Speicher sicher zu verwalten. Wie im letzten Kapitel sehen wir uns erst die notwendige Theorie an, danach geht es dann wieder in die Praxis.

Code-Beispiele aus dem Skript

Listing 1-3-L9

Ziel : Implementierung und Instanziierung einer Klasse, welche die Rule of Three befolgt

Ansatz : 

Implementierung von Destruktor, Copy-Konstruktor und Copy-Zuweisungsoperator sowie deren Aufruf in main().

Referenz :

Skript "Move-Semantik in C++", Seite 25 

Zurück zum Inhaltsverzeichnis
Weiter zum nächsten Teil
 
Teil 1.4 : Lvalues und Rvalues

Im vierten und letzten Grundlagen-Kapitel werden wir uns mit zwei elementaren Wertekategorien in C++ befassen, nämlich Lvalues und Rvalues. Beide zu verstehen ist wichtig, wenn es darum geht, Daten in einem Programm auf effiziente Art und Weise von einer Funktion an die andere zu übergeben.

 

Wir haben sowohl Rvalues als auch Lvalues bereits verwendet, ohne dies besonders zu erwähnen. Ziel dieses Kapitels ist es, die Eigenschaften beider Wertekategorien zu erläutern und deren elementare Bedeutung für die Move-Semantik klar herauszustellen. 

Wenn wir im Hauptteil über Rvalues und Rvalue-Referenzen reden, wird mit den Grundlagen aus diesem Kapitel der wesentliche Mechanismus hinter der Move-Semantik besser zu verstehen sein.

Code-Beispiele aus dem Skript

Listing 1-4-L13

Ziel : 

Lvalues und Rvalues im Code erkennen und sicher verwenden.

Ansatz : 

 Implementierung einer Reihe von Lvales, Rvalues und Zuweisungen zwischen den beiden Wertekategorien

Referenz :

Skript "Move-Semantik in C++", Seite 34 

Zurück zum Inhaltsverzeichnis
Weiter zum nächsten Teil
 

Move-Semantik • Hauptteil

Teil 2.1 : Rvalue-Referenzen

In den vier Videos des Grundlagenteils haben wir uns mit einigen elementaren Konzepten befasst, die für ein solides Verständnis der Move-Semantik wichtig sind.

In diesem ersten Video des Hauptteils werden wir uns ausführlich mit Rvalue-Referenzen befassen - also Alias-Identifiern für temporäre Werte. Ein derartiges Konzept mag auf den ersten Blick relativ trivial klingen, tatsächlich ist es aber der Mechanismus, der die Move-Semantik und die Ideen dahinter überhaupt erst möglich macht.

 

Sehen wir uns an, warum das so ist. 

Code-Beispiele aus dem Skript

Listing 2-1-L16

Ziel : 

Sichere Verwendung von Rvalue-Referenzen​ in unterschiedlichen Situationen

Ansatz : 

Zuweisung von Rvalues an Rvalue-Referenzen, Rvalue-Referenzen als Funktionsparameter nutzen sowie Beispiele für fehlerhafte Zuweisungen zwischen Rvalues und Lvalues

Referenz :

Skript "Move-Semantik in C++", Seite 43 

Zurück zum Inhaltsverzeichnis
Weiter zum nächsten Teil
 
 
Teil 2.2 : Die "Rule of Five"

Nachdem wir im letzten Video Rvalue-Referenzen kennen gelernt haben, ist vielleicht die Frage nach der Sinnhaftigkeit eines derartigen Konstrukts aufgekommen. In diesem letzten Video werden wir uns daher ausführlich mit der Antwort auf diese Frage beschäftigen.

Wir werden uns den eigentlichen Kern der Move-Semantik ansehen, nämlich den Move-Konstruktor und den Move-Zuweisungsoperator. Dabei werden wir die bereits bekannte Rule of Three aus dem Video zur Copy-Semantik um zwei weitere Methoden zur Rule of Five erweitern und eine Klasse schreiben, die in der Lage ist, in sehr effizienter Weise einen größeren Block Heap-Speicher verwaltet. 

Code-Beispiele aus dem Skript

Listing 2-2-L18

Ziel : 

Implementierung der Rule of Five in einer eigenen Klasse

Ansatz : 

Überschreiben von u.a. Move-Konstruktor und Move-Zuweisungsoperator, um ein konsistentes Speichermanagement beim Instanziieren und Kopieren zu erreichen.

Referenz :

Skript "Move-Semantik in C++", Seite 53 

Zurück zum Inhaltsverzeichnis
Weiter zum nächsten Teil
 
Teil 3.1 : Performance-Vergleich "Move" vs. "Copy"​

Dies ist das letzte Video im Mini-Kurs. Bisher haben wir uns ausführlich mit den wichtigsten vorbereitenden Grundlagen sowie mit der eigentlichen Move-Semantik beschäftigt. An mehreren Stellen wurde betont, dass uns die Move-Semantik Geschwindigkeitsvorteile gegenüber der Copy-Semantik bringt. Bisher mussten wir das einfach ohne Nachweis glauben. 

 

In diesem Video werden wir daher gemeinsam ein Programm entwickeln, in dem wir die Performance-Unterschiede zwischen Copy und Move untersuchen können. Die Idee ist, einen digitalen Staffellauf zu programmieren, in dem Läufer-Objekte einen virtuellen Staffelstab entweder mit Move- oder mit Copy-Semantik von einer Läufer-Instanz an die nächste übergeben. Wir stoppen dabei die Zeit und schauen uns an, wer gewinnt und wie groß der Geschwindigkeitsunterschied der beiden Methoden am Ende wirklich ist.

Übungen

Listing 3-1

Ziel : 

Performance-Vergleich zwischen Copy- und Move-Semantik durchführen

Ansatz : 

Implementierung einer Klasse, die sowohl Copy- als auch​ Move-Semantik unterstützt und Vergleich beider Methoden in einem virtuellen "Staffellauf", in dem Instanzen von einem "Läufer" an den nächsten übergeben werden. 

Hinweis: Der Laufzeit-Unterschied zwischen Copy und Move ist beachtlich!

Zurück zum Inhaltsverzeichnis
UdacityAd.jpg
Wie geht es weiter? 

Die Move-Sem​antik ist eine Schlüssel-Technik, die den Weg zu mehr Performance in der Programmierung mit C++ ebnet. Wenn du die Move-Semantik verstanden hast, dann sind auch Smart Pointer oder Multi-Threading kein großes Problem mehr - denn an wichtigen Stellen wird hier auch "verschoben" statt "kopiert". 

Wenn du C++ in deinem (zukünftigen) Beruf professionell nutzen willst, dann möchte ich dir an dieser Stelle das Nanodegree C++ von Udacity empfehlen. Das ist ein professionell angeleiteter Kurs mit Abschluss-Zertifikat, in dem sehr ausführlich und strukturiert modernes C++ vermittelt wird.

Ein Highlight des Kurses sind aus meiner Sicht die vielen Beiträge vom Schöpfer von C++, Bjarne Stroustrup. Zwei der Kursmodule (Speichermanagement und Concurrency) wurden von mir entwickelt und ich denke, dass besonders der hohe Praxisanteil mit vielen Beispielen sehr hilfreich beim Lernen sind.

Hier kannst du mehr dazu erfahren.

Udacity-Abschlussprojekt: "Program a Concurrent Traffic Simulation"

traffic_simulation.gif