001
28.06.2003, 20:23 Uhr
virtual
Sexiest Bit alive (Operator)
|
Ein Copyconstructor dient dazu, eine Kopie neu zu konstruieren. Ein zuweisungsoperator wird dann verwendet, wenn man einer bestehenden Instanz etwas zuweisen will.
C++: |
string hallo("Welf"); string kopie(hallo); // Copy constructor;
hallo = kopie; // Zuweisungsoperator
|
Nun könnte man etwas vorschnell sagen: Sobald da irgendwie ein "=" Zeichen auftaucht, dann ist der Zuweisungoperator gefragt. Dem ist nicht so:
C++: |
string hallo("Welf"); string kopie = hallo; // Auch Copy constructor;
|
Wenn eine Variable deklariert und initialisiert wird, dann wird für die initialisierung auch der Constructor aufgerufen. (und nicht, wie man annehmen koennte zunächst der Defaultconstructor und danach der Zuweisungsoperator).
Standardmäßig bringt jede Klasse einen Copyconstructor und einen Zuweisungsoperator mit. Diese Standardversionen machen allerdings nur eine Bitweise kopie von Quelle nach Ziel. So kann so eine Klasse hier ohne expliziten CopyConstructor/Zuweisungoperator auskommen:
C++: |
class Vector3D { int x; int y; int z; ... };
|
Anders sieht es aus, wenn man eine tiefe Kopie benötigt. Nehmen wir mal als Beispiel eine Klasse String:
C++: |
class String { private: char* text; int laenge; public: String(const char* text) { this->laenge = strlen(text); this->text = new[this->laenge+1]; strcpy(this->text, text); } virtual ~String() { delete [] text; } };
|
Mit dieser Klasse koennte man nun - ohne das Der Compiler meckert - schreiben:
C++: |
String a("Hallo"); String b(a); // Copy constructor
|
Es gibt ja diesen Standard copy-constructor, der eine Bitweise Kopie macht. Dummerweise Wird das Programm aber mit an Sicherheit grenzender Wahrscheinlichkeit abstürzen. Der Grund ist, daß ja irgendwann der Destructor von a und b aufgerufen wird und beide werden dann versuchen, den gleichen Speicherbereich freizugeben. Möglichweise wird man schon davor bemerkt haben, daß Änderungen an a auch plötzlich in b sichtbar werden. Bei unserer Klasse String muß dahr der CopyConstructor und der Zuweisungsoperator dafür sorgen, daß jedes Strignobjekt seinen eigenen Text haben, in dem sie ihren Karm machen. Also zB:
C++: |
class String { ... String(const String& quelle) // Copy-Constructor { this->laenge = quelle->laenge; this->text = new char[this->laenge]; strcpy(this->text, quelle->text); } String& operator = (const String& quelle) // Zuweisungsoperator { delete [] this->text; this->laenge = quelle->laenge; this->text = new char[this->laenge]; strcpy(this->text, quelle->text); return *this; } };
|
Die große Ähnlichkeit der Implementierung von Zuweisungsoperator und Copyconstructor sollte aber jetzt nicht dazuverleiten, einfach sowas zu machen (Mircosoft tut es in CString ):
C++: |
class String { ... String(const String& quelle) // Copy-Constructor { this->text = NULL; this->operator = (quelle); } String& operator = (const String& quelle) // Zuweisungsoperator { delete [] this->text; this->laenge = quelle->laenge; this->text = new char[this->laenge]; strcpy(this->text, quelle->text); return *this; } };
|
Damit verbaut man sich die Möglichkeit, exceptionsicheren Code zu schreiben. Im Sinne der Exceptionsicherheit kann es sinnvoll sein, es genau umgekehrt zu machen: Da ein new[] jederzeit schiefgehen kann, hat obige Implementierung des Zuweisungsoperator den nachteil, daß der Zuweisungsoperator ggf. ein Ungültiges Objekt hinterläßt:
C++: |
class String { String& operator = (const String& quelle) // Zuweisungsoperator { delete [] this->text; this->laenge = quelle->laenge; this->text = new char[this->laenge]; // WEnn dies schief geht, wird eine bas_alloc exception geworfen strcpy(this->text, quelle->text); return *this; } };
|
Wir haben bereits mit delete[] den Speicher freigegeben und wenn das new[] schief geht, dann stehen wir im Regen. weil this->text auf nicht mehr gültigen Speicherbereich verweist. Um exceptionsichere Zuweisungsoperatoren zu schreiben bedient man sich ganz gerne einer Swap-methode (die die Inhalte der eigenen Instanz mit der einer anderen tauscht) und des Copyconstructors:
C++: |
class String { void swap(String& other) { std::swap(other->text, this->text); std::swap(other->laenge, this->laenge); } String& operator = (String& quelle) { String temp(quell); // Copyconstructor. Kann zwar eine Exception werfen, tut aber nicht weh swap(temp); // Unseren Inhalt mit dem vom Temp vertauschen return *this; // temp wird nun gelöscht, damit auch unserer alter Inhalt. } };
|
-- Gruß, virtual Quote of the Month Ich eß' nur was ein Gesicht hat (Creme 21) Dieser Post wurde am 28.06.2003 um 20:30 Uhr von virtual editiert. |