2009-08-04 16 views
73

मेरे पास रचनाकारों के एक सेट और एक असाइनमेंट ऑपरेटर के साथ कक्षा बी है।सी ++ में बेस क्लास के कन्स्ट्रक्टर और असाइनमेंट ऑपरेटर का उपयोग कैसे करें?

class B 
{ 
public: 
    B(); 
    B(const string & s); 
    B(const B & b){(*this) = b;}; 
    B & operator= (const B & b); 
private: 
    virtual void foo(); 
    // and other private member variables and functions 
} 

मैं एक इनहेरीट वर्ग डी कि सिर्फ समारोह foo (को पार कर जाएगी) बनाना चाहते हैं, और कोई अन्य परिवर्तन की आवश्यकता है।

लेकिन मैं डी कंस्ट्रक्टर्स के एक ही सेट, कॉपी निर्माता और बी के रूप में असाइनमेंट ऑपरेटर सहित करना चाहते हैं:

D(const D & d){(*this) = d;}; 
    D & operator= (const D & d); 

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

+0

यदि आप 'foo' विधि को ओवरराइड करना चाहते हैं, तो आप असाइनमेंट ऑपरेटर के उत्तराधिकारी के लिए' बी :: ऑपरेटर =; 'का उपयोग कर सकते हैं, लेकिन प्रतिलिपि बनाने और चालकों को विरासत में नहीं मिला: https://stackoverflow.com/q/49045026/5447906 –

उत्तर

93

आप स्पष्ट रूप कंस्ट्रक्टर और असाइनमेंट ऑपरेटरों कॉल कर सकते हैं।

class ImplicitBase { 
    int value_; 
    // No operator=() defined 
}; 

class Derived : public ImplicitBase { 
    const char* name_; 
public: 
    Derived& operator=(const Derived& d) 
    { 
     ImplicitBase::operator=(d); // Call compiler generated operator= 
     name_ = strdup(d.name_); 
     return *this; 
    } 
}; 
+0

इसका क्या अर्थ है? 'बेस (कॉन्स बेस और)' – qed

+1

@ क्रॉविंगस्पिट यह एक [प्रतिलिपि निर्माता] है (http://en.wikipedia.org/wiki/Copy_constructor) (तर्क नाम छोड़ दिया गया है)। – Motti

+0

धन्यवाद। यदि पहले से ही ऑपरेटर = ओवरलोडिंग है तो हमें एक प्रतिलिपि बनाने की आवश्यकता क्यों है? – qed

16

लघु उत्तर: हाँ, आप डी में काम दोहराने की आवश्यकता होगी

लांग जवाब:

अपने व्युत्पन्न वर्ग 'डी' कोई नया सदस्य चर तो डिफ़ॉल्ट संस्करणों (संकलक द्वारा उत्पन्न होता है तो ऐसा करना चाहिए काम ठीक है)। डिफ़ॉल्ट प्रतिलिपि निर्माता कंटेंट कॉपी कन्स्ट्रक्टर को कॉल करेगा और डिफ़ॉल्ट असाइनमेंट ऑपरेटर पैरेंट असाइनमेंट ऑपरेटर को कॉल करेगा।

लेकिन यदि आपकी कक्षा 'डी' में संसाधन हैं तो आपको कुछ काम करने की आवश्यकता होगी।

B(const B& b){(*this) = b;} 

D(const D& d){(*this) = d;} 

आम तौर पर कंस्ट्रक्टर्स श्रृंखला नकल इतना है कि वे बेस से निर्मित कॉपी कर रहे हैं:

मैं आपके प्रति निर्माता थोड़ा अजीब लगता है। यहां क्योंकि आप असाइनमेंट ऑपरेटर को कॉल कर रहे हैं, कॉपी कन्स्ट्रक्टर को डिफॉल्ट कन्स्ट्रक्टर को डिफ़ॉल्ट रूप से ऑब्जेक्ट को पहले नीचे से प्रारंभ करने के लिए कॉल करना होगा। फिर आप असाइनमेंट ऑपरेटर का उपयोग करके फिर से नीचे जाते हैं। यह बल्कि अक्षम है।

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

आम तौर पर मैंने इसे दूसरी तरफ देखा है।
असाइनमेंट ऑपरेटर को कॉपी कन्स्ट्रक्टर और स्वैप के संदर्भ में परिभाषित किया गया है। ऐसा इसलिए है क्योंकि यह मजबूत अपवाद गारंटी प्रदान करना आसान बनाता है। मुझे नहीं लगता कि आप इसे इस तरह से कर कर मजबूत गारंटी प्रदान करने में सक्षम होंगे (मैं गलत हो सकता हूं)।

class X 
{ 
    // If your class has no resources then use the default version. 
    // Dynamically allocated memory is a resource. 
    // If any members have a constructor that throws then you will need to 
    // write your owen version of these to make it exception safe. 


    X(X const& copy) 
     // Do most of the work here in the initializer list 
    { /* Do some Work Here */} 

    X& operator=(X const& copy) 
    { 
     X tmp(copy);  // All resource all allocation happens here. 
          // If this fails the copy will throw an exception 
          // and 'this' object is unaffected by the exception. 
     swap(tmp); 
     return *this; 
    } 
    // swap is usually trivial to implement 
    // and you should easily be able to provide the no-throw guarantee. 
    void swap(X& s) throws() 
    { 
     /* Swap all members */ 
    } 
}; 

भले ही आप एक्स से कक्षा डी प्राप्त करते हैं, यह इस पैटर्न को प्रभावित नहीं करता है।
माना जाता है कि आपको बेस क्लास में स्पष्ट कॉल करके काम को थोड़ा सा दोहराना होगा, लेकिन यह अपेक्षाकृत मामूली है।

class D: public X 
{ 

    // Note: 
    // If D contains no members and only a new version of foo() 
    // Then the default version of these will work fine. 

    D(D const& copy) 
     :X(copy) // Chain X's copy constructor 
     // Do most of D's work here in the initializer list 
    { /* More here */} 



    D& operator=(D const& copy) 
    { 
     D tmp(copy);  // All resource all allocation happens here. 
          // If this fails the copy will throw an exception 
          // and 'this' object is unaffected by the exception. 
     swap(tmp); 
     return *this; 
    } 
    // swap is usually trivial to implement 
    // and you should easily be able to provide the no-throw guarantee. 
    void swap(D& s) throws() 
    { 
     X::swap(s); // swap the base class members 
     /* Swap all D members */ 
    } 
}; 
+0

+1। चूंकि आप इसमें हैं, अपने प्रकार के लिए std :: स्वैप में विशेषज्ञता जोड़ें जो आपके स्वैप सदस्य विधि पर प्रतिनिधि है: 'नेमस्पेस std {template <> void std :: swap (डी & lhs, डी और rhs) {lhs.swap (आरएचएस); }} 'इस तरह से विशेष स्वैप ऑपरेशन एसटीएल एल्गोरिदम में उपयोग किया जा सकता है। –

+1

एक्स * के समान नाम में एक फ्री स्वैप फ़ंक्शन जोड़ना * एडीएल के माध्यम से एक ही प्रभाव (एडीएल के माध्यम से) होना चाहिए, लेकिन कोई हाल ही में कह रहा था कि एमएसवीसी गलत तरीके से std :: स्वैप को कॉल करता है, इस प्रकार सूखे को सही बनाता है ... –

+0

तकनीकी रूप से आप भी मानक नामस्थान में सामान जोड़ने की अनुमति नहीं है। –

4

आप सबसे अधिक संभावना (: टुकड़ा करने की क्रिया, इकाई अर्थ विज्ञान बनाम मूल्य अर्थ विज्ञान संकेत) अपने डिजाइन में एक दोष है। एक पूर्ण प्रतिलिपि/मूल्य semantics एक पॉलीमोर्फिक पदानुक्रम से किसी ऑब्जेक्ट पर अक्सर आवश्यकता नहीं होती है। यदि आप इसे प्रदान करना चाहते हैं तो किसी को बाद में इसकी आवश्यकता हो सकती है, इसका मतलब है कि आपको इसकी आवश्यकता नहीं होगी।आधार वर्ग को इसके बजाय प्रतिलिपि बनाओ (बढ़ावा से विरासत में :: उदाहरण के लिए noncopyable), और यह सब कुछ है।

केवल सही समाधान जब इस तरह की जरूरत वास्तव में प्रकट होता है एन्वलप अक्षर का मुहावरा, या नियमित वस्तुओं शॉन जनक और Alexander Stepanov IIRC द्वारा पर लेख से थोड़ा ढांचे हैं। अन्य सभी समाधान आपको स्लाइसिंग, और/या एलएसपी के साथ परेशानी देंगे।

1

आपको उन सभी रचनाकारों को फिर से परिभाषित करना होगा जो डिफ़ॉल्ट या प्रति रचनाकारों की प्रतिलिपि बनाना होगा।

struct base 
{ 
    base() { std::cout << "base()" << std::endl; } 
    base(base const &) { std::cout << "base(base const &)" << std::endl; } 
    base& operator=(base const &) { std::cout << "base::=" << std::endl; } 
}; 
struct derived : public base 
{ 
    // compiler will generate: 
    // derived() : base() {} 
    // derived(derived const & d) : base(d) {} 
    // derived& operator=(derived const & rhs) { 
    // base::operator=(rhs); 
    // return *this; 
    // } 
}; 
int main() 
{ 
    derived d1;  // will printout base() 
    derived d2 = d1; // will printout base(base const &) 
    d2 = d1;   // will printout base::= 
} 

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

class Base { 
//... 
public: 
    Base(const Base&) { /*...*/ } 
    Base& operator=(const Base&) { /*...*/ } 
}; 

class Derived : public Base 
{ 
    int additional_; 
public: 
    Derived(const Derived& d) 
     : Base(d) // dispatch to base copy constructor 
     , additional_(d.additional_) 
    { 
    } 

    Derived& operator=(const Derived& d) 
    { 
     Base::operator=(d); 
     additional_ = d.additional_; 
     return *this; 
    } 
}; 

दिलचस्प बात यह काम करता है कि भले ही आप स्पष्ट रूप से इन कार्यों को परिभाषित नहीं किया है (यह तो संकलक उत्पन्न कार्यों का उपयोग करता):

+0

ध्यान दें कि यदि कोई अन्य ctor (इसमें कॉपी ctor शामिल है) परिभाषित किया गया है तो संकलक डिफ़ॉल्ट ctor प्रदान नहीं करेगा। इसलिए यदि आप 'व्युत्पन्न' को डिफ़ॉल्ट ctor रखना चाहते हैं, तो आपको स्पष्ट रूप से परिभाषित करने की आवश्यकता होगी। – sbi

1

मूल कोड गलत है:

class B 
{ 
public: 
    B(const B& b){(*this) = b;} // copy constructor in function of the copy assignment 
    B& operator= (const B& b); // copy assignment 
private: 
// private member variables and functions 
}; 

सामान्य तौर पर, आप प्रति काम के मामले में प्रति निर्माता को परिभाषित नहीं कर सकते, क्योंकि प्रति असाइनमेंट संसाधनों और प्रतिलिपि निर्माता डॉन रिलीज चाहिए नहीं !!!

यह समझने के लिए, पर विचार करें:

class B 
{ 
public: 
    B(Other& ot) : ot_p(new Other(ot)) {} 
    B(const B& b) {ot_p = new Other(*b.ot_p);} 
    B& operator= (const B& b); 
private: 
    Other* ot_p; 
}; 

स्मृति रिसाव से बचने के लिए, प्रतिलिपि काम पहले स्मृति ot_p द्वारा बताया हटाना होगा:

B::B& operator= (const B& b) 
{ 
    delete(ot_p); // <-- This line is the difference between copy constructor and assignment. 
    ot_p = new Other(*b.ot_p); 
} 
void f(Other& ot, B& b) 
{ 
    B b1(ot); // Here b1 is constructed requesting memory with new 
    b1 = b; // The internal memory used in b1.op_t MUST be deleted first !!! 
} 

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

तो आप क्या मूल रूप से इस लेख में सुझाव दिया है कार्य करें:

B(const B& b){(*this) = b;} // copy constructor 

आप एक unexisting स्मृति को हटा देंगे।

संबंधित मुद्दे