Advanced   Java   Services move-semantics Back Next Up Home


move semantics

Sowohl die Kopiervorgänge im Kopierkonstruktor als auch die in der Operatorfunktion operator=() brauchen Speicher und kosten Zeit, da die Kopiervorgänge über temporäre Speicher ablaufen. Wird so ein temporärer Speicher nur einmal benützt und nie mehr wieder, so gelingt es mitHilfe von rValue-Referenzen solche Speicher in einen persistenten Speicher umzuwandeln. Dazu greift man in das Speichermanagement derart ein, daß man einen regulären Pointer auf diesen Bereich setzt und den temporären Pointer, der auf diesen Speicher zeigt auf NULL setzt. Auf diese Weise verhindert man das Löschen des temporären Speichers. Eine rValue-Referenz in der Parameterliste einer Funktion zeigt an, daß man mit dieser Technik arbeiten kann. Dazu werden der Kopierkonstruktor und der Zuweisungsoperator, die in jeder Klasse vom Compiler erzeugt werden um einen move-Konstruktor und einen move-Zuweisungsoperator ergänzt. Damit besitzt jede Klasse 5 vom Compiler erzeugte Konmstruktoren bzw. Operatorfunktionen, die vom Compiler automatisch erzeugt werden, die man aber auch selbst überschreiben kann. Zudem gibt es in C++11 globale swap- und move-Funktionen, die mit diesem neuen Feature arbeiten.


move-Konstruktor und move-Zuweisung

Im folgenden werden alle fünf Standardkonstruktoren bzw. Zuweisungsoperatoren mit ihren Signaturen aufgelistet.

FunktionSignatur
Parameterloser KonstruktorXxx( )
KopierkonstruktorXxx( const Xxx &)
move-KonstruktorXxx( Xxx && )
ZuweisungsoperatorXxx & operator=( const Xxx & )
move-ZuweisungsoperatorXxx & operator=( Xxx && )

Beispiel

Im folgenden wird eine einfache Stringklasse entworfen. Es wird ein move-Konstruktor und ein move-Zuweisungsoperator geschrieben und gezeigt, daß die beiden Methoden verwendet werden.

Die Headerdatei

#ifndef STRING_H_
#define STRING_H_

#include <iostream>

using namespace std;

/*
*/
class String
{
    char* data;

public:
   // default
   String();
   // Constructor
   String(const char* p);

   //// Copy-Constructor klassisch
   String(const String& that);

   //// move constructor
   String(String&& that);  // String&& is an rvalue reference to a string

   //// operator=() // classical implementation
   String& operator=(const String & that);

   //// move-assignment
   String& operator=(String&& other);

   // Destruktor
   virtual ~String();  // virtual nur hier!

   char* c_str() const;
};
#endif // STRING_H_

Die Implementierung

#include "String.h"

/*
   Defaultkonstruktor erzeugt Leerstring
*/
String::String()
{
   //cout << "constructor String()" << endl;
   this->data = new char[1];
   *this->data = 0;
   // this->data = "";  dieser Speicher kann nun nicht mehr freigegeben werden!
}

/*
   Initialisiert einen String mit einem c-String
*/
String::String(const char* cstr)
{
   //cout << "constructor String(const char*)" << endl;
   if (cstr != NULL)
   {
      size_t size = strlen(cstr) + 1;
      this->data = new char[size];
      memcpy(this->data, cstr, size);
   }
   //else ...
}

/*
   copy-Constructor
*/
String::String(const String& other)
{
   cout << "copy-constructor: String(const String&)" << endl;
   size_t size = strlen(other.data) + 1;
   this->data = new char[size];
   if (this->data !=NULL)
      memcpy(this->data, other.data, size);
   // else...
}

/*
   move-Constructor
   oder auch : swap(*this, other);
*/
String::String(String&& other) //: data(other.data)
{
   cout << "move-constructor: String(String&&)" << endl;
   this->data = other.data;
   other.data = NULL;
}

/*
   assignment  operator=(const X &); //
   http://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom
*/
String& String::operator=(const String & other)
{
   cout << "assignment: operator=(const String&)" << endl;
   if( this != &other)
   {
      // other ist zumindest ein leerstring, other.data ist also nie NULL, es gibt daher immer was zu kopieren.
      size_t size = strlen(other.data) + 1;    // strlen zählt die 0 nicht mit !
      delete[] this->data;
      this->data = new char[size];
      memcpy(this->data, other.data, size);
   }
   return *this;
}

/*
   move assignment operator=(X&&) // Move semantics: exchange content between this and rhs and return *this .
*/
String& String::operator=(String&& other)
{
   cout << "move assignment: operator=(String&&)" << endl;
   // other ist zumindest ein leerstring, other.data ist also nie NULL, es gibt daher immer was zu kopieren.
   this->data = std::move(other.data);
   other.data = NULL;
   //std::swap(this->data, other.data);
   //this->data = other.data;
   other.data = NULL;
   return *this;
}

// Destruktor
String::~String()
{
   //cout << "destructor" << endl;
   delete[] data;
}

char* String::c_str() const
{
   return this->data;
}

Die Verwendung

#include "String.h"

String testfunc1(String s);
String testfunc2(String* s);
String* testfunc3(String* s);

int main()
{
   cout << "main" << endl; //
   String s("Hi") ;
   cout << "---------- calling testfunc1 -----------"  << endl;
   s = testfunc1(s);
   cout << "\n---------- calling testfunc2 -----------"  << endl;
   s = testfunc2(&s);
   cout << "\n---------- calling testfunc3 -----------"  << endl;
   String* t = testfunc3(&s);
   cout << "end main" << endl; //
   return 0;
}

Screenshot

cpp-move-constructor-move-assignment.jpg









Valid XHTML 1.0 Strict top Back Next Up Home