Hallo zusammen,
heute wurde uns bei der MSDN Hotline unter anderem folgende Frage gestellt:
Ich schreibe ein C++ Programm. Ich habe Klassen in einer Vererbungshierarchie. Kopiere ich die Elemente nun über Referenzen zur Basisklasse, werden nicht alle Elemente kopiert.
Unsere Antwort bzw. unser Lösungsvorschlag darauf war:
Dies ist ein klassisches Problem in C++. Es ist auch unter dem Namen slicing bekannt. Das sieht etwa so aus:
//Beispiel Slicing:
#include <iostream>
class Base
{
public:
Base():a_(0){}
int a_;
virtual ~Base{}
};
class Child : Base
{
public:
Child():b_(0){}
int b_;
};
int main()
{
Child c1,c2;
c2.a_ = 5;
c2.b_ = 42;
Base& ref_b1 = c1;
ref_b1 = c2;
std::cout << c1.a_ << std::endl; //gibt 5 aus
std::cout << c1.b_ << std::endl; // immer noch 0
}
Das obige Codebeispiel demonstriert, dass c2 nur zur Hälfte kopiert wird. Die Elemente der Kindklasse werden "abgeschnitten" (engl. slicing). Dadurch, dass eine Referenz der Basisklasse verwendet wird, bestimmt der Compiler, dass der Zuweisungsoperator ("=") der Basisklasse zu verwenden ist.
Dieser Operator kennt nur die Elemente der Basisklasse in der er definiert ist und ignoriert alle Elemente der abgeleiteten Klasse. Dieses Verhalten ist in der Regel unerwünscht. Ein naiver (und sehr gefährlicher) Lösungsversuch wäre, einen virtuellen Zuweisungsoperator zu definieren. Dies würde im obigen Beispiel zwar funktionieren, aber lässt schon Fehler zu in Hierarchien mit mehr als einer abgeleitet Klasse. Außerdem wäre es nach wie vor möglich, einer Instanz der Basisklasse eine Instanz einer abgeleiteten Klasse zuzuweisen.
Die Lösung ist in Wahrheit ebenso effizient wie radikal. Die Faustregel besagt, dass Klassen in Vererbungshierarchien keine value Semantics haben sollten (also keinen Kopie und Zuweisungsoperator). Dies ist eine Faustregel (und es gibt Ausnahmen, wie immer), aber insbesondere bei polymorphen Klassen (solche, die virtuelle Methoden besitzen) sind value Semantics immer ein Design-Fehler und führen zu nichts als Ärger.
Da C++ automatisch einen Copy-Konstruktor und einen Zuweisungsoperator anlegt (eine schlechte Angewohnheit, die von C geerbt wurde. In C gibt es keine Vererbungshierarchien und daher stellt das dort kein Problem dar), muss man C++ nun überreden das bleiben zu lassen. Dies geschieht üblicherweise dadurch, dass man den Kopierkonstruktor und den Zuweisungsoperator als privat deklariert und nicht definiert:
//Beispiel Slicing:
#include <iostream>
class Base
{
public:
Base():a_(5){}
int a_;
virtual ~Base(){}
private:
Base& operator= ( const Base& );
Base ( const Base& );
};
class Child : public Base
{
public:
Child():b_(42){}
int b_;
};
int main()
{
Child c1,c2;
c2.b_ = 13;
Base& ref_b1 = c1;
ref_b1 = c2; //wirft jetzt einen Compiler-Fehler
//der Compiler bewahrt uns jetzt
//davor Unsinn zu machen
std::cout << c1.b_;
}
Diese Technik ist unter C++ Entwicklern so populär, dass es in C++0x dafür jetzt eigene Schlüsselwörter gibt:
class Base
{
//Zuweisung und Kopieren verhindern
Base( const Base& ) = delete;
Base& operator= ( const Base& ) = delete;
};
Wir hoffen, vielen Besuchern der MSDN Foren durch das Posten dieses Problems und einer möglichen Lösung weiterhelfen zu können.
Viele Grüße,
Markus Klein
MSDN Hotline für MSDN Online Deutschland
Disclaimer:
Bitte haben Sie Verständnis dafür, dass wir hier auf Rückfragen gar nicht oder nur sehr zeitverzögert antworten können.
Bitte nutzen Sie für Rückfragen oder neue Fragen den telefonischen Weg über die MSDN Hotline: http://www.msdn-online.de/Hotline

Es gelten für die MSDN Hotline und dieses Posting diese Nutzungsbedingungen, Hinweise zu Markenzeichen sowie die allgemein gültigen Informationen zur Datensicherheit sowie die gesonderten Nutzungsbedingungen für die MSDN Hotline.