2012-04-01 5 views
15

यह देखते हुए कि एक वर्ग वास्तव में चलने योग्य है, मैन्युअल रूप से चालक को कार्यान्वित करने और कक्षा के लिए असाइनमेंट ऑपरेटर को स्थानांतरित करने के लिए जल्दी से थकाऊ हो जाता है।मुझे किस परिदृश्य में एक चालक कन्स्ट्रक्टर को लागू करने और असाइनमेंट ऑपरेटर को स्थानांतरित करने की स्पष्ट रूप से आवश्यकता है?

मैं सोच रहा था जब ऐसा वास्तव में एक भारी, भारी, समयपूर्व अनुकूलन है?

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

लेकिन क्या यह गारंटी है? को किस परिदृश्य में एक चालक कन्स्ट्रक्टर को कार्यान्वित करने और असाइनमेंट ऑपरेटर को स्थानांतरित करने की आवश्यकता है?

संपादित करें:https://stackoverflow.com/a/9966105/6345 में अपने जवाब के लिए एक टिप्पणी में निकोल Bolas से नीचे उल्लेख किया है, दृश्य स्टूडियो 11 बीटा के साथ (और पहले) कोई चाल निर्माता या स्थानांतरित असाइनमेंट ऑपरेटर कभी स्वचालित रूप से उत्पन्न होता है। संदर्भ: http://blogs.msdn.com/b/vcblog/archive/2011/09/12/10209291.aspx

उत्तर

8

यदि आप पाते हैं अपने आप को लागू करने, के किसी भी:

  • नाशक
  • प्रतिलिपि निर्माता
  • प्रतिलिपि काम

तो फिर तुम अपने आप को पूछ किया जाना चाहिए अगर आप इस कदम निर्माण को लागू करने की जरूरत है। यदि आप उपर्युक्त में से किसी भी "डिफ़ॉल्ट" हैं, तो आपको खुद से पूछना चाहिए कि क्या आपको चाल सदस्यों को "= डिफ़ॉल्ट" भी करना चाहिए।

भी अधिक महत्वपूर्ण बात, आप का दस्तावेजीकरण किया जाना चाहिए और अपनी मान्यताओं का परीक्षण, उदाहरण के लिए:

static_assert(std::is_nothrow_default_constructible<A>::value, ""); 
static_assert(std::is_copy_constructible<A>::value, ""); 
static_assert(std::is_copy_assignable<A>::value, ""); 
static_assert(std::is_nothrow_move_constructible<A>::value, ""); 
static_assert(std::is_nothrow_move_assignable<A>::value, ""); 
static_assert(std::is_nothrow_destructible<A>::value, ""); 
+0

मुझे पता है कि प्रतिलिपि बनाने वाले निर्माता की पीढ़ी के बारे में हाल ही में बदलाव आया है जब कॉपी कन्स्ट्रक्टर हस्तनिर्मित था (और असाइनमेंट के बराबर)। '= Default' के प्रभाव के बारे में उस परिवर्तन से पहले एक चर्चा हुई थी, अगर मुझे सही याद है ... क्या 'डिफ़ॉल्ट' वास्तव में चालक कन्स्ट्रक्टर/असाइनमेंट उत्पन्न नहीं कर सकता है? –

+0

@Matthieu: §12.8/11 उन स्थितियों का वर्णन करता है जिनमें कक्षा के लिए एक डिफ़ॉल्ट प्रतिलिपि/चालक कन्स्ट्रक्टर को हटाया गया है। – ildjarn

+0

@ildjarn: हाँ, हालांकि यह हाल ही में बदल गया मुझे लगता है। और मुझे यकीन नहीं है कि डीआर के संकल्प को लागू करने के बाद पहले क्या हटाना होगा और नहीं होगा। –

0

मुझे खेद है। मैंने आपके प्रश्न के बिंदु को याद किया होगा। मैं आपका प्रश्न कॉपी कन्स्ट्रक्टर के लिए ले रहा हूं।

1 99 0 के दशक में जब मैंने सी ++ सीखा, तो मुझे कक्षा बनाने के दौरान हमेशा एक प्रतिलिपि लिखने के लिए सिखाया जाता था। अन्यथा, और यह कंपाइलर के नए संस्करणों के साथ बदल सकता है, सी ++ उन परिस्थितियों में आपके लिए एक प्रतिलिपि निर्माता उत्पन्न करेगा जिसके लिए एक की आवश्यकता होती है।

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

अंत में, मुझे सिखाया गया था कि एक प्रतिलिपि निर्माता लिखकर, आप अपनी कक्षा की प्रतिलिपि बनाने के तरीके पर सटीक नियंत्रण ले रहे हैं।

मुझे उम्मीद है कि इससे मदद मिलती है।

+2

"चाल कॉपी कन्स्ट्रक्टर"! = "प्रतिलिपि बनाएँ"। – mfontanini

+0

@ ऑक्टोपसग्राबस: मेरा प्रश्न चलने वाले अर्थशास्त्र के बारे में है, न कि अर्थशास्त्र की प्रतिलिपि बनाएँ (जिनके बारे में मेरे पास कोई प्रश्न नहीं है)। –

+0

@ फ़ोंटानिनी: दरअसल एक "चाल प्रतिलिपि निर्माता" मौजूद नहीं है, केवल एक कॉपी कन्स्ट्रक्टर और (सी ++ 11) एक चालक कन्स्ट्रक्टर है। ध्यान दें कि प्रश्न में केवल सही शब्द "चालक कन्स्ट्रक्टर" है। – celtschk

7

पहले, ले जाने के अर्थ विज्ञान केवल वर्गों है कि किसी भी तरह के संसाधनों पकड़ के लिए मदद करते हैं। "फ्लैट" वर्गों से इसका कोई फायदा नहीं होता है।

अगला, आपको vector, , unique_ptr जैसे "बिल्डिंग ब्लॉक" से बाहर अपनी कक्षाएं बनाना चाहिए और पसंद है, जो सभी संसाधनों के कम-स्तर वाले विवरण के साथ सौदा करते हैं। यदि आपकी कक्षा इस प्रकार की जाती है, तो आपको कुछ भी लिखना नहीं होगा क्योंकि संकलक सदस्यों को आपके लिए सही तरीके से उत्पन्न करेगा।

यदि आपको एक विनाशक लिखने, कहने, लॉगिंग करने की आवश्यकता है, तो चालक ctors की पीढ़ी अक्षम कर दी जाएगी, इसलिए आपको इसे समर्थन करने वाले संकलक के लिए T(T&&) = default; की आवश्यकता है। अन्यथा, यह एकमात्र जगहों में से एक है जो खुद को एक विशेष सदस्य लिखना था (ठीक है, अगर आप इस तरह के "बिल्डिंग ब्लॉक" को लिखते हैं)।

ध्यान दें कि विनाशक और कन्स्ट्रक्टर में लॉगिंग एक आसान तरीका किया जा सकता है। बस एक विशेष वर्ग से उत्तराधिकारी जो निर्माण/विनाश पर लॉग ऑन करता है। या इसे एक सदस्य चर बनाओ। इसके साथ:

टीएल; डी कंपाइलर आपके लिए विशेष सदस्य उत्पन्न करने दें। यह कॉपी कन्स्ट्रक्टर और असाइनमेंट ऑपरेटर के साथ-साथ विनाशक के लिए भी गिना जाता है। खुद को मत लिखो।

(ठीक है, शायद असाइनमेंट ऑपरेटर, यदि आप उन्हें बोतल की गर्दन के रूप में पहचान सकते हैं और उन्हें अनुकूलित करना चाहते हैं, या यदि आप विशेष अपवाद सुरक्षा या कुछ चाहते हैं। हालांकि, "बिल्डिंग ब्लॉक" पहले से ही यह सब कुछ प्रदान करना चाहिए।)

+0

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

+0

मानक कंटेनर केवल आपको "सरल" संसाधनों (अनिवार्य रूप से स्मृति) के साथ मदद करते हैं; संसाधनों के लिए उदा। बाहरी उपकरणों पर आवंटित आपको विशेष आवंटकों को लिखने की आवश्यकता होगी। इसके बजाय अपने स्वयं के "बिल्डिंग ब्लॉक" वर्ग को लिखना अक्सर अधिक व्यावहारिक होता है। – leftaroundabout

+0

@ बाएंअराउंडबाउट: हाँ, लेकिन आप में से कितने हैं? शायद 4? –

2

हर बार जब डिफ़ॉल्ट व्यवहार अवांछित होता है या हर बार डिफ़ॉल्ट वाले हटा दिए जाते हैं और आपको अभी भी उनकी आवश्यकता होती है।

चाल के लिए संकलक डिफ़ॉल्ट व्यवहार सदस्य और आधार चाल को कॉल करता है। फ्लैट कक्षाओं/बील-इन प्रकारों के लिए यह प्रतिलिपि की तरह है।

समस्या आम तौर पर कक्षाओं वाले होल्डिंग पॉइंटर्स या मान वाले संसाधनों (जैसे हैंडल, या विशेष इंडेक्स इत्यादि) के साथ मौजूद होती है जहां एक कदम को नए स्थान पर मूल्यों की प्रतिलिपि बनाने की आवश्यकता होती है, लेकिन पुराने स्थान को कुछ "शून्य राज्य मूल्य "विनाशक द्वारा मान्यता प्राप्त। अन्य सभी मामलों में, डिफ़ॉल्ट व्यवहार ठीक है।

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

2

मुझे किन परिदृश्यों में एक चालक को लागू करने और असाइनमेंट ऑपरेटर को स्थानांतरित करने की स्पष्ट रूप से आवश्यकता होने की अपेक्षा की जानी चाहिए?

निम्नलिखित मामलों के तहत

:

  1. आप दृश्य स्टूडियो 10 या 11. वे आर-मूल्य संदर्भ कार्यान्वित करते रहे हैं, लेकिन नहीं संकलक उत्पन्न चाल अर्थ विज्ञान। तो यदि आपके पास ऐसा कोई प्रकार है जिसमें सदस्यों को स्थानांतरित करने की आवश्यकता है या यहां तक ​​कि एक चलने योग्य प्रकार (std::unique_ptr, आदि) भी शामिल है, तो आप स्वयं को स्थान लिखना चाहिए।

  2. जब आपको कॉपी कन्स्ट्रक्टर/असाइनमेंट ऑपरेटर और/या विनाशक की आवश्यकता हो सकती है। यदि आपकी कक्षा में कुछ ऐसा है जो आपको मैन्युअल रूप से कॉपी तर्क लिखता है या किसी विनाशक में विशेष सफाई की आवश्यकता है, तो बाधाएं अच्छी हैं कि आपको तर्क को भी स्थानांतरित करने की आवश्यकता होगी। ध्यान दें कि इसमें प्रतिलिपि तंत्र (Type(const Type &t) = delete;) को हटा रहा है।यदि आप ऑब्जेक्ट की प्रतिलिपि बनाना नहीं चाहते हैं, तो आपको शायद तर्क को स्थानांतरित करने या move फ़ंक्शंस को हटाने की आवश्यकता है।

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

नोट का उपयोग कर रहे: इस कदम के साथ एक अच्छा चाल या कॉपी असाइनमेंट ऑपरेटरों है उन्हें मूल्य से लेने के लिए:

Type &operator=(Type t) { std::swap(*this, t); return *this; } 

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

नकारात्मक पक्ष को लिखने की कार्यों की संख्या को कम करता है कि आप प्रभावी रूप से दो बार कॉपी कर रहे हैं Type (पहले ही बुनियादी प्रकार से बना है है पैरामीटर में कॉपी करें, swap में दूसरा)। बेशक, आपको swap को लागू/विशेषज्ञता देना है, लेकिन यह मुश्किल नहीं है।

+0

+1 के लिए * जब आप विजुअल स्टूडियो 10 या 11 * –

+0

का उपयोग कर रहे हैं तो, वीएस 11 में, यदि मेरे पास 3 'std :: string' सदस्यों के साथ कक्षा है, तो संकलक स्वचालित रूप से उस स्थानांतरित सामग्री को उत्पन्न नहीं करेगा जो उपयोग करेगा 'std :: string' के अपने कदम कार्यान्वयन? –

+1

@ जोहान गेल: आप अपने कथन से "अगर मेरे पास कक्षा है ..." को हटा सकते हैं। सही फॉर्मूलेशन है, "कंपाइलर ** कुछ भी ** ** के लिए स्वचालित रूप से चाल सामग्री उत्पन्न नहीं करेगा"। इससे कोई फर्क नहीं पड़ता कि इसमें चलने योग्य प्रकार हैं या नहीं; यदि आप एक चालक कन्स्ट्रक्टर चाहते हैं, तो आपको इसे मैन्युअल रूप से कार्यान्वित करना होगा। हालांकि वीएस 10 के मामले में, एक बहाना था: उस समय के मानक के पास भी अंतर्निहित रूप से उत्पन्न कन्स्ट्रक्टर नहीं थे। –

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

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