C ++ में कॉपी कंस्ट्रक्टर और = ऑपरेटर अधिभार: एक सामान्य कार्य संभव है?


87

एक कॉपी कंस्ट्रक्टर के बाद से

MyClass(const MyClass&);

और एक = ऑपरेटर अधिभार

MyClass& operator = (const MyClass&);

बहुत अधिक समान कोड, एक ही पैरामीटर, और केवल रिटर्न पर अलग है, क्या उन दोनों का उपयोग करने के लिए एक समान कार्य करना संभव है?


6
"... मेरे पास बहुत समान कोड है ..."? हम्म ... आप कुछ गलत कर रहे होंगे। इसके लिए उपयोगकर्ता-परिभाषित कार्यों का उपयोग करने की आवश्यकता को कम करने की कोशिश करें और संकलक को सभी गंदे काम करने दें। यह अक्सर अपने स्वयं के सदस्य वस्तु में संसाधनों को घेरने का मतलब है। आप हमें कुछ कोड दिखा सकते हैं। शायद हमारे पास कुछ अच्छे डिजाइन सुझाव हैं।
17

जवाबों:


121

हाँ। दो सामान्य विकल्प हैं। एक - जो आम तौर पर हतोत्साहित किया जाता है - operator=स्पष्ट रूप से कॉपी कंस्ट्रक्टर से कॉल करना है:

MyClass(const MyClass& other)
{
    operator=(other);
}

हालांकि, एक अच्छा प्रदान operator=करना एक चुनौती है जब यह पुरानी स्थिति और स्वयं के असाइनमेंट से उत्पन्न होने वाले मुद्दों से निपटने के लिए आता है। साथ ही, सभी सदस्यों और ठिकानों को पहले से तयशुदा आरंभिक रूप दिया जाता है, भले ही वे किसके द्वारा सौंपे जाएं other। यह सभी सदस्यों और ठिकानों के लिए भी मान्य नहीं हो सकता है और यहां तक ​​कि जहां यह वैध है, यह शब्दार्थ निरर्थक है और व्यावहारिक रूप से महंगा हो सकता है।

एक तेजी से लोकप्रिय समाधान operator=कॉपी कंस्ट्रक्टर और एक स्वैप विधि का उपयोग करके लागू करना है।

MyClass& operator=(const MyClass& other)
{
    MyClass tmp(other);
    swap(tmp);
    return *this;
}

या और भी:

MyClass& operator=(MyClass other)
{
    swap(other);
    return *this;
}

एक swapफ़ंक्शन आमतौर पर लिखने के लिए सरल है क्योंकि यह केवल इंटर्नल के स्वामित्व को स्वैप करता है और मौजूदा स्थिति को साफ करने या नए संसाधनों को आवंटित करने की आवश्यकता नहीं है।

कॉपी और स्वैप मुहावरे का लाभ यह है कि यह स्वचालित रूप से स्व-असाइनमेंट सुरक्षित है और यह प्रदान करता है कि स्वैप ऑपरेशन नो-थ्रो है - यह भी दृढ़ता से सुरक्षित अपवाद है।

जोरदार अपवाद सुरक्षित होने के लिए, एक 'हाथ' लिखा हुआ असाइनमेंट ऑपरेटर को आमतौर पर नए पुराने संसाधनों को आवंटित करने से पहले नए संसाधनों की एक प्रति आवंटित करनी होती है, ताकि यदि कोई अपवाद नए संसाधनों को आवंटित करता है, तो पुरानी स्थिति अभी भी वापस आ सकती है। । यह सब कॉपी-एंड-स्वैप के साथ मुफ्त में आता है, लेकिन आम तौर पर अधिक जटिल होता है, और इसलिए स्क्रैच से करने के लिए त्रुटि प्रवण होता है।

इससे सावधान रहने वाली बात यह है कि स्वैप विधि एक सही स्वैप है, न कि वह डिफ़ॉल्ट std::swapजो कॉपी कंस्ट्रक्टर और असाइनमेंट ऑपरेटर का उपयोग करता है।

आमतौर पर एक सदस्य swapका उपयोग किया जाता है। std::swapकाम करता है और सभी बुनियादी प्रकारों और सूचक प्रकारों के साथ 'नो-थ्रो' की गारंटी है। अधिकांश स्मार्ट पॉइंटर्स को नो-थ्रो गारंटी के साथ स्वैप किया जा सकता है।


3
दरअसल, वे आम ऑपरेशन नहीं हैं। जबकि कॉपी ctor पहली बार ऑब्जेक्ट के सदस्यों को इनिशियलाइज़ करता है, असाइनमेंट ऑपरेटर मौजूदा मानों को ओवरराइड करता है। इस पर विचार करते हुए, operator=प्रतिलिपि ctor से आबंटन वास्तव में बहुत बुरा है, क्योंकि यह पहले सभी मानों को कुछ डिफ़ॉल्ट के लिए आरम्भ करता है, बस दूसरे वस्तु के मूल्यों के साथ उन्हें ओवरराइड करने के लिए।
sbi

14
शायद "मैं अनुशंसा नहीं करता", जोड़ "और न ही कोई सी ++ विशेषज्ञ"। कोई भी साथ आ सकता है और यह महसूस करने में विफल हो सकता है कि आप सिर्फ एक व्यक्तिगत अल्पसंख्यक वरीयता व्यक्त नहीं कर रहे हैं, लेकिन जो लोग वास्तव में इसके बारे में सोचते हैं, उनके बारे में सुलझा हुआ मत है। और, ठीक है, शायद मैं गलत हूं और कुछ सी ++ विशेषज्ञ इसकी सिफारिश करते हैं, लेकिन व्यक्तिगत रूप से मैं अभी भी उस सिफारिश के संदर्भ के लिए किसी के लिए गेंटलेट रखना चाहता हूं।
16:17 पर स्टीव जेसोप

4
पर्याप्त रूप से, मैंने आपको पहले से ही अपग्रेड कर लिया है :-)। मुझे लगता है कि अगर किसी चीज़ को व्यापक रूप से सबसे अच्छा अभ्यास माना जाता है, तो ऐसा कहना सबसे अच्छा है (और इसे फिर से देखें अगर कोई कहता है कि यह वास्तव में सबसे अच्छा नहीं है)। इसी तरह अगर किसी ने पूछा "क्या C ++ में म्यूटेक्स का उपयोग करना संभव है", मैं नहीं कहूंगा "एक काफी सामान्य विकल्प है कि पूरी तरह से RAII को अनदेखा करें, और गैर-अपवाद-सुरक्षित कोड लिखें जो उत्पादन में गतिरोध उत्पन्न करता है, लेकिन यह लिखने के लिए तेजी से लोकप्रिय है सभ्य, वर्किंग कोड ";-)
स्टीव जेसप

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

2
@litb: मैं इससे हैरान था, इसलिए मैंने अपवाद C ++ में आइटम 41 को देखा (जो कि यह समझ में आ गया) और यह विशेष सिफारिश चली गई है और वह इसकी जगह पर कॉपी-एंड-स्वैप की सिफारिश करता है। बल्कि चुपके से उसने एक ही समय में "समस्या # 4: यह अक्षम के लिए काम कर रहा है" गिरा दिया है।
सीबी बेली

13

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

यदि दोनों के बीच समानता है, तो यह है कि असाइनमेंट ऑपरेटर विनाश और कॉपी-कंस्ट्रक्शन करता है। कुछ डेवलपर्स वास्तव में प्लेसमेंट कॉपी-निर्माण के बाद इन-प्लेस विनाश द्वारा असाइनमेंट को लागू करते थे। हालाँकि, यह एक बहुत बुरा विचार है। (क्या होगा यदि यह किसी बेस क्लास का असाइनमेंट ऑपरेटर है जो किसी व्युत्पन्न वर्ग के असाइनमेंट के दौरान कहा जाता है?)

आमतौर पर कैनोनिकल मुहावरे पर विचार किया जाता है जो आजकल swapचार्ल्स द्वारा सुझाया गया है:

MyClass& operator=(MyClass other)
{
    swap(other);
    return *this;
}

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


swapघोषित किया जाना चाहिए virtual?

1
@ जोहान्स: पॉलीमोर्फिक क्लास पदानुक्रम में वर्चुअल फ़ंक्शंस का उपयोग किया जाता है। असाइनमेंट ऑपरेटरों का उपयोग मूल्य प्रकारों के लिए किया जाता है। मुश्किल से दो मिश्रण।
sbi

-3

कुछ मुझे परेशान करता है:

MyClass& operator=(const MyClass& other)
{
    MyClass tmp(other);
    swap(tmp);
    return *this;
}

सबसे पहले, "स्वैप" शब्द पढ़ना जब मेरा दिमाग सोच रहा है "कॉपी" मेरे सामान्य ज्ञान को परेशान करता है। इसके अलावा, मैं इस फैंसी ट्रिक के लक्ष्य पर सवाल उठाता हूं। हां, नए (कॉपी किए गए) संसाधनों के निर्माण में कोई भी अपवाद स्वैप से पहले होना चाहिए, जो यह सुनिश्चित करने के लिए एक सुरक्षित तरीका की तरह लगता है कि सभी नए डेटा को लाइव करने से पहले भरा जाए।

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

इसलिए, मैं "स्वैग" के बजाय अधिक प्राकृतिक "स्थानांतरण" करने का प्रस्ताव करता हूं:

MyClass& operator=(const MyClass& other)
{
    MyClass tmp(other);
    transfer(tmp);
    return *this;
}

अभी भी अस्थायी वस्तु का निर्माण चल रहा है, लेकिन अगली तत्काल कार्रवाई चलती से पहले गंतव्य के सभी वर्तमान संसाधनों को मुक्त करना है (और इसे खाली करना है ताकि वे स्रोत के संसाधनों से डबल-मुक्त न हों)।

{निर्माण, चाल, विनाश} के बजाय, मैं प्रस्ताव {निर्माण, विनाश, चाल}। यह कदम, जो सबसे खतरनाक कार्रवाई है, वह सब कुछ खत्म हो जाने के बाद अंतिम रूप दिया गया है।

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

स्वैप के बजाय स्थानांतरण। वैसे भी मेरा सुझाव यही है।


2
एक विध्वंसक विफल नहीं होना चाहिए, इसलिए विनाश पर अपवाद अपेक्षित नहीं हैं। और, मुझे नहीं लगता कि विनाश के पीछे की चाल को बढ़ाने से क्या फायदा होगा, अगर चाल सबसे खतरनाक ऑपरेशन है? यानी, मानक योजना में, एक चाल की विफलता पुरानी स्थिति को दूषित नहीं करेगी, जबकि आपकी नई योजना करती है। तो क्यों? इसके अलावा, First, reading the word "swap" when my mind is thinking "copy" irritates-> एक पुस्तकालय लेखक के रूप में, आप आम तौर पर सामान्य प्रथाओं (कॉपी + स्वैप) को जानते हैं, और क्रूक्स है my mind। आपका मन वास्तव में सार्वजनिक इंटरफ़ेस के पीछे छिपा हुआ है। यही सब फिर से प्रयोग करने योग्य कोड है।
सेबस्टियन मच
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.