ऑब्जेक्ट स्लाइसिंग क्या है?

Nov 08 2008

किसी ने इसका उल्लेख आईआरसी में टुकड़ा करने की समस्या के रूप में किया।

जवाब

635 DavidDibben Nov 08 2008 at 18:22

"स्लाइसिंग" वह जगह है जहां आप एक व्युत्पन्न वर्ग की वस्तु को बेस क्लास के उदाहरण के लिए असाइन करते हैं, जिससे जानकारी का कुछ हिस्सा खो जाता है - इसमें से कुछ "स्लाइस" हो जाता है।

उदाहरण के लिए,

class A {
   int foo;
};

class B : public A {
   int bar;
};

तो एक प्रकार Bकी वस्तु में दो डेटा सदस्य होते हैं, fooऔर bar

तो अगर आप यह लिखने के लिए थे:

B b;

A a = b;

फिर bसदस्य के बारे में जानकारी barखो जाती है a

530 fgp Jan 22 2013 at 22:00

यहां अधिकांश उत्तर यह स्पष्ट करने में विफल हैं कि स्लाइसिंग के साथ वास्तविक समस्या क्या है। वे केवल टुकड़ा करने के सौम्य मामलों की व्याख्या करते हैं, विश्वासघाती लोगों की नहीं। दूसरे उत्तरों की तरह मान लें, कि आप दो वर्गों के साथ काम कर रहे हैं Aऔर B, जहाँ Bसे (सार्वजनिक रूप से) प्राप्त होता है A

इस स्थिति में, सी ++ आप का एक उदाहरण से पारित की सुविधा देता है Bके लिए Aकी असाइनमेंट ऑपरेटर (और यह भी प्रति निर्माता के लिए)। यह काम करता है क्योंकि एक उदाहरण Bको एक में परिवर्तित किया जा सकता है const A&, जो कि असाइनमेंट ऑपरेटर और कॉपी-कंस्ट्रक्टर अपने तर्क होने की उम्मीद करते हैं।

सौम्य मामला

B b;
A a = b;

वहां कुछ भी बुरा नहीं होता है - आपने एक उदाहरण के लिए कहा, Aजिसकी एक प्रति है B, और वास्तव में आपको यही मिलता है। ज़रूर, aइसमें कुछ bसदस्य नहीं होंगे, लेकिन यह कैसे होना चाहिए? यह एक है A, सब के बाद, नहीं B, तो यह इन सदस्यों के बारे में भी नहीं सुना है , अकेले उन्हें स्टोर करने में सक्षम हो जाएगा।

विश्वासघाती मामला

B b1;
B b2;
A& a_ref = b2;
a_ref = b1;
//b2 now contains a mixture of b1 and b2!

आप सोच सकते हैं कि बाद की b2प्रति होगी b1। लेकिन, अफसोस, यह नहीं है ! यदि आप इसका निरीक्षण करते हैं, तो आपको पता चलता है कि b2एक फ्रेंकस्टीनियन प्राणी है, जो कुछ चूजों से बना है b1( Bचूजों से जो विरासत में मिला है A), और कुछ चूजों b2(केवल उनमें Bशामिल हैं)। आउच!

क्या हुआ? खैर, सी ++ डिफ़ॉल्ट रूप से असाइनमेंट ऑपरेटरों के रूप में व्यवहार नहीं करता है virtual। इस प्रकार, रेखा a_ref = b1असाइनमेंट ऑपरेटर को कॉल करेगी A, इसके बारे में नहीं B। ऐसा इसलिए है क्योंकि गैर-आभासी कार्यों के लिए, घोषित (औपचारिक रूप से: स्थैतिक ) प्रकार (जो है A&) निर्धारित करता है कि कौन सा फ़ंक्शन कहा जाता है, वास्तविक (औपचारिक रूप से: गतिशील ) प्रकार के विपरीत (जो होगा B, a_refसंदर्भ के बाद से B) । अब, Aअसाइनमेंट ऑपरेटर स्पष्ट रूप से केवल घोषित सदस्यों के बारे में जानता है A, इसलिए यह केवल उन लोगों को कॉपी करेगा, जो Bअपरिवर्तित में जोड़े गए सदस्यों को छोड़ देंगे ।

एक समाधान

केवल एक वस्तु के कुछ हिस्सों को असाइन करना आमतौर पर बहुत कम समझ में आता है, फिर भी C ++, दुर्भाग्य से, इसे मना करने का कोई अंतर्निहित तरीका प्रदान नहीं करता है। हालाँकि, आप अपना स्वयं का रोल कर सकते हैं। पहला चरण असाइनमेंट ऑपरेटर को आभासी बना रहा है । यह गारंटी देगा कि यह हमेशा वास्तविक प्रकार का असाइनमेंट ऑपरेटर है जिसे कहा जाता है, घोषित प्रकार का नहीं। दूसरा चरण यह dynamic_castसत्यापित करने के लिए उपयोग करना है कि निर्दिष्ट ऑब्जेक्ट में एक संगत प्रकार है। तीसरे चरण एक (सुरक्षित!) सदस्य में वास्तविक काम करना है assign(), के बाद से Bकी assign()शायद का उपयोग करना चाहते जाएगा A'एस assign()कॉपी करने के लिए Aकी, सदस्य हैं।

class A {
public:
  virtual A& operator= (const A& a) {
    assign(a);
    return *this;
  }

protected:
  void assign(const A& a) {
    // copy members of A from a to this
  }
};

class B : public A {
public:
  virtual B& operator= (const A& a) {
    if (const B* b = dynamic_cast<const B*>(&a))
      assign(*b);
    else
      throw bad_assignment();
    return *this;
  }

protected:
  void assign(const B& b) {
    A::assign(b); // Let A's assign() copy members of A from b to this
    // copy members of B from b to this
  }
};

ध्यान दें कि, शुद्ध सुविधा के लिए, B's operator=covariantly, वापसी प्रकार ओवरराइड करता है के बाद से यह जानता है कि यह का एक उदाहरण लौटा रहा है B

158 Black Nov 08 2008 at 18:28

यदि आपके पास एक आधार वर्ग Aऔर एक व्युत्पन्न वर्ग है B, तो आप निम्न कार्य कर सकते हैं।

void wantAnA(A myA)
{
   // work with myA
}

B derived;
// work with the object "derived"
wantAnA(derived);

अब विधि wantAnAकी एक प्रति की आवश्यकता है derived। हालाँकि, ऑब्जेक्ट derivedको पूरी तरह से कॉपी नहीं किया जा सकता है, क्योंकि वर्ग Bअतिरिक्त सदस्य चर का आविष्कार कर सकता है जो इसके आधार वर्ग में नहीं हैं A

इसलिए, कॉल करने के लिए wantAnA, संकलित व्युत्पन्न वर्ग के सभी अतिरिक्त सदस्यों को "स्लाइस ऑफ" करेगा। परिणाम एक ऐसी वस्तु हो सकती है जिसे आप बनाना नहीं चाहते थे, क्योंकि

  • यह अधूरा हो सकता है,
  • यह एक A-object की तरह व्यवहार करता है (वर्ग Bका सभी विशेष व्यवहार खो जाता है)।
42 geh Aug 23 2014 at 01:33

ये सभी अच्छे उत्तर हैं। मैं केवल एक निष्पादन उदाहरण जोड़ना चाहूंगा जब संदर्भ द्वारा वस्तुओं को संदर्भ के आधार पर पारित किया जाएगा:

#include <iostream>

using namespace std;

// Base class
class A {
public:
    A() {}
    A(const A& a) {
        cout << "'A' copy constructor" << endl;
    }
    virtual void run() const { cout << "I am an 'A'" << endl; }
};

// Derived class
class B: public A {
public:
    B():A() {}
    B(const B& a):A(a) {
        cout << "'B' copy constructor" << endl;
    }
    virtual void run() const { cout << "I am a 'B'" << endl; }
};

void g(const A & a) {
    a.run();
}

void h(const A a) {
    a.run();
}

int main() {
    cout << "Call by reference" << endl;
    g(B());
    cout << endl << "Call by copy" << endl;
    h(B());
}

आउटपुट है:

Call by reference
I am a 'B'

Call by copy
'A' copy constructor
I am an 'A'
30 TheArchetypalPaul Nov 08 2008 at 18:14

"C ++ स्लाइसिंग" के लिए Google में तीसरा मैच मुझे यह विकिपीडिया लेख देता है http://en.wikipedia.org/wiki/Object_slicing और यह (गर्म, लेकिन पहले कुछ पोस्ट समस्या को परिभाषित करते हैं): http://bytes.com/forum/thread163565.html

तो यह तब होता है जब आप सुपर क्लास को एक उपवर्ग का ऑब्जेक्ट सौंपते हैं। सुपरक्लास को उपवर्ग में अतिरिक्त जानकारी के बारे में कुछ नहीं पता है, और इसे स्टोर करने के लिए जगह नहीं मिली है, इसलिए अतिरिक्त जानकारी "कटा हुआ" हो जाती है।

यदि वे लिंक "अच्छे उत्तर" के लिए पर्याप्त जानकारी नहीं देते हैं, तो कृपया अपने प्रश्न को संपादित करें ताकि हमें पता चल सके कि आप और क्या देख रहे हैं।

29 WalterBright Nov 08 2008 at 18:56

स्लाइसिंग की समस्या गंभीर है क्योंकि इसके परिणामस्वरूप स्मृति भ्रष्टाचार हो सकता है, और यह गारंटी देना बहुत मुश्किल है कि किसी कार्यक्रम को इससे नुकसान न हो। इसे भाषा से डिजाइन करने के लिए, विरासत का समर्थन करने वाली कक्षाएं केवल संदर्भ द्वारा (मूल्य से नहीं) सुलभ होनी चाहिए। डी प्रोग्रामिंग भाषा में यह संपत्ति है।

क्लास ए पर विचार करें, और ए। मेमोरी भ्रष्टाचार से उत्पन्न वर्ग बी हो सकता है यदि ए के हिस्से में एक पॉइंटर पी और बी उदाहरण है जो बी के अतिरिक्त डेटा को इंगित करता है। फिर, जब अतिरिक्त डेटा बंद हो जाता है, तो पी कचरा की ओर इशारा करता है।

11 KartikMaheshwari Mar 07 2018 at 16:35

C ++ में, एक व्युत्पन्न वर्ग वस्तु को एक बेस क्लास ऑब्जेक्ट को सौंपा जा सकता है, लेकिन दूसरा तरीका संभव नहीं है।

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
}

ऑब्जेक्ट स्लाइसिंग तब होती है जब एक व्युत्पन्न वर्ग ऑब्जेक्ट को बेस क्लास ऑब्जेक्ट को सौंपा जाता है, बेस क्लास ऑब्जेक्ट बनाने के लिए एक व्युत्पन्न क्लास ऑब्जेक्ट की अतिरिक्त विशेषताओं को कटा हुआ होता है।

7 SteveSteiner Nov 09 2008 at 00:38

तो ... क्यों व्युत्पन्न जानकारी खराब हो रही है? ... क्योंकि व्युत्पन्न वर्ग के लेखक ने प्रतिनिधित्व को बदल दिया हो सकता है जैसे कि अतिरिक्त जानकारी को हटाने से वस्तु द्वारा दर्शाए जा रहे मूल्य में परिवर्तन होता है। यह तब हो सकता है यदि व्युत्पन्न वर्ग अगर एक प्रतिनिधित्व को कैश करने के लिए उपयोग किया जाता है जो कुछ कार्यों के लिए अधिक कुशल है, लेकिन आधार प्रतिनिधित्व में वापस बदलने के लिए महंगा है।

यह भी सोचा कि किसी को भी उल्लेख करना चाहिए कि आपको स्लाइसिंग से बचने के लिए क्या करना चाहिए ... C ++ कोडिंग मानकों की एक प्रति प्राप्त करें, 101 नियम दिशानिर्देश और सर्वोत्तम अभ्यास। टुकड़ा करने की क्रिया # 54 है।

It suggests a somewhat sophisticated pattern to fully deal with the issue: have a protected copy constructor, a protected pure virtual DoClone, and a public Clone with an assert which will tell you if a (further) derived class failed to implement DoClone correctly. (The Clone method makes a proper deep copy of the polymorphic object.)

You can also mark the copy constructor on the base explicit which allows for explicit slicing if it is desired.

7 ididak Nov 09 2008 at 07:31

The slicing problem in C++ arises from the value semantics of its objects, which remained mostly due to compatibility with C structs. You need to use explicit reference or pointer syntax to achieve "normal" object behavior found in most other languages that do objects, i.e., objects are always passed around by reference.

The short answers is that you slice the object by assigning a derived object to a base object by value, i.e. the remaining object is only a part of the derived object. In order to preserve value semantics, slicing is a reasonable behavior and has its relatively rare uses, which doesn't exist in most other languages. Some people consider it a feature of C++, while many considered it one of the quirks/misfeatures of C++.

6 haberdar Jan 29 2012 at 01:00

1. THE DEFINITION OF SLICING PROBLEM

If D is a derived class of the base class B, then you can assign an object of type Derived to a variable (or parameter) of type Base.

EXAMPLE

class Pet
{
 public:
    string name;
};
class Dog : public Pet
{
public:
    string breed;
};

int main()
{   
    Dog dog;
    Pet pet;

    dog.name = "Tommy";
    dog.breed = "Kangal Dog";
    pet = dog;
    cout << pet.breed; //ERROR

Although the above assignment is allowed, the value that is assigned to the variable pet loses its breed field. This is called the slicing problem.

2. HOW TO FIX THE SLICING PROBLEM

To defeat the problem, we use pointers to dynamic variables.

EXAMPLE

Pet *ptrP;
Dog *ptrD;
ptrD = new Dog;         
ptrD->name = "Tommy";
ptrD->breed = "Kangal Dog";
ptrP = ptrD;
cout << ((Dog *)ptrP)->breed; 

In this case, none of the data members or member functions of the dynamic variable being pointed to by ptrD (descendant class object) will be lost. In addition, if you need to use functions, the function must be a virtual function.

4 Minok Jul 25 2009 at 02:45

It seems to me, that slicing isn't so much a problem other than when your own classes and program are poorly architected/designed.

If I pass a subclass object in as a parameter to a method, which takes a parameter of type superclass, I should certainly be aware of that and know the internally, the called method will be working with the superclass (aka baseclass) object only.

It seems to me only the unreasonable expectation that providing a subclass where a baseclass is requested, would somehow result in subclass specific results, would cause slicing to be a problem. Its either poor design in the use of the method or a poor subclass implementation. I'm guessing its usually the result of sacrificing good OOP design in favor of expediency or performance gains.

3 Dude Oct 18 2012 at 10:22

OK, I'll give it a try after reading many posts explaining object slicing but not how it becomes problematic.

The vicious scenario that can result in memory corruption is the following:

  • Class provides (accidentally, possibly compiler-generated) assignment on a polymorphic base class.
  • Client copies and slices an instance of a derived class.
  • Client calls a virtual member function that accesses the sliced-off state.
3 Santosh Mar 13 2014 at 01:08

Slicing means that the data added by a subclass are discarded when an object of the subclass is passed or returned by value or from a function expecting a base class object.

Explanation: Consider the following class declaration:

           class baseclass
          {
                 ...
                 baseclass & operator =(const baseclass&);
                 baseclass(const baseclass&);
          }
          void function( )
          {
                baseclass obj1=m;
                obj1=m;
          }

As baseclass copy functions don't know anything about the derived only the base part of the derived is copied. This is commonly referred to as slicing.

1 quidkid Nov 29 2012 at 19:32
class A 
{ 
    int x; 
};  

class B 
{ 
    B( ) : x(1), c('a') { } 
    int x; 
    char c; 
};  

int main( ) 
{ 
    A a; 
    B b; 
    a = b;     // b.c == 'a' is "sliced" off
    return 0; 
}
1 Sorush Sep 12 2020 at 18:27

I see all the answers mention when object slicing happens when data members are sliced. Here I give an example that the methods are not overridden:

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 (object b) is derived from A (object a1 and a2). b and a1, as we expect, call their member function. 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++.

To solve this problem, a reference or pointer should be used

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

or

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

For more details see my post