2010-03-05 4 views
5

Object Slicing ist eine Sache, dass ein Objekt einige seiner Attribute oder Funktionen verliert, wenn eine Kindklasse der Basisklasse zugewiesen wird. soetwas wieObject Slicing, ist es von Vorteil?

Class A{ 

} 
Class B extends A{ 

} 

Class SomeClass{ 
A a = new A(); 
B b = new B(); 

// Some where if might happen like this */ 
a = b; (Object slicing happens) 

} 

sagen wir Objekt schneiden jede vorteilhaft in jeder Hinsicht ist? Wenn ja, kann mir jemand sagen, wie Objekt-Slicing in der Entwicklung hilfreich ist und wo es hilfreich sein könnte?

+3

C++? Da der Code nicht C++ ist, ist es unmöglich herauszufinden, ob hier aus C++ - Sicht ein Slicing stattfindet. – AnT

+3

Ich bin mir ziemlich sicher, dass Java Objekt Slicing nicht ausführt, also sollte dieses Tag aus dieser Frage entfernt werden. – ChrisH

+1

Überprüfen Sie den folgenden Link http://www.theserverside.com/tt/articles/article.tss?l=ObjectSlices – gmhk

Antwort

21

In C++ sollte man sich einen Objektschnitt als Umwandlung vom abgeleiteten Typ zum Basistyp [*] vorstellen. Ein brandneues Objekt entsteht, das "von einer wahren Geschichte inspiriert" wird.

Manchmal ist dies etwas, was Sie tun möchten, aber das Ergebnis ist in keinster Weise das gleiche Objekt wie das Original. Wenn Objekt-Slicing schief geht, ist es, wenn Leute nicht aufpassen und denken, dass es das gleiche Objekt oder eine Kopie davon ist.

Es ist normalerweise nicht vorteilhaft. Tatsächlich geschieht dies normalerweise versehentlich, wenn jemand an einem Wert vorbeikommt, wenn er als Referenz durchgehen wollte.

Es ist ziemlich schwierig, ein Beispiel zu finden, wenn Slicing definitiv das Richtige ist, denn es ist ziemlich schwierig (vor allem in C++), ein Beispiel zu finden, wo eine nicht abstrakte Basisklasse definitiv das Richtige ist etwas zu tun. Dies ist ein wichtiger Entwurfspunkt, und nicht einer, der leicht übergangen wird - wenn Sie feststellen, dass Sie ein Objekt entweder absichtlich oder zufällig schneiden, ist es sehr wahrscheinlich, dass Ihre Objekthierarchie falsch ist. Entweder sollte die Basisklasse nicht als Basisklasse verwendet werden, oder sie sollte mindestens eine reine virtuelle Funktion haben und daher nicht nach Wert skalierbar oder passierbar sein.

Also, jedes Beispiel, das ich gab, wo ein Objekt in ein Objekt seiner Basisklasse umgewandelt wird, würde zu Recht den Einwand provozieren: "Moment mal, was machst du überhaupt von einer konkreten Klasse?" . Wenn Schneiden zufällig ist, dann ist es wahrscheinlich ein Fehler, und wenn es absichtlich ist, dann ist es wahrscheinlich "Code-Geruch".

Aber die Antwort könnte „ja sein, OK, diese sollte nicht wirklich sein, wie die Dinge strukturiert sind, aber wenn man bedenkt, dass sie sind so strukturiert, ich brauche von der abgeleiteten Klasse auf die Basisklasse zu konvertieren , und das ist definitionsgemäß eine Scheibe ". In diesem Sinne, hier ist ein Beispiel:

struct Soldier { 
    string name; 
    string rank; 
    string serialNumber; 
}; 

struct ActiveSoldier : Soldier { 
    string currentUnit; 
    ActiveSoldier *commandingOfficer; // the design errors multiply! 
    int yearsService; 
}; 

template <typename InputIterator> 
void takePrisoners(InputIterator first, InputIterator last) { 
    while (first != last) { 
     Soldier s(*first); 
     // do some stuff with name, rank and serialNumber 
     ++first; 
    } 
} 

nun die Forderung der takePrisoners Funktionsschablone ist, dass seine Parameter ein Iterator für einen Typen konvertierbar Soldaten sein. Es muss keine abgeleitete Klasse sein, und wir greifen nicht direkt auf die Mitglieder "name" usw. zu, so takePrisoners hat versucht, die einfachste mögliche Schnittstelle zu bieten, die Einschränkungen (a) sollte mit Soldier, und funktionieren (b) sollte möglich sein, andere Typen zu schreiben, mit denen es auch arbeitet.

ActiveSoldier ist ein solcher anderer Typ. Aus Gründen, die nur dem Autor dieser Klasse am besten bekannt sind, hat er sich entschieden, öffentlich von Soldier zu erben, anstatt einen überladenen Konvertierungsoperator bereitzustellen. Wir können uns streiten, ob das jemals eine gute Idee ist, aber nehmen wir an, wir sind damit beschäftigt. Da es sich um eine abgeleitete Klasse handelt, ist sie in Soldier umwandelbar. Diese Umwandlung wird als Slice bezeichnet. Wenn wir also takePrisoners aufrufen und die begin() und end() Iteratoren für einen Vektor von ActiveSoldiers übergeben, werden wir sie in Scheiben schneiden.

Sie könnten wahrscheinlich ähnliche Beispiele für einen OutputIterator finden, bei dem der Empfänger nur den Basisklassen-Teil der zu liefernden Objekte interessiert und sie so in Scheiben schneiden kann, während sie in den Iterator geschrieben werden. Der Grund dafür ist, dass wir (a) das Umschreiben von ActiveSoldier und (b) das Ändern von Soldier so berücksichtigen sollten, dass auf Funktionen statt auf Mitgliederzugriff zugegriffen werden kann, damit wir diese Funktionen abstrahieren können als eine Schnittstelle, die andere Typen unabhängig voneinander implementieren können, so dass takePrisoners nicht zu Soldier konvertieren muss. Beides würde die Notwendigkeit eines Slices beseitigen und hätte potenzielle Vorteile für die Leichtigkeit, mit der unser Code in Zukunft erweitert werden kann.

[*] weil es eins ist. Die letzten beiden Zeilen weiter unten tun das Gleiche:

struct A { 
    int value; 
    A(int v) : value(v) {} 
}; 

struct B : A { 
    int quantity; 
    B(int v, int q) : A(v), quantity(q) {} 
}; 

int main() { 
    int i = 12; // an integer 
    B b(12, 3); // an instance of B 
    A a1 = b; // (1) convert B to A, also known as "slicing" 
    A a2 = i; // (2) convert int to A, not known as "slicing" 
} 

Der einzige Unterschied besteht darin, dass (1) ruft eine Kopie Konstruktor (dass der Compiler liefert auch wenn der Code nicht), während (2) eine der Anrufe int Konstruktor

Wie jemand anders sagte, macht Java keine Objekt-Slicing. Wenn der von Ihnen bereitgestellte Code in Java umgewandelt würde, würde keine Art von Objekt-Slicing passieren. Java-Variablen sind Referenzen, keine Objekte. Die Nachbedingung von a = b ist also nur, dass die Variable "a" auf dasselbe Objekt wie die Variable "b" verweist - Änderungen über eine Referenz können über die andere Referenz gesehen werden, und so weiter. Sie beziehen sich nur auf einen anderen Typ, der Teil des Polymorphismus ist. Eine typische Analogie dazu ist, dass ich eine Person als "meinen Bruder" [**] bezeichnen könnte, und jemand anders könnte sich die gleiche Person wie "mein Vikar" vorstellen. Gleiches Objekt, andere Schnittstelle.

Sie können die Java-ähnlichen Effekt in C++ erhalten Zeiger oder Verweise verwenden:

B b(24,7); 
A *a3 = &b; // No slicing - a3 is a pointer to the object b 
A &a4 = b; // No slicing - a4 is a reference to (pseudonym for) the object b 

[**] In der Tat, mein Bruder kein Pfarrer ist.

+0

Die Frage, ob es möglich sein sollte, eine Instanz von "A" zu Code zu übergeben, der eine Instanz von "B" erwartet, sollte meiner Meinung nach orthogonal zur Frage sein, ob es möglich sein sollte, einen Verweis auf "A" zu übergeben um zu erwarten, dass ein Verweis auf "B" erwartet wird. Selbst wenn man auf ein Framework wie .NET beschränkt wäre, das keine Kopierkonstruktoren hat, gibt es viele Situationen, in denen das Kopieren eines Speicherplatzes eines Strukturtyps durch Kopieren in einen Speicherort eines anderen zu einer legitimen Instanz des letzteren Typs führt; Ich wünschte, das Framework hätte eine Möglichkeit, dies auszudrücken, vor allem für ... – supercat

+0

... Strukturen wie 'KeyValuePair ', deren einziger Zweck darin besteht, eine feste Menge unabhängiger Werte zu kapseln. – supercat

+0

@supercat: Sie fragen also nach 'reininterpret_cast'? Ich bin mir ziemlich sicher, dass es für jedes Low-Level-Feature, das nicht in einem High-Level-Framework enthalten ist, seltene Fälle geben wird, in denen Sie wünschen, dass Sie es haben. Und als Konsequenz für jeden Umstand in .NET, wo der einzige Weg ist, etwas zu tun, was C- oder C++ - Code ist, um Bytes direkt zu inspizieren, würden Sie wünschen, dass Sie sich nicht um die zusätzliche Quelldatei usw. kümmern müssten. –