एक कॉपी कंस्ट्रक्टर के बाद से
MyClass(const MyClass&);
और एक = ऑपरेटर अधिभार
MyClass& operator = (const MyClass&);
बहुत अधिक समान कोड, एक ही पैरामीटर, और केवल रिटर्न पर अलग है, क्या उन दोनों का उपयोग करने के लिए एक समान कार्य करना संभव है?
एक कॉपी कंस्ट्रक्टर के बाद से
MyClass(const MyClass&);
और एक = ऑपरेटर अधिभार
MyClass& operator = (const MyClass&);
बहुत अधिक समान कोड, एक ही पैरामीटर, और केवल रिटर्न पर अलग है, क्या उन दोनों का उपयोग करने के लिए एक समान कार्य करना संभव है?
जवाबों:
हाँ। दो सामान्य विकल्प हैं। एक - जो आम तौर पर हतोत्साहित किया जाता है - 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
काम करता है और सभी बुनियादी प्रकारों और सूचक प्रकारों के साथ 'नो-थ्रो' की गारंटी है। अधिकांश स्मार्ट पॉइंटर्स को नो-थ्रो गारंटी के साथ स्वैप किया जा सकता है।
operator=
प्रतिलिपि ctor से आबंटन वास्तव में बहुत बुरा है, क्योंकि यह पहले सभी मानों को कुछ डिफ़ॉल्ट के लिए आरम्भ करता है, बस दूसरे वस्तु के मूल्यों के साथ उन्हें ओवरराइड करने के लिए।
assign
कुछ मामलों (हल्के कक्षाओं के लिए) में कॉपी कॉर्ट और असाइनमेंट ऑपरेटर दोनों द्वारा उपयोग किए जाने वाले सदस्य फ़ंक्शन का वाजिब होना उचित है । अन्य मामलों में (संसाधन गहन / मामलों का उपयोग, संभाल / शरीर) एक कॉपी / स्वैप कोर्स में जाने का एक तरीका है।
कॉपी कंस्ट्रक्टर उन वस्तुओं का पहली बार आरंभीकरण करता है जो कच्ची मेमोरी हुआ करती थीं। असाइनमेंट ऑपरेटर, OTOH, नए लोगों के साथ मौजूदा मूल्यों को ओवरराइड करता है। अधिक से अधिक कभी नहीं, इसमें पुराने संसाधनों को खारिज करना (उदाहरण के लिए, मेमोरी) और नए लोगों को आवंटित करना शामिल है।
यदि दोनों के बीच समानता है, तो यह है कि असाइनमेंट ऑपरेटर विनाश और कॉपी-कंस्ट्रक्शन करता है। कुछ डेवलपर्स वास्तव में प्लेसमेंट कॉपी-निर्माण के बाद इन-प्लेस विनाश द्वारा असाइनमेंट को लागू करते थे। हालाँकि, यह एक बहुत बुरा विचार है। (क्या होगा यदि यह किसी बेस क्लास का असाइनमेंट ऑपरेटर है जो किसी व्युत्पन्न वर्ग के असाइनमेंट के दौरान कहा जाता है?)
आमतौर पर कैनोनिकल मुहावरे पर विचार किया जाता है जो आजकल swap
चार्ल्स द्वारा सुझाया गया है:
MyClass& operator=(MyClass other)
{
swap(other);
return *this;
}
यह कॉपी-कंस्ट्रक्शन (नोट जो other
कॉपी किया गया है) और विनाश का उपयोग करता है (यह फ़ंक्शन के अंत में नष्ट हो जाता है) - और यह उन्हें सही क्रम में उपयोग करता है, विनाश से पहले भी निर्माण (विफल हो सकता है) (विफल नहीं होना चाहिए)।
कुछ मुझे परेशान करता है:
MyClass& operator=(const MyClass& other)
{
MyClass tmp(other);
swap(tmp);
return *this;
}
सबसे पहले, "स्वैप" शब्द पढ़ना जब मेरा दिमाग सोच रहा है "कॉपी" मेरे सामान्य ज्ञान को परेशान करता है। इसके अलावा, मैं इस फैंसी ट्रिक के लक्ष्य पर सवाल उठाता हूं। हां, नए (कॉपी किए गए) संसाधनों के निर्माण में कोई भी अपवाद स्वैप से पहले होना चाहिए, जो यह सुनिश्चित करने के लिए एक सुरक्षित तरीका की तरह लगता है कि सभी नए डेटा को लाइव करने से पहले भरा जाए।
कोई बात नहीं। तो, स्वैप के बाद होने वाले अपवादों के बारे में क्या? (जब अस्थायी संसाधन के दायरे से बाहर हो जाने पर पुराने संसाधन नष्ट हो जाते हैं) असाइनमेंट के उपयोगकर्ता के दृष्टिकोण से, ऑपरेशन विफल हो गया है, सिवाय इसके कि यह नहीं हुआ। इसका बहुत बड़ा साइड इफेक्ट है: प्रतिलिपि वास्तव में हुई। यह केवल कुछ संसाधन क्लीनअप था जो विफल रहा। गंतव्य ऑब्जेक्ट की स्थिति बदल गई है भले ही ऑपरेशन बाहर से विफल हो गया लगता है।
इसलिए, मैं "स्वैग" के बजाय अधिक प्राकृतिक "स्थानांतरण" करने का प्रस्ताव करता हूं:
MyClass& operator=(const MyClass& other)
{
MyClass tmp(other);
transfer(tmp);
return *this;
}
अभी भी अस्थायी वस्तु का निर्माण चल रहा है, लेकिन अगली तत्काल कार्रवाई चलती से पहले गंतव्य के सभी वर्तमान संसाधनों को मुक्त करना है (और इसे खाली करना है ताकि वे स्रोत के संसाधनों से डबल-मुक्त न हों)।
{निर्माण, चाल, विनाश} के बजाय, मैं प्रस्ताव {निर्माण, विनाश, चाल}। यह कदम, जो सबसे खतरनाक कार्रवाई है, वह सब कुछ खत्म हो जाने के बाद अंतिम रूप दिया गया है।
हां, विनाश असफलता या तो योजना में एक समस्या है। डेटा या तो दूषित है (कॉपी किया गया था जब आपने ऐसा नहीं सोचा था) या खो गया (जब आपको नहीं लगता था कि यह मुक्त हो गया है)। खोया हुआ भ्रष्टाचारी से बेहतर है। कोई भी डेटा खराब डेटा से बेहतर नहीं है।
स्वैप के बजाय स्थानांतरण। वैसे भी मेरा सुझाव यही है।
First, reading the word "swap" when my mind is thinking "copy" irritates
-> एक पुस्तकालय लेखक के रूप में, आप आम तौर पर सामान्य प्रथाओं (कॉपी + स्वैप) को जानते हैं, और क्रूक्स है my mind
। आपका मन वास्तव में सार्वजनिक इंटरफ़ेस के पीछे छिपा हुआ है। यही सब फिर से प्रयोग करने योग्य कोड है।