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


जवाबों:


608

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

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

class A {
   int foo;
};

class B : public A {
   int bar;
};

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

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

B b;

A a = b;

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


66
बहुत जानकारीपूर्ण है, लेकिन विधि कॉल के दौरान स्लाइसिंग कैसे होती है (जो सादे असाइनमेंट उदाहरण से थोड़ा बेहतर खतरे को रेखांकित करता है) के एक उदाहरण के लिए stackoverflow.com/questions/274626#274636 देखें ।
ब्लेयर कॉनराड

55
दिलचस्प। मैं 15 वर्षों से C ++ में प्रोग्रामिंग कर रहा हूं और यह समस्या मेरे लिए कभी नहीं हुई, क्योंकि मैंने हमेशा दक्षता और व्यक्तिगत शैली के रूप में संदर्भ द्वारा वस्तुओं को पारित किया है। यह दिखाने के लिए जाता है कि कितनी अच्छी आदतें आपकी मदद कर सकती हैं।
कार्ल बेज़ेलफेल्ट

10
@ फेलिक्स थैंक्स लेकिन मुझे नहीं लगता कि कास्टिंग (क्योंकि कोई पॉइन्ट अंकगणित नहीं) काम करेगा, A a = b; aअब Aउस प्रकार की वस्तु है जिसकी कॉपी है B::foo। यह अब मुझे लगता है कि इसे कास्ट करने के लिए गलती होगी।

37
यह "स्लाइसिंग" नहीं है, या कम से कम इसका एक सौम्य संस्करण है। असली समस्या तब होती है जब आप करते हैं B b1; B b2; A& b2_ref = b2; b2 = b1। आप सोच सकते हैं कि आपने नकल की b1है b2, लेकिन आपने नहीं की है! तुम एक की नकल की है भाग के b1लिए b2(का हिस्सा b1है कि Bसे विरासत में मिली Aहै), और के अन्य भागों के लिए छोड़ दिया b2अपरिवर्तित। b2अब एक फ्रेंकस्टीनियन प्राणी है, जिसमें b1कुछ विखंडू होते हैं b2। ओह! डाउनवोटिंग क्योंकि मुझे लगता है कि इसका उत्तर बहुत भ्रामक है।
fgp

24
@fgp आपकी टिप्पणी को पढ़ना चाहिए B b1; B b2; A& b2_ref = b2; b2_ref = b1" वास्तविक समस्या तब होती है जब आप " ... एक गैर-आभासी असाइनमेंट ऑपरेटर के साथ एक वर्ग से प्राप्त करते हैं। है Aभी व्युत्पत्ति लिए करना? इसका कोई आभासी कार्य नहीं है। यदि आप एक प्रकार से प्राप्त करते हैं, तो आपको इस तथ्य से निपटना होगा कि इसके सदस्य कार्यों को कहा जा सकता है!
जिज्ञासु

508

यहां अधिकांश उत्तर यह स्पष्ट करने में विफल हैं कि स्लाइसिंग के साथ वास्तविक समस्या क्या है। वे केवल टुकड़ा करने के सौम्य मामलों की व्याख्या करते हैं, विश्वासघाती लोगों की नहीं। दूसरे उत्तरों की तरह मान लें, कि आप दो वर्गों के साथ काम कर रहे हैं 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


11
IMHO, समस्या यह है कि दो अलग-अलग प्रकार की प्रतिस्थापन हैं जो विरासत द्वारा निहित हो सकते हैं: या तो किसी derivedमूल्य को उम्मीद करने वाले कोड को दिया जा सकता है base, या किसी व्युत्पन्न संदर्भ को आधार संदर्भ के रूप में उपयोग किया जा सकता है। मैं एक भाषा को एक प्रकार की प्रणाली के साथ देखना चाहता हूं जो दोनों अवधारणाओं को अलग-अलग संबोधित करता है। ऐसे कई मामले हैं जहां एक व्युत्पन्न संदर्भ को आधार संदर्भ के लिए प्रतिस्थापित किया जाना चाहिए, लेकिन व्युत्पन्न उदाहरणों को आधार के लिए प्रतिस्थापित नहीं किया जाना चाहिए; ऐसे कई मामले भी हैं जहां उदाहरण परिवर्तनीय होने चाहिए लेकिन संदर्भ स्थानापन्न नहीं होने चाहिए।
सुपरकैट

16
मुझे समझ नहीं आ रहा है कि आपके "विश्वासघाती" मामले में क्या बुरा है। आपने कहा था कि आप चाहते हैं कि: 1) कक्षा A और 2 के ऑब्जेक्ट का संदर्भ प्राप्त करें) क्लास B के लिए ऑब्जेक्ट B1 को डालें और क्लास A के संदर्भ में उसके सामान की प्रतिलिपि बनाएँ। वास्तव में यहाँ क्या गलत है, इसके पीछे उचित तर्क है दिया गया कोड। दूसरे शब्दों में, आपने एक छोटी छवि फ़्रेम (ए) ली, इसे एक बड़ी छवि (बी) के ऊपर रखा और आपने उस फ्रेम के माध्यम से पेंट किया, बाद में शिकायत की कि आपकी बड़ी छवि अब बदसूरत लगती है :) लेकिन अगर हम सिर्फ उस फ़्रेमयुक्त क्षेत्र पर विचार करते हैं, यह बहुत अच्छा लग रहा है, जैसा चित्रकार चाहता था, ठीक है? :)
म्लादेन बी।

12
समस्या यह है कि अलग-अलग तरीके से, सी + + डिफ़ॉल्ट रूप से एक बहुत मजबूत प्रकार की प्रतिस्थापन योग्यता मानता है योग्यता है - इसके लिए आधार वर्ग के संचालन की आवश्यकता होती है ताकि उप-वर्ग उदाहरणों पर सही ढंग से काम किया जा सके। और वह भी संचालन के लिए जो संकलक ने असाइनमेंट की तरह ऑटोजेनर किया। तो इस संबंध में अपने स्वयं के संचालन को खराब न करने के लिए पर्याप्त नहीं है, आपको कंपाइलर द्वारा उत्पन्न गलत लोगों को भी स्पष्ट रूप से अक्षम करना होगा। या निश्चित रूप से, सार्वजनिक विरासत से दूर रहें, जो आमतौर पर एक अच्छा सुझाव है; ;-)
fgp

14
एक अन्य आम तरीका यह है कि बस कॉपी और असाइनमेंट ऑपरेटर को निष्क्रिय कर दें। वंशानुक्रम पदानुक्रम के भीतर वर्गों के लिए, आमतौर पर संदर्भ या सूचक के बजाय मूल्य का उपयोग करने का कोई कारण नहीं है।
सियुआन रेन

13
क्या? मुझे नहीं पता था कि ऑपरेटरों को वर्चुअल
पॉलम

153

यदि आपके पास एक आधार वर्ग 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का सभी विशेष व्यवहार खो जाता है)।

40
C ++ जावा नहीं है! यदि wantAnA(जैसा कि इसके नाम का अर्थ है!) एक चाहता है A, तो यह वही है जो इसे प्राप्त करता है। और A, इच्छा, उह, का एक उदाहरण एक की तरह व्यवहार करता है A। यह आश्चर्य की बात कैसे है?
fgp

82
@ एफजीपी: यह आश्चर्य की बात है, क्योंकि आप को फ़ंक्शन में पास नहीं करते हैं।
ब्लैक

10
@ एफजीपी: व्यवहार समान है। हालांकि, औसत C ++ प्रोग्रामर के लिए यह कम स्पष्ट हो सकता है। जहां तक ​​मुझे सवाल समझ में आया, कोई भी "शिकायत" नहीं कर रहा है। यह सिर्फ इस बारे में है कि कंपाइलर कैसे स्थिति को संभालता है। इम्हो, गुज़रना (कांस्टीट्यूशन) सन्दर्भों से बिल्कुल भी परहेज करना बेहतर है।
ब्लैक अप

8
@ThomasW नहीं, मैं विरासत से बाहर नहीं फेंकूंगा, लेकिन संदर्भों का उपयोग करूंगा। अगर wantAnA के हस्ताक्षर शून्य चाहते हैं (const A & myA) , तो स्लाइस नहीं किया गया था। इसके बजाय, कॉलर की ऑब्जेक्ट के लिए केवल-पढ़ने के लिए संदर्भ दिया गया है।
काली

14
समस्या ज्यादातर स्वचालित कास्टिंग पर होती है जो कंपाइलर derivedटाइप से करता है A। इंप्लिक्ट कास्टिंग हमेशा C ++ में अप्रत्याशित व्यवहार का एक स्रोत है, क्योंकि अक्सर स्थानीय रूप से कोड को देखने से समझना मुश्किल है कि एक कास्ट जगह है।
पक्नेट

41

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

#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'

हैलो। शानदार जवाब लेकिन मेरा एक सवाल है। अगर मैं ऐसा कुछ करता हूं ** देव डी; आधार * b = & d; ** स्लाइसिंग भी होती है
एड्रियन

@ एड्रियन यदि आप व्युत्पन्न वर्ग में कुछ नए सदस्य कार्य या सदस्य चर पेश करते हैं, तो वे सीधे आधार वर्ग सूचक से पहुंच योग्य नहीं हैं। हालाँकि आप अभी भी उन्हें ओवरलोड बेस क्लास वर्चुअल फ़ंक्शंस के अंदर से एक्सेस कर सकते हैं। इसे देखें: godbolt.org/z/LABx33
विशाल शर्मा

30

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

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

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


29

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

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


3
कृपया बताएं कि स्मृति भ्रष्टाचार कैसे हो सकता है।
रात्रि

4
मैं भूल गया कि प्रतिलिपि ctor vptr, मेरी गलती को रीसेट करेगा। लेकिन आप अभी भी भ्रष्टाचार प्राप्त कर सकते हैं यदि ए में एक पॉइंटर है, और बी सेट करता है कि बी के अनुभाग में इंगित करने के लिए जो कटा हुआ हो जाता है।
वाल्टर ब्राइट

18
यह समस्या केवल स्लाइसिंग तक सीमित नहीं है। किसी भी वर्ग जिसमें पॉइंटर्स होते हैं, एक डिफॉल्ट असाइनमेंट ऑपरेटर और कॉपी-कंस्ट्रक्टर के साथ संदिग्ध व्यवहार करते हैं।
वीवेबल

2
@Weeble - यही वजह है कि आप इन मामलों में डिफॉल्ट डिस्ट्रक्टर, असाइनमेंट ऑपरेटर और कॉपी-कंस्ट्रक्टर को ओवरराइड करते हैं।
बज्कर फ्रायंड-हेंसन

7
@Weeble: सामान्य सूचक फ़िक्सअप की तुलना में ऑब्जेक्ट स्लाइसिंग को क्या बदतर बनाता है, यह सुनिश्चित करने के लिए कि आपने स्लाइसिंग को होने से रोका है, एक बेस क्लास को प्रत्येक व्युत्पन्न वर्ग के लिए कंवर्टिंग कंस्ट्रक्टर प्रदान करना होगा । (क्यों? कोई भी व्युत्पन्न वर्ग जो छूट गए हैं उन्हें बेस क्लास के कॉपी कॉर्ट द्वारा उठाए जाने की संभावना है, क्योंकि Derivedयह अनुमानित रूप से परिवर्तनीय है Base।) यह स्पष्ट रूप से ओपन-क्लोज्ड प्रिंसिपल और एक बड़ा रखरखाव बोझ के लिए काउंटर है।
j_random_hacker 12

10

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
}

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


8

C ++ में स्लाइसिंग समस्या इसकी वस्तुओं के मूल्य शब्दार्थ से उत्पन्न होती है, जो ज्यादातर C संरचनाओं के साथ संगतता के कारण बनी हुई है। आपको अधिकांश अन्य भाषाओं में पाए जाने वाले "सामान्य" ऑब्जेक्ट व्यवहार को प्राप्त करने के लिए स्पष्ट संदर्भ या पॉइंटर सिंटैक्स का उपयोग करने की आवश्यकता है, जो ऑब्जेक्ट्स करते हैं, अर्थात, ऑब्जेक्ट्स को हमेशा संदर्भ द्वारा पास किया जाता है।

संक्षिप्त उत्तर यह है कि आप किसी वस्तु को मान द्वारा आधार वस्तु पर नियत करके वस्तु का टुकड़ा करते हैं , अर्थात शेष वस्तु व्युत्पन्न वस्तु का ही एक हिस्सा है। मूल्य शब्दार्थों को संरक्षित करने के लिए, स्लाइसिंग एक उचित व्यवहार है और इसके अपेक्षाकृत दुर्लभ उपयोग हैं, जो अधिकांश अन्य भाषाओं में मौजूद नहीं है। कुछ लोग इसे C ++ की विशेषता मानते हैं, जबकि कई इसे C ++ के क्विर्क / मिसफिटर्स में से एक मानते हैं।


5
" " सामान्य "वस्तु व्यवहार " यह "सामान्य वस्तु व्यवहार" नहीं है, यह संदर्भ अर्थ है । और यह सी , संगतता, या अन्य गैर-भावना के साथ किसी भी तरह से संबंधित structनहीं है जो किसी भी यादृच्छिक ओओपी पुजारी ने आपको बताया था।
जिज्ञासु

4
@ कुरसीगुई आमीन, भाई। यह देखकर दुख होता है कि कितनी बार C ++ जावा नहीं होने से धराशायी हो जाता है, जब मूल्य शब्दार्थ एक ऐसी चीज है जो C ++ को इतनी शक्तिशाली बना देता है।
fgp

यह एक सुविधा नहीं है, एक क्विक / मिसफिट्योर नहीं है। यह सामान्य ऑन-स्टैक-कॉपी करने वाला व्यवहार है, क्योंकि एक अरग या (एक ही) प्रकार के स्टैक वेरिएबल के साथ किसी फ़ंक्शन को कॉल करने के लिए , संभव संरेखण के साथ मेमोरी में Baseबिल्कुल sizeof(Base)बाइट्स लेना चाहिए , शायद, इसलिए "असाइनमेंट" (ऑन-स्टैक-कॉपी ) व्युत्पन्न वर्ग के सदस्यों की नकल नहीं करेंगे, उनके ऑफ़सेट आकार के बाहर हैं। "डेटा खोने" से बचने के लिए, बस सूचक का उपयोग करें, किसी और की तरह, क्योंकि सूचक मेमोरी जगह और आकार में तय की गई है, जबकि स्टैक बहुत ही अस्थिर है
क्रॉल

निश्चित रूप से सी ++ का एक मिसफिट है। किसी व्युत्पन्न वस्तु को आधार वस्तु के लिए नियत किया जाना चाहिए, जबकि किसी व्युत्पन्न वस्तु को किसी संदर्भ में बाँधना या आधार वर्ग के सूचक को ठीक होना चाहिए।
जॉन जेड। ली

7

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

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

यह समस्या से पूरी तरह से निपटने के लिए कुछ हद तक परिष्कृत पैटर्न का सुझाव देता है: एक संरक्षित प्रतिलिपि निर्माता, एक संरक्षित शुद्ध आभासी DoClone और एक सार्वजनिक क्लोन है जो आपको बताएगा कि क्या एक (आगे) व्युत्पन्न वर्ग DoClone को सही तरीके से लागू करने में विफल रहा है। (क्लोन विधि बहुरूपी वस्तु की उचित गहरी प्रतिलिपि बनाती है।)

आप आधार पर कॉपी कंस्ट्रक्टर को भी स्पष्ट रूप से चिह्नित कर सकते हैं जो वांछित होने पर स्पष्ट टुकड़ा करने की अनुमति देता है।


3
" आप आधार पर प्रतिलिपि निर्माता को भी स्पष्ट रूप से चिह्नित कर सकते हैं " जो बिल्कुल भी मदद नहीं करता है ।
क्यूरियसगुए

6

1. SLICING PROBLEM की परिभाषा

यदि D बेस क्लास B का एक व्युत्पन्न वर्ग है, तो आप टाइप बेस के एक चर (या पैरामीटर) के लिए व्युत्पन्न प्रकार की वस्तु को असाइन कर सकते हैं।

उदाहरण

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

यद्यपि उपरोक्त असाइनमेंट की अनुमति है, लेकिन चर पालतू को सौंपा गया मूल्य इसकी नस्ल क्षेत्र खो देता है। इसे स्लाइसिंग समस्या कहा जाता है

2. SLIC PROBLEM को कैसे फिट करें

समस्या को हराने के लिए, हम पॉइंटर्स को डायनामिक वैरिएबल का उपयोग करते हैं।

उदाहरण

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

इस स्थिति में, ptrD (अवर श्रेणी के ऑब्जेक्ट) द्वारा इंगित किए जा रहे डायनामिक वैरिएबल का कोई भी डेटा सदस्य या सदस्य कार्य नहीं खोएगा। इसके अलावा, यदि आपको फ़ंक्शन का उपयोग करने की आवश्यकता है, तो फ़ंक्शन एक वर्चुअल फ़ंक्शन होना चाहिए।


7
मैं "टुकड़ा करने की क्रिया" भाग को समझता हूं, लेकिन मैं "समस्या" नहीं समझता। यह कैसे एक समस्या है कि कुछ राज्य dogवर्ग का हिस्सा नहीं है Pet( breedडेटा सदस्य) चर में कॉपी नहीं किया गया है pet? कोड केवल Petडेटा सदस्यों में रुचि रखता है - जाहिरा तौर पर। अगर यह अवांछित है, तो स्लाइसिंग निश्चित रूप से एक "समस्या" है, लेकिन मैं इसे यहाँ नहीं देखता।
जिज्ञासु

4
" ((Dog *)ptrP)" मैं सुझाव देता हूंstatic_cast<Dog*>(ptrP)
क्यूरियसग्यु

मेरा सुझाव है कि आप 'पीटीआरपी' के माध्यम से डिलीट करते समय स्ट्रिंग को 'ब्रीड' को एक वर्चुअल डिस्ट्रक्टर ('स्ट्रिंग' का डिस्ट्रॉक्टर नहीं कहा जाएगा) के बिना मेमोरी को लीक कर देंगे। '' आप समस्याग्रस्त क्यों दिख रहे हैं? फिक्स ज्यादातर उचित वर्ग डिजाइन है। इस मामले में समस्या यह है कि इनहेरिटेंस को नियंत्रित करने के लिए निर्माणकर्ताओं को लिखना जब विरासत में थकाऊ और आसानी से भुला दिया जाता है। आप अपने कोड के साथ डेंजर ज़ोन के आस-पास कहीं भी नहीं पहुंचेंगे क्योंकि इसमें कोई बहुरूपता शामिल नहीं है या यहां तक ​​कि उल्लेख किया गया है (टुकड़ा करना आपकी वस्तु को छोटा कर देगा लेकिन आपके कार्यक्रम को दुर्घटनाग्रस्त नहीं करेगा, यहां)।
ड्यूड

24
-1 वास्तविक समस्या की व्याख्या करने में यह पूरी तरह विफल है। C ++ में मूल्य शब्दार्थ है, जावा की तरह संदर्भ शब्दार्थ नहीं है , इसलिए यह पूरी तरह से अपेक्षित है। और "फिक्स" वास्तव में भयानक सी ++ कोड का एक उदाहरण है । डायनेमिक एलोकेशन का सहारा लेकर इस तरह की स्लाइसिंग जैसी गैर-मौजूदा समस्याओं को "फिक्सिंग" छोटी गाड़ी कोड, लीक की गई मेमोरी और भयानक प्रदर्शन के लिए एक नुस्खा है। नोट है कि कर रहे हैं मामलों में जहां टुकड़ा करने की क्रिया बुरा है, लेकिन क्या यह उत्तर failes उन्हें बाहर बात करने के लिए। संकेत: यदि आप संदर्भों के माध्यम से असाइन करते हैं तो परेशानी शुरू हो जाती है ।
fgp

क्या आप यह भी समझते हैं कि परिभाषित किए गए प्रकार के सदस्य तक पहुँचने की कोशिश करना ( Dog::breedकोई तरीका नहीं है) SLICING से संबंधित ERROR है?
क्रॉल

4

यह मुझे लगता है, कि जब आपके स्वयं के कक्षाएं और कार्यक्रम खराब रूप से आर्किटेक्चर / डिज़ाइन किए गए हैं, तो इसके अलावा स्लाइसिंग इतनी समस्या नहीं है।

यदि मैं एक उप-प्रकार की वस्तु को एक विधि के पैरामीटर के रूप में पास करता हूं, जो कि प्रकार के सुपरक्लास का एक पैरामीटर लेता है, तो मुझे निश्चित रूप से इसके बारे में पता होना चाहिए और आंतरिक रूप से पता होना चाहिए, कहा जाता है कि विधि सुपरक्लास (उर्फ बास्केट) ऑब्जेक्ट के साथ ही काम करेगी।

यह मुझे केवल अनुचित उम्मीद है कि एक उपवर्ग प्रदान करने का अनुरोध करता है जहां एक बेसकलैस का अनुरोध किया जाता है, जो किसी भी तरह से उपवर्ग विशिष्ट परिणाम देगा, जिससे समस्या का कारण बन जाएगा। विधि के उपयोग में इसका या तो खराब डिज़ाइन या घटिया उपवर्ग कार्यान्वयन। मैं अनुमान लगा रहा हूँ कि इसका परिणाम आमतौर पर अच्छा ओओपी डिज़ाइन का विस्तार या प्रदर्शन के लाभ के पक्ष में त्याग है।


3
लेकिन याद रखें, मिनोक, कि आप उस वस्तु के संदर्भ में नहीं गुजर रहे हैं। आप उस ऑब्जेक्ट की एक नई कॉपी पास कर रहे हैं, लेकिन इस प्रक्रिया में कॉपी करने के लिए बेस क्लास का उपयोग कर रहे हैं।
अराफंगियन

बेस क्लास पर संरक्षित कॉपी / असाइनमेंट और यह समस्या हल हो गई है।
यार

1
आप सही हे। अच्छा अभ्यास अमूर्त आधार वर्गों का उपयोग करना है या कॉपी / असाइनमेंट तक पहुंच को प्रतिबंधित करना है। हालांकि, यह एक बार वहां पहुंचना इतना आसान नहीं है और इसकी देखभाल करना भूल जाना आसान है। कटा हुआ * के साथ आभासी तरीकों को कॉल करना * यह रहस्यमय चीजें हो सकती हैं यदि आप एक एक्सेस उल्लंघन के बिना दूर हो जाते हैं।
यार

1
मुझे याद है कि विश्वविद्यालय में मेरे C ++ प्रोग्रामिंग पाठ्यक्रमों में सबसे अच्छी प्रथाएँ थीं जो हमने बनाई हर वर्ग के लिए, हमें डिफ़ॉल्ट कन्स्ट्रक्टर, कॉपी कंस्ट्रक्टर और असाइनमेंट ऑपरेटर, साथ ही एक विध्वंसक लिखना आवश्यक था। इस तरह से आपने यह सुनिश्चित कर लिया कि कॉपी निर्माण और जैसा हुआ वैसा ही हुआ, जबकि कक्षा को लिखते समय ... बाद में कुछ अजीब व्यवहार पर ध्यान देना चाहिए।
मिनोक

3

ठीक है, मैं ऑब्जेक्ट स्लाइसिंग समझाते हुए कई पोस्ट पढ़ने के बाद यह कोशिश करूँगा लेकिन यह नहीं कि यह समस्याग्रस्त कैसे हो जाता है।

स्मृति भ्रष्टाचार के परिणामस्वरूप होने वाला दुष्परिणाम निम्नलिखित है:

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

3

स्लाइसिंग का अर्थ है कि उप-वर्ग द्वारा जोड़ा गया डेटा तब छोड़ दिया जाता है जब उप-वर्ग का ऑब्जेक्ट पास हो जाता है या मान द्वारा या किसी बेस क्लास ऑब्जेक्ट की अपेक्षा वाले फ़ंक्शन से वापस आ जाता है।

स्पष्टीकरण: निम्नलिखित वर्ग घोषणा पर विचार करें:

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

जैसा कि बेसकलैस कॉपी फ़ंक्शंस के बारे में कुछ भी नहीं पता है कि व्युत्पन्न के आधार भाग की प्रतिलिपि बनाई गई है। इसे आमतौर पर स्लाइसिंग कहा जाता है।


1
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; 
}

4
क्या आप कुछ अतिरिक्त विवरण देना चाहेंगे? आपका उत्तर पहले से ही पोस्ट किए गए लोगों से अलग कैसे है?
एलेक्सिस पिजन

2
मुझे लगता है कि अधिक स्पष्टीकरण बुरा नहीं होगा।
लूपर

-1

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

class Base { 
int x;
 };

class Derived : public Base { 
 int z; 
 };

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

-1

जब एक व्युत्पन्न वर्ग ऑब्जेक्ट बेस क्लास ऑब्जेक्ट को सौंपा जाता है, तो व्युत्पन्न वर्ग ऑब्जेक्ट के सभी सदस्यों को बेस क्लास ऑब्जेक्ट के लिए कॉपी किया जाता है, केवल उन सदस्यों को छोड़कर जो बेस क्लास में मौजूद नहीं हैं। इन सदस्यों को संकलक द्वारा अलग कर दिया जाता है। इसे ऑब्जेक्ट स्लिंग कहते हैं।

यहाँ एक उदाहरण है:

#include<bits/stdc++.h>
using namespace std;
class Base
{
    public:
        int a;
        int b;
        int c;
        Base()
        {
            a=10;
            b=20;
            c=30;
        }
};
class Derived : public Base
{
    public:
        int d;
        int e;
        Derived()
        {
            d=40;
            e=50;
        }
};
int main()
{
    Derived d;
    cout<<d.a<<"\n";
    cout<<d.b<<"\n";
    cout<<d.c<<"\n";
    cout<<d.d<<"\n";
    cout<<d.e<<"\n";


    Base b = d;
    cout<<b.a<<"\n";
    cout<<b.b<<"\n";
    cout<<b.c<<"\n";
    cout<<b.d<<"\n";
    cout<<b.e<<"\n";
    return 0;
}

यह उत्पन्न करेगा:

[Error] 'class Base' has no member named 'd'
[Error] 'class Base' has no member named 'e'

नीचा दिखाया क्योंकि यह एक अच्छा उदाहरण नहीं है। यह या तो काम नहीं करेगा यदि d से b में कॉपी करने के बजाय, आप एक पॉइंटर का उपयोग करेंगे जिस स्थिति में d और e अभी भी मौजूद होंगे, लेकिन आधार में वे सदस्य नहीं हैं। आपका उदाहरण केवल दिखाता है कि आप उन सदस्यों तक नहीं पहुँच सकते हैं जिनके पास वर्ग नहीं है।
स्टीफन फेबियन

-2

मैं बस स्लाइसिंग समस्या के दौरान भाग गया और तुरंत यहां उतरा। तो मुझे इसमें अपने दो सेंट जोड़ने चाहिए।

चलो "प्रोडक्शन कोड" (या ऐसा कुछ जो करीबी आता है) से एक उदाहरण है:


मान लीजिए कि हमारे पास कुछ है जो कार्यों को भेजता है। उदाहरण के लिए एक नियंत्रण केंद्र UI।
इस UI को उन चीजों की एक सूची प्राप्त करने की आवश्यकता है जो वर्तमान में भेजे जाने में सक्षम हैं। इसलिए हम एक वर्ग को परिभाषित करते हैं जिसमें प्रेषण-जानकारी होती है। चलो इसे बुलाओ Action। तो Actionकुछ सदस्य चर है। सादगी के लिए हमारे पास सिर्फ 2 हैं, एक std::string nameऔर एक होने के नाते std::function<void()> f। तब यह एक है void activate()जो सिर्फ निष्पादित करता हैf सदस्य होता है सदस्य को ।

तो यूआई को एक std::vector<Action>आपूर्ति मिलती है । कुछ कार्यों की कल्पना करें जैसे:

void push_back(Action toAdd);

अब हमने स्थापित किया है कि यह UI के दृष्टिकोण से कैसा दिखता है। अब तक कोई समस्या नहीं। लेकिन इस परियोजना पर काम करने वाले कुछ अन्य व्यक्ति अचानक निर्णय लेते हैं कि ऐसे विशेष कार्य हैं जो Actionवस्तु में अधिक जानकारी की आवश्यकता है । किस कारण से यह भी लंबोदर कब्जा के साथ हल किया जा सकता है। यह उदाहरण कोड से 1-1 नहीं लिया गया है।

तो आदमी Actionअपने स्वाद को जोड़ने के लिए प्राप्त करता है ।
वह अपने घर से पीसा हुआ वर्ग के एक उदाहरण के लिए गुजरता है, push_backलेकिन फिर कार्यक्रम haywire जाता है।

तो क्या हुआ?
जैसा कि आप अनुमान लगा सकते हैं: ऑब्जेक्ट कटा हुआ है।

उदाहरण से अतिरिक्त जानकारी खो गई है, और fअब अपरिभाषित व्यवहार का खतरा है।


मुझे उम्मीद है कि यह उदाहरण उन लोगों के लिए प्रकाश लाता है जो वास्तव में चीजों की कल्पना नहीं कर सकते हैं जब Aएस और Bएस के बारे में किसी तरह से बात की जा रही है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.