C ++ में अर्थ और संसाधन प्रबंधन की प्रतिलिपि बनाएँ


सी ++ में, प्रोग्रामर को यह तय करना होगा कि उपयोग किए गए संसाधनों को कैसे मुक्त किया जाएगा? कचरा कलेक्टर जैसे कोई स्वचालित उपकरण नहीं हैं। लेख इस समस्या के संभावित समाधानों पर चर्चा करता है, विस्तार से संभावित समस्याओं की जांच करता है, साथ ही संबंधित मुद्दों की एक संख्या।




सामग्री की तालिका


सामग्री की तालिका

परिचय
1. मूल प्रति-स्वामित्व रणनीतियों
1.1। नकल निषेध रणनीति
1.2। विशिष्ट स्वामित्व रणनीति
1.3। गहरी नकल की रणनीति
1.4। सह-स्वामित्व की रणनीति
2. डीप कॉपी रणनीति - समस्याएं और समाधान
2.1। रिकॉर्ड पर कॉपी
2.2। एक वर्ग के लिए एक राज्य विनिमय समारोह को परिभाषित करना
2.3। संकलक द्वारा मध्यवर्ती प्रतियां निकालना
2.4। विस्थापन के शब्दार्थों का क्रियान्वयन
2.5। आवास बनाम डालने
2.6। परिणाम
3. एक साझा स्वामित्व रणनीति को लागू करने के लिए विकल्प
4. विशेष स्वामित्व की रणनीति और आंदोलन शब्दार्थ
5. कॉपी निषेध रणनीति - त्वरित शुरुआत
6. संसाधन और संसाधन स्वामी ऑब्जेक्ट का जीवन चक्र
6.1। संसाधन प्रारंभ पर कब्जा
6.2। उन्नत संसाधन जीवनचक्र प्रबंधन विकल्प
6.2.1। विस्तारित संसाधन जीवन चक्र
6.2.2। एकल संसाधन पर कब्जा
6.2.3। अप्रत्यक्ष रूप से वृद्धि हुई
6.3। सह-स्वामित्व
7. सारांश
क्षुधा
परिशिष्ट ए। लिंक लिंक
विस्थापन का परिशिष्ट B. शब्दार्थ
संदर्भ




परिचय


संसाधन प्रबंधन एक ऐसी चीज है जो C ++ प्रोग्रामर को हर समय करनी होती है। संसाधनों में मेमोरी ब्लॉक, ओएस कर्नेल ऑब्जेक्ट, बहु-थ्रेडेड लॉक, नेटवर्क कनेक्शन, डेटाबेस कनेक्शन और गतिशील मेमोरी में बनाई गई किसी भी ऑब्जेक्ट शामिल हैं। संसाधन तक पहुंच एक डिस्क्रिप्टर के माध्यम से होती है, डिस्क्रिप्टर का प्रकार आमतौर पर एक पॉइंटर होता है या इसका एक उपनाम ( HANDLE , आदि), कभी-कभी पूरे (यूनिक्स फ़ाइल डिस्क्रिप्टर) होता है। संसाधन का उपयोग करने के बाद, आपको इसे जारी करना होगा, अन्यथा जल्दी या बाद में एक आवेदन जो संसाधनों को जारी नहीं करता है (और संभवतः अन्य अनुप्रयोग) संसाधनों से बाहर चलेगा। यह समस्या बहुत तीव्र है, हम कह सकते हैं कि .NET, जावा और कई अन्य प्लेटफार्मों की प्रमुख विशेषताओं में से एक कचरा संग्रहण पर आधारित एकीकृत संसाधन प्रबंधन प्रणाली है।


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


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



1. मूल प्रति-स्वामित्व रणनीतियों


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



1.1। नकल निषेध रणनीति


यह सबसे सरल रणनीति है। इस मामले में, क्लास इंस्टेंस को कॉपी और असाइन करने की मनाही है। विध्वंसक कैप्चर किए गए संसाधन को मुक्त करता है। C ++ में, नकल पर रोक लगाना मुश्किल नहीं है, वर्ग को घोषित करना चाहिए, लेकिन परिभाषित नहीं, बंद कॉपी निर्माता और कॉपी असाइनमेंट ऑपरेटर।


 class X { private:    X(const X&);    X& operator=(const X&); // ... }; 

कंपाइलर और लिंकर द्वारा कॉपी प्रयासों को विफल कर दिया जाता है।


C ++ 11 मानक इस मामले के लिए एक विशेष वाक्यविन्यास प्रदान करता है:


 class X { public:    X(const X&) = delete;    X& operator=(const X&) = delete; // ... }; 

यह वाक्य-विन्यास अधिक दृश्य है और कॉपी करने की कोशिश करने पर कंपाइलर को अधिक सहज संदेश देता है।


मानक लाइब्रेरी (C ++ 98) के पिछले संस्करण में, इनपुट / आउटपुट स्ट्रीम ( std::fstream , आदि) की कक्षाएं कॉपी-प्रतिबंध रणनीति का उपयोग करती std::fstream , और विंडोज पर, MFC ( CFile , CEvent , CMutex , आदि) से कई कक्षाएं। C ++ 11 मानक पुस्तकालय में, कुछ कक्षाएं बहु-थ्रेडेड सिंक्रनाइज़ेशन का समर्थन करने के लिए इस रणनीति का उपयोग करती हैं।



1.2। विशिष्ट स्वामित्व रणनीति


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


 class X { public:    X(const X&) = delete;    X& operator=(const X&) = delete;    X(X&& src) noexcept;    X& operator=(X&& src) noexcept; // ... }; 

इस प्रकार, अनन्य स्वामित्व रणनीति को प्रति-प्रतिबंध रणनीति का विस्तार माना जा सकता है।


C ++ 11 मानक पुस्तकालय में, यह रणनीति स्मार्ट पॉइंटर std::unique_ptr<> और कुछ अन्य वर्गों का उपयोग करती है, उदाहरण के लिए: std::thread , std::unique_lock<> , साथ ही साथ वे कक्षाएं जो पहले कॉपी निषेध रणनीति ( std::fstream std::unique_lock<> उपयोग करती थीं std::fstream , आदि)। विंडोज पर, MFC कक्षाएं जो पहले कॉपी-बैन की रणनीति का उपयोग करती CFile , CEvent भी विशेष स्वामित्व रणनीति ( CFile , CEvent , CMutex , आदि) का उपयोग करने लगीं।



1.3। गहरी नकल की रणनीति


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


यह रणनीति सभी संसाधनों पर लागू नहीं होती है। इसे मेमोरी बफर से जुड़े संसाधनों पर लागू किया जा सकता है, जैसे स्ट्रिंग्स, लेकिन यह बहुत स्पष्ट नहीं है कि इसे ओएस कर्नेल की वस्तुओं जैसे कि फाइल, म्यूटेक्स, आदि पर कैसे लागू किया जाए।


गहरी प्रतिलिपि रणनीति का उपयोग सभी प्रकार के ऑब्जेक्ट स्ट्रिंग्स, std::vector<> और मानक पुस्तकालय के अन्य कंटेनरों में किया जाता है।



1.4। सह-स्वामित्व की रणनीति


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


सह-स्वामित्व रणनीतियों का उपयोग अक्सर स्मार्ट पॉइंटर्स द्वारा किया जाता है, और अपरिवर्तनीय संसाधनों के लिए उनका उपयोग करना भी स्वाभाविक है। std::shared_ptr<> स्मार्ट पॉइंटर इस रणनीति को C ++ 11 मानक लाइब्रेरी में लागू करता है।



2. डीप कॉपी रणनीति - समस्याएं और समाधान


C ++ 98 मानक पुस्तकालय में टाइप T की वस्तुओं के राज्य विनिमय समारोह के लिए एक टेम्पलेट पर विचार करें।


 template<typename T> void swap(T& a, T& b) {    T tmp(a);    a = b;    b = tmp; } 

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



2.1। रिकॉर्ड पर कॉपी


कॉपी ऑन राइट (गाय), जिसे आस्थगित प्रतिलिपि भी कहा जाता है, को एक गहरी कॉपी रणनीति और एक साझा स्वामित्व रणनीति को संयोजित करने के प्रयास के रूप में देखा जा सकता है। प्रारंभ में, किसी ऑब्जेक्ट की प्रतिलिपि बनाते समय, संसाधन विवरणक की प्रतिलिपि बनाई जाती है, संसाधन के बिना, और मालिकों के लिए संसाधन साझा और केवल पढ़ने योग्य हो जाता है, लेकिन जैसे ही कुछ स्वामी को साझा संसाधन को संशोधित करने की आवश्यकता होती है, संसाधन की प्रतिलिपि बनाई जाती है और फिर यह स्वामी उसके लिए काम करता है एक प्रति। गाय को लागू करने से राज्य विनिमय की समस्या हल होती है: संसाधनों का अतिरिक्त आवंटन और नकल नहीं होती है। गाय का उपयोग स्ट्रिंग्स को लागू करते समय काफी लोकप्रिय है; उदाहरण के लिए, CString (MFC, ATL)। गाय और उभरते मुद्दों को लागू करने के संभावित तरीकों की चर्चा [मेयर्स 1], [सटर] में हो सकती है। [गनथेरथ] ने गाय का उपयोग करके एक गाय के कार्यान्वयन का प्रस्ताव दिया std::shared_ptr<> । बहु-थ्रेडेड वातावरण में गाय को लागू करते समय समस्याएं हैं, यही वजह है कि मानक सी ++ 11 पुस्तकालय में तार के लिए गाय का उपयोग करने के लिए मना किया गया है, देखें [जोसुतिस], [गनथोथ]।


गाय विचार का विकास निम्नलिखित संसाधन प्रबंधन योजना की ओर जाता है: साझा स्वामित्व रणनीति का उपयोग करके ऑब्जेक्ट अपरिवर्तनीय और प्रबंधित होता है, यदि आवश्यक हो, तो संसाधन बदलें, एक नया, उचित रूप से संशोधित संसाधन बनाया जाता है, और एक नया मालिक ऑब्जेक्ट वापस किया जाता है। यह स्कीमा .NET और जावा प्लेटफार्मों पर तार और अन्य अपरिवर्तनीय वस्तुओं के लिए उपयोग किया जाता है। कार्यात्मक प्रोग्रामिंग में, इसका उपयोग अधिक जटिल डेटा संरचनाओं के लिए किया जाता है।



2.2। एक वर्ग के लिए एक राज्य विनिमय समारोह को परिभाषित करना


यह ऊपर दिखाया गया था कि नकल और असाइनमेंट के माध्यम से राज्य विनिमय समारोह को कैसे अक्षम किया जा सकता है, इसे सीधे तरीके से लागू किया जा सकता है। और यह काफी व्यापक रूप से उपयोग किया जाता है, उदाहरण के लिए, मानक पुस्तकालय के कई एल्गोरिदम द्वारा इसका उपयोग किया जाता है। एल्गोरिदम के लिए एक और std::swap() का उपयोग नहीं करने के लिए, लेकिन एक अन्य फ़ंक्शन विशेष रूप से वर्ग के लिए परिभाषित किया गया है, दो चरणों का प्रदर्शन किया जाना चाहिए।


1. क्लास में परिभाषित करें एक सदस्य फ़ंक्शन Swap() (नाम महत्वपूर्ण नहीं है) जो राज्यों के विनिमय को लागू करता है।


 class X { public:    void Swap(X& other) noexcept; // ... }; 

आपको यह सुनिश्चित करना होगा कि यह फ़ंक्शन अपवादों को नहीं फेंकता है, C ++ 11 में, ऐसे कार्यों को noexcept घोषित किया जाना चाहिए।


2. कक्षा X (आमतौर पर एक ही हेडर फ़ाइल में) के समान नामस्थान में, नि: शुल्क (गैर-सदस्य) swap() फ़ंक्शन निम्नानुसार परिभाषित करें (नाम और हस्ताक्षर मौलिक हैं):


 inline void swap(X& a, X& b) noexcept { a.Swap(b); } 

उसके बाद, मानक पुस्तकालय के एल्गोरिदम इसका उपयोग करेंगे, न कि std::swap() । यह तर्क आश्रित लुकअप (ADL) नामक एक तंत्र प्रदान करता है। ADL पर अधिक जानकारी के लिए, [Dewhurst1] देखें।


C ++ मानक पुस्तकालय में, सभी कंटेनर, स्मार्ट पॉइंटर्स, साथ ही साथ अन्य वर्ग ऊपर वर्णित राज्य एक्सचेंज फ़ंक्शन को लागू करते हैं।


Swap() सदस्य फ़ंक्शन को आमतौर पर आसानी से परिभाषित किया जाता है: डेटाबेस और सदस्यों के लिए राज्य विनिमय ऑपरेशन को क्रमिक रूप से लागू करना आवश्यक है, अगर वे इसका समर्थन करते हैं, और std::swap() अन्यथा।


उपर्युक्त विवरण कुछ हद तक सरलीकृत है, अधिक विस्तृत एक [मेयर्स 2] में पाया जा सकता है। राज्य विनिमय समारोह से संबंधित मुद्दों की एक चर्चा [सटर / अलेक्जेंड्रेस्कु] में भी पाई जा सकती है।


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


 X& X::operator=(const X& src) {    X tmp(src);    Swap(tmp);    return *this; } 

इस टेम्पलेट को कॉपी और एक्सचेंज मुहावरा या हर्ब सटर मुहावर कहा जाता है, अधिक विवरण के लिए [सटर], [सटर / अलेक्जेंड्रेस्कु], [मेयर्स 2] देखें। इसके संशोधन को विस्थापन के शब्दार्थ को लागू करने के लिए लागू किया जा सकता है, खंड 2.4, 2.6.1 देखें।



2.3। संकलक द्वारा मध्यवर्ती प्रतियां निकालना


वर्ग पर विचार करें


 class X { public:    X(/*  */); // ... }; 

और कार्य करते हैं


 X Foo() { // ...    return X(/*  */); } 

एक सीधा दृष्टिकोण के साथ, Foo() फ़ंक्शन से वापसी को X के उदाहरण की प्रतिलिपि बनाकर महसूस किया जाता है। लेकिन संकलक कोड से कॉपी ऑपरेशन को हटाने में सक्षम हैं, ऑब्जेक्ट सीधे कॉल के बिंदु पर बनाया गया है। इसे रिटर्न वैल्यू ऑप्टिमाइज़ेशन (आरवीओ) कहा जाता है। आरवीओ का उपयोग कंपाइलर डेवलपर्स द्वारा काफी समय से किया जा रहा है और वर्तमान में सी ++ 11 मानक में तय किया गया है। यद्यपि RVO पर निर्णय कंपाइलर द्वारा किया जाता है, प्रोग्रामर इसके उपयोग के आधार पर कोड लिख सकता है। ऐसा करने के लिए, यह वांछनीय है कि फ़ंक्शन में एक वापसी बिंदु है और लौटा हुआ अभिव्यक्ति का प्रकार फ़ंक्शन के रिटर्न मान के प्रकार से मेल खाता है। कुछ मामलों में, एक विशेष कम्प्यूटेशनल कंस्ट्रक्टर को परिभाषित करने की सलाह दी जाती है जिसे "कम्प्यूटेशनल कंस्ट्रक्टर" कहा जाता है, अधिक विवरण के लिए [देहुरस्ट 2] देखें। [Meyers3] और [Guntheroth] में RVO की भी चर्चा की गई है।


संकलक अन्य स्थितियों में मध्यवर्ती प्रतियों को हटा सकते हैं।



2.4। विस्थापन के शब्दार्थों का क्रियान्वयन


चाल शब्दार्थों के कार्यान्वयन में एक चाल निर्माणकर्ता को परिभाषित करने में होता है जिसमें स्रोत के प्रकार प्रकार-संदर्भ का पैरामीटर होता है और उसी पैरामीटर के साथ एक चाल असाइनमेंट ऑपरेटर होता है।


C ++ 11 मानक लाइब्रेरी में, राज्य विनिमय फ़ंक्शन टेम्पलेट निम्नानुसार परिभाषित किया गया है:


 template<typename T> void swap(T& a, T& b) {    T tmp(std::move(a));    a = std::move(b);    b = std::move(tmp); } 

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


स्थानांतरण के शब्दार्थ का उपयोग करके ऊपर वर्णित स्टेट एक्सचेंज फ़ंक्शन की तुलना में बहुत व्यापक संदर्भ में अस्थायी प्रतियां बनाने से बचा जाता है। आंदोलन शब्दार्थ किसी भी प्रतिद्वंद्विता मूल्य पर लागू होता है, जो कि एक अस्थायी, अनाम मान है, साथ ही एक फ़ंक्शन के रिटर्न मूल्य के लिए है अगर इसे स्थानीय रूप से (लैवल्यू सहित) बनाया गया था, और आरवीओ लागू नहीं किया गया था। इन सभी मामलों में, यह गारंटी दी जाती है कि कदम के बाद किसी भी तरह से स्रोत ऑब्जेक्ट का उपयोग नहीं किया जा सकता है। चाल शब्दार्थ भी lvalue मान पर लागू होता है जिसमें std::move() परिवर्तन लागू होता है। लेकिन इस मामले में, प्रोग्रामर इस बात के लिए ज़िम्मेदार है कि चाल के बाद स्रोत वस्तुओं का उपयोग कैसे किया जाएगा (उदाहरण std::swap() )।


मानक C ++ 11 पुस्तकालय को आंदोलन के शब्दार्थ को ध्यान में रखते हुए नया रूप दिया गया है। कई वर्गों ने एक मूव कंस्ट्रक्टर और एक मूव असाइनमेंट ऑपरेटर के साथ-साथ अन्य सदस्य कार्यों को टाइप राइवल रेफरेंस के मापदंडों के साथ जोड़ा है। उदाहरण के लिए, std::vector<T> में void push_back(T&& src) का अतिभारित संस्करण है। यह सब कई मामलों में अस्थायी प्रतियां बनाने से बचने की अनुमति देता है।


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


 class X { public:    X() noexcept {/*    */}    void Swap(X& other) noexcept {/*   */}    X(X&& src) noexcept : X()    {        Swap(src);    }    X& operator=(X&& src) noexcept    {        X tmp(std::move(src)); //         Swap(tmp);        return *this;    } // ... }; 

मूव कंस्ट्रक्टर और मूव असाइनमेंट ऑपरेटर वे सदस्य कार्य हैं जिनके लिए यह सुनिश्चित करना अत्यंत वांछनीय है कि वे अपवादों को नहीं फेंकते हैं और तदनुसार noexcept घोषित किए noexcept । यह आपको अपवादों की सुरक्षा की सख्त गारंटी का उल्लंघन किए बिना मानक पुस्तकालय कंटेनरों के कुछ संचालन का अनुकूलन करने की अनुमति देता है; विवरण के लिए [मेयर्स 3] और [गनथरोथ] देखें। प्रस्तावित टेम्पलेट ऐसी गारंटी प्रदान करता है, बशर्ते कि डिफ़ॉल्ट निर्माता और राज्यों के आदान-प्रदान का सदस्य कार्य अपवाद न फेंके।


कंपाइलर के लिए C ++ 11 मानक स्वचालित रूप से एक मूविंग कंस्ट्रक्टर और एक मूविंग असाइनमेंट ऑपरेटर उत्पन्न करता है। ऐसा करने के लिए, उन्हें "=default" निर्माण का उपयोग करके घोषित किया जाना चाहिए।


 class X { public:    X(X&&) = default;    X& operator=(X&&) = default; // ... }; 

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


सामान्य तौर पर, विस्थापन के शब्दार्थ का कार्यान्वयन और उपयोग काफी "सूक्ष्म बात" है। कंपाइलर कॉपी करने के लिए आवेदन कर सकता है जहां प्रोग्रामर एक कदम की उम्मीद करता है। इस तरह की स्थिति की संभावना को कम करने या कम करने के लिए यहां कुछ नियम दिए गए हैं।


  1. यदि संभव हो, तो कॉपी निषेध का उपयोग करें।
  2. मूव कंस्ट्रक्टर को डिक्लेयर करें और असाइनमेंट ऑपरेटर को noexcept रूप में noexcept
  3. बेस क्लास और सदस्यों के लिए आंदोलन शब्दार्थ को लागू करें।
  4. प्रकार std::move() rvalue संदर्भ के कार्यों के मापदंडों में std::move() परिवर्तन लागू करें।

नियम 2 के ऊपर चर्चा की गई। 4 , rvalue- lvalue (. ). .


 class B { // ...    B(B&& src) noexcept; }; class D : public B { // ...    D(D&& src) noexcept; }; D::D(D&& src) noexcept    : B(std::move(src)) //  {/* ... */} 

, . 6.2.1.



2.5। vs.


, RVO (. 2.3), , . ( ), , . , . C++11 - emplace() , emplace_front() , emplace_back() , . , - — (variadic templates), . , C++11 — .


:


  1. , , .
  2. , , .

, .


 std::vector<std::string> vs; vs.push_back(std::string(3, 'X')); //  vs.emplace_back(3, '7');           //  

std::string , . . , , . , [Meyers3].



2.6। परिणाम


, , . - . . — : , . , , , . : , , «» .


: , , .NET Java. , Clone() Duplicate() .


- - , :


  1. .
  2. .
  3. - rvalue-.

.NET Java - , , .NET IClonable . , .



3.


, . - , . , . Windows: , HANDLE , COM-. DuplicateHandle() , CloseHandle() . COM- - IUnknown::AddRef() IUnknown::Release() . ATL ComPtr<> , COM- . UNIX, C, _dup() , .


C++11 std::shared_ptr<> . , , , , , . , . std::shared_ptr<> [Josuttis], [Meyers3].


: - , ( ). ( ) , . std::shared_ptr<> std::weak_ptr<> . . [Josuttis], [Meyers3].


- [Alexandrescu]. ( ) , [Schildt]. , .


( ) [Alger].


-. [Josuttis] [Alexandrescu].


- .NET Java. , , , .



4.


, C++ rvalue- . C++98 std::auto_ptr<> , , , . , , ( ). C++11 rvalue- , , . C++11 std::auto_ptr><> std::unique_ptr<> . , [Josuttis], [Meyers3].


: - ( std::fstream , etc.), ( std::thread , std::unique_lock<> , etc.). MFC , ( CFile , CEvent , CMutex , etc.).



5. —


. , . , , , . , , , ( ) . , , , . ( ) , . , . — . 6.


, - -, « », - . - . , , , , - . «».



6. -


, - . , -. .



6.1.


- . , , :


  1. . , .
  2. .
  3. .

, , , . C++11 .


« » (resource acquisition is initialization, RAII). RAII ( ), ., [Dewhurst1]. «» RAII. , , , (immutable) RAII.



6.2.


, RAII, , , . - , , - . , , , . .



6.2.1.


, , , , :


  1. , .
  2. .
  3. .
  4. .

C++11 , , , . , - clear() , , , . . , shrink_to_fit() , , (. ).


, RAII, , , . , .


 class X { public: // RAII    X(const X&) = delete;            //      X& operator=(const X&) = delete; //      X(/*  */);              //      ~X();                            //   //     X() noexcept;                    //       X(X&& src) noexcept              //      X& operator=(X&& src) noexcept;  //    // ... }; 

.


 X x;                    //  ""  x = X(/*  */); //   x = X(/*  */); //   ,   x = X();                //   

std::thread .


2.4, - . , - - . .


 class X { // RAII // ... public: // ,         X() noexcept;    X(X&& src) noexcept;    X& operator=(X&& src) noexcept;    void Swap(X& other) noexcept; //      void Create(/*  */); //      void Close() noexcept;        //   // ... }; X::X() noexcept {/*    */} 

:


 X::X(X&& src) noexcept : X() {    Swap(src); } X& X::operator=(X&& src) noexcept {    X tmp(std::move(src)); //     Swap(tmp);    return *this; } 

- :


 void X::Create(/*  */) {    X tmp(/*  */); //      Swap(tmp); } void X::Close() noexcept {    X tmp;    Swap(tmp); } 

, , , - . , , , . , .


- « », , . : , , ( ). : , . , : , , . , . [Sutter], [Sutter/Alexandrescu], [Meyers2].


, RAII .



6.2.2.


RAII . , , , , :


  1. , .
  2. .
  3. . , .
  4. .
  5. .

«» RAII, — . , , . 3. . «», .



6.2.3.


— . RAII , . , . , , ( -). - ( -). 6.2.1, .



6.3.


, - RAII, : . , , .



7.


, , , , . - -.


4 -:


  1. .
  2. .
  3. .
  4. .

. , - : , , - .


, . , , -, , .


- . . , (. 6.2.3). , (. 6.2.1). , . , , . , std::shared_ptr<> .



क्षुधा



. Rvalue-


Rvalue- C++ , , rvalue-. rvalue- T T&& .


:


 class Int {    int m_Value; public:    Int(int val) : m_Value(val) {}    int Get() const { return m_Value; }    void Set(int val) { m_Value = val; } }; 

, rvalue- .


 Int&& r0; // error C2530: 'r0' : references must be initialized 

rvalue- ++ , lvalue. एक उदाहरण:


 Int i(7); Int&& r1 = i; // error C2440: 'initializing' : cannot convert from 'Int' to 'Int &&' 

rvalue:


 Int&& r2 = Int(42); // OK Int&& r3 = 5;       // OK 

lvalue rvalue-:


 Int&& r4 = static_cast<Int&&>(i); // OK 

rvalue- ( ) std::move() , ( <utility> ).


Rvalue rvalue , .


 int&& r5 = 2 * 2; // OK int& r6 = 2 * 2;  // error 

rvalue- .


 Int&& r = 7; std::cout << r.Get() << '\n'; // : 7 r.Set(19); std::cout << r.Get() << '\n'; // : 19 

Rvalue- .


 Int&& r = 5; Int& x = r;           // OK const Int& cx = r;    // OK 

Rvalue- , . , rvalue-, rvalue .


 void Foo(Int&&); Int i(7); Foo(i);            // error, lvalue  Foo(std::move(i)); // OK Foo(Int(4));       // OK Foo(5);            // OK 

, rvalue rvalue- , . rvalue-.


, , , rvalue-, (ambiguous) rvalue .



 void Foo(Int&&); void Foo(const Int&); 


 Int i(7); Foo(i);            // Foo(const Int&) Foo(std::move(i)); // Foo(Int&&) Foo(Int(6));       // Foo(Int&&) Foo(9);            // Foo(Int&&) 

: rvalue- lvalue.


 Int&& r = 7; Foo(r);            // Foo(const Int&) Foo(std::move(r)); // Foo(Int&&) 

, rvalue-, lvalue std::move() . . 2.4.


++11, rvalue- — -. (lvalue/rvalue) this .


 class X { public:    X();    void DoIt() &;  // this   lvalue    void DoIt() &&; // this   rvalue // ... }; X x; x.DoIt();   // DoIt() & X().DoIt(); // DoIt() && 


.


, ( std::string , std::vector<> , etc.) . — . , rvalue- . , , - , - , . , , , rvalue, lvalue. , rvalue. . , ( lvalue), RVO.



संदर्भ


[Alexandrescu]
, . C++.: . अंग्रेजी से - एम।: एलएलसी “आई.डी. », 2002.


[Guntheroth]
, . C++. .: . अंग्रेजी से — .: «-», 2017.


[Josuttis]
, . C++: , 2- .: . अंग्रेजी से - एम।: एलएलसी “आई.डी. », 2014.


[Dewhurst1]
, . C++. , 2- .: . अंग्रेजी से — .: -, 2013.


[Dewhurst2]
, . C++. .: . अंग्रेजी से — .: , 2012.


[Meyers1]
, . C++. 35 .: . अंग्रेजी से — .: , 2000.


[Meyers2]
, . C++. 55 .: . अंग्रेजी से — .: , 2014.


[Meyers3]
, . C++: 42 C++11 C ++14.: . अंग्रेजी से - एम।: एलएलसी “आई.डी. », 2016.


[Sutter]
, . C++.: . अंग्रेजी से — : «.. », 2015.


[Sutter/Alexandrescu]
, . , . ++.: . अंग्रेजी से - एम।: एलएलसी “आई.डी. », 2015.


[Schildt]
, . C++.: . अंग्रेजी से — .: -, 2005.


[Alger]
, . C++: .: . अंग्रेजी से — .: « «», 1999.




Source: https://habr.com/ru/post/hi425837/


All Articles