911 Stimmen

Was ist Object Slicing?

Was ist Objektslicing in C++ und wann kommt es vor?

34voto

Walter Bright Punkte 4171

Das Slicing-Problem ist schwerwiegend, da es zu einer Beschädigung des Speichers führen kann, und es ist sehr schwierig, zu garantieren, dass ein Programm nicht darunter leidet. Um es aus der Sprache herauszuhalten, sollten Klassen, die Vererbung unterstützen, nur über Referenzen (nicht über Werte) zugänglich sein. Die Programmiersprache D hat diese Eigenschaft.

Betrachten wir die Klasse A und die von A abgeleitete Klasse B. Der Speicher kann beschädigt werden, wenn der Teil A einen Zeiger p und eine Instanz B hat, die p auf die zusätzlichen Daten von B verweist. Wenn dann die zusätzlichen Daten abgeschnitten werden, zeigt p auf Müll.

3 Stimmen

Erläutern Sie bitte, wie es zu der Speicherbeschädigung kommen kann.

2 Stimmen

Gegeben: class A { virtual void foo() { } }; class B : A { int *p; void foo() { *p = 3; } }; Schneiden Sie nun ein B, wenn es A zugewiesen ist, rufen Sie foo() auf, das B::foo() aufruft, und voila! Speicherverfälschung durch Zuweisung von Garbage-Werten für B::p.

1 Stimmen

Ja, Walter vermischt eine Zeigerzuweisung (bei der A* auf ein B-Objekt zeigt, so dass B::p NICHT verloren geht) mit einer Objektzuweisung (nach der A::foo aufgerufen wird).

14voto

Sorush Punkte 2165

Ich sehe alle Antworten erwähnen, wenn Objekt Slicing geschieht, wenn Datenelemente gesliced werden. Hier gebe ich ein Beispiel, dass die Methoden nicht überschrieben werden:

class A{
public:
    virtual void Say(){
        std::cout<<"I am A"<<std::endl;
    }
};

class B: public A{
public:
    void Say() override{
        std::cout<<"I am B"<<std::endl;
    }
};

int main(){
   B b;
   A a1;
   A a2=b;

   b.Say(); // I am B
   a1.Say(); // I am A
   a2.Say(); // I am A   why???
}

B (Objekt b) wird von A (Objekt a1 und a2) abgeleitet. b und a1 rufen erwartungsgemäß ihre Mitgliedsfunktion auf. Aus der Sicht des Polymorphismus erwarten wir jedoch nicht, dass a2, das von b zugewiesen wird, nicht überschrieben wird. Im Grunde speichert a2 nur den Teil der Klasse A von b, und das ist Objekt-Slicing in C++.

Um dieses Problem zu lösen, sollte eine Referenz oder ein Zeiger verwendet werden

 A& a2=b;
 a2.Say(); // I am B

oder

A* a2 = &b;
a2->Say(); // I am B

0 Stimmen

But from polymorphism viewpoint we don’t expect a2, which is assigned by b, to not be overridden. Basically, a2 only saves A-class part of b and that is object slicing in C++. Das klingt nicht richtig und ist nicht klar. a2 ist der statische Typ A weshalb A::Say() aufgerufen wurde - ich glaube nicht, dass es mit Object Slicing zu tun hat.

13voto

Kartik Maheshwari Punkte 337

In C++ kann ein Objekt einer abgeleiteten Klasse einem Objekt einer Basisklasse zugewiesen werden, aber der andere Weg ist nicht möglich.

class Base { int x, y; };

class Derived : public Base { int z, w; };

int main() 
{
    Derived d;
    Base b = d; // Object Slicing,  z and w of d are sliced off
}

Beim Object Slicing wird ein Objekt einer abgeleiteten Klasse einem Objekt einer Basisklasse zugeordnet, wobei zusätzliche Attribute des Objekts einer abgeleiteten Klasse abgeschnitten werden, um das Objekt der Basisklasse zu bilden.

8voto

ididak Punkte 5654

Das Slicing-Problem in C++ ergibt sich aus der Wertesemantik seiner Objekte, die vor allem aufgrund der Kompatibilität mit C-Strukturen bestehen blieb. Sie müssen eine explizite Referenz- oder Zeigersyntax verwenden, um ein "normales" Objektverhalten zu erreichen, wie es in den meisten anderen Sprachen, die Objekte verwenden, anzutreffen ist, d.h. Objekte werden immer per Referenz weitergegeben.

Die kurze Antwort ist, dass Sie das Objekt aufteilen, indem Sie ein abgeleitetes Objekt einem Basisobjekt zuweisen nach Wert , d. h. das verbleibende Objekt ist nur ein Teil des abgeleiteten Objekts. Um die Wertesemantik zu erhalten, ist Slicing ein vernünftiges Verhalten und hat seine relativ seltenen Anwendungen, die es in den meisten anderen Sprachen nicht gibt. Einige Leute betrachten es als eine Eigenschaft von C++, während viele es als eine der Macken von C++ betrachten.

6 Stimmen

" "normales" Objektverhalten "Das ist kein "normales Objektverhalten", das ist Referenzsemantik . Und sie bezieht sich auf in keiner Weise mit C struct , Kompatibilität oder anderen Unsinn, den Ihnen ein beliebiger OOP-Priester erzählt.

4 Stimmen

@curiousguy Amen, Bruder. Es ist traurig zu sehen, wie oft C++ gebasht wird, weil es nicht Java ist, obwohl die Wertesemantik eines der Dinge ist, die C++ so wahnsinnig leistungsfähig machen.

0 Stimmen

Dies ist keine Besonderheit, keine Eigenart oder Unzulänglichkeit. Es ist ein normales On-Stack-Copying-Verhalten, da der Aufruf einer Funktion mit einem Argument oder einer Stack-Variable vom Typ Base muss genau sizeof(Base) Bytes im Speicher, mit möglicher Ausrichtung, vielleicht, das ist, warum "Zuweisung" (on-Stack-Copy) nicht abgeleitete Klasse Mitglieder kopieren, ihre Offsets sind außerhalb sizeof. Um "Datenverlust" zu vermeiden, verwenden Sie einfach Zeiger, wie jeder andere auch, da Zeigerspeicher in Ort und Größe fixiert ist, während Stack sehr volatil ist

7voto

Steve Steiner Punkte 5259

Also ... Warum ist der Verlust der abgeleiteten Informationen schlecht? ... weil der Autor der abgeleiteten Klasse die Darstellung so verändert haben kann, dass das Abschneiden der zusätzlichen Informationen den Wert verändert, der durch das Objekt dargestellt wird. Dies kann passieren, wenn die abgeleitete Klasse verwendet wird, um eine Darstellung zwischenzuspeichern, die für bestimmte Operationen effizienter ist, deren Rücktransformation in die Basisdarstellung aber teuer ist.

Ich dachte, man sollte auch erwähnen, was man tun sollte, um Slicing zu vermeiden... Besorgen Sie sich ein Exemplar der C++ Coding Standards, 101 rules guidlines, and best practices. Der Umgang mit Slicing ist #54.

Er schlägt ein etwas ausgeklügeltes Muster vor, um das Problem vollständig zu lösen: einen geschützten Kopierkonstruktor, ein geschütztes rein virtuelles DoClone und ein öffentliches Clone mit einer Assert, die Ihnen mitteilt, wenn eine (weitere) abgeleitete Klasse DoClone nicht korrekt implementiert hat. (Die Clone-Methode erstellt eine ordnungsgemäße Deep Copy des polymorphen Objekts.)

Sie können auch den Kopierkonstruktor auf der Basis explizit markieren, was ein explizites Slicing ermöglicht, wenn dies gewünscht ist.

3 Stimmen

" Sie können den Kopierkonstruktor auch in der Basis explizit markieren ", die die no überhaupt nicht helfen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X