शून्य का नियम

C ++ 03 के संबंध में, "तीन का नियम" है, c ++ 11 के आगमन के साथ इसे "5 के नियम" में बदल दिया गया। यद्यपि ये नियम अनिवार्य रूप से आपके स्वयं के डेटा प्रकारों को डिजाइन करने के लिए अनौपचारिक सिफारिशों से अधिक कुछ नहीं हैं, फिर भी वे अक्सर उपयोगी होते हैं। शून्य का नियम इन सिफारिशों को जारी रखता है। इस पोस्ट में, मैं आपको याद दिलाऊंगा कि वास्तव में, पहले 2 नियम क्या हैं, और "शून्य नियम" के पीछे के विचार को समझाने का भी प्रयास करें।



प्रेरणा



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

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



स्वामित्व की रणनीतियाँ



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

1. नकल और चलती का निषेध;

2. हैंडलर ( गहरी प्रति ) के साथ साझा संसाधन की प्रतिलिपि बनाना ;

3. नकल की मनाही, लेकिन स्थानांतरित करने की अनुमति;

4. संयुक्त स्वामित्व (विनियमित, उदाहरण के लिए, संदर्भ गिनती द्वारा)।



"तीन का नियम" और "पांच का नियम"



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

ऐसा क्यों है, निम्न उदाहरण में देखना आसान है। मान लीजिए कि हमारी कक्षा का कोई सदस्य ढेर पर किसी वस्तु का सूचक है।



class my_handler { public: my_handler(int c) : counter_(new int(c)) {} private: int* counter_; };
      
      







इस स्थिति में डिफ़ॉल्ट विध्वंसक हमें शोभा नहीं देता है, क्योंकि यह केवल काउंटर_ पॉइंटर को ही नष्ट कर देता है, लेकिन इसे इंगित नहीं करता है। एक विध्वंसक को परिभाषित करें।



 my_handler::~my_handler() {delete counter_;}
      
      







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

तो सौदा क्या है? आइए हमेशा सभी 5 "विशेष" कार्यों को परिभाषित करें और सब कुछ ठीक होगा। यह संभव है, लेकिन, स्पष्ट रूप से, त्रुटियों के साथ काफी थकाऊ और भयावह। फिर हम केवल उन लोगों को निर्धारित करते हैं जो वर्तमान स्थिति में वास्तव में आवश्यक हैं, और बाकी को संकलक द्वारा उत्पन्न किया जाए? यह भी एक विकल्प है, लेकिन सबसे पहले, "स्थिति" जिसमें हमारे कोड का उपयोग किया जाता है, हमारी जानकारी के बिना अच्छी तरह से बदल सकता है, और हमारी कक्षा नई परिस्थितियों में काम करने में असमर्थ होगी, और दूसरी बात यह है कि विशेष (और, यह मुझे लगता है, बल्कि भ्रमित करने वाले नियम हैं) संकलक पीढ़ी कल्पना। कार्य करता है। उदाहरण के लिए, "संचलन कार्यों को संकलक द्वारा उत्पन्न नहीं किया जाएगा यदि 5k से कम से कम एक स्पष्ट रूप से घोषित फ़ंक्शन है" या "प्रतिलिपि फ़ंक्शन उत्पन्न नहीं होगा यदि कम से कम एक स्पष्ट रूप से घोषित चालन समारोह है"।



"शून्य का नियम"



मार्टिनो फर्नांडीज द्वारा संभावित समाधानों में से एक को "शून्य नियम" के रूप में आवाज दी गई थी और इसे निम्नानुसार संक्षेप में प्रस्तुत किया जा सकता है: "इसके लिए किसी भी 5k फ़ंक्शन को परिभाषित न करें, बजाय इसके लिए आविष्कार किए गए वर्गों के लिए संसाधनों के स्वामित्व की जिम्मेदारी सौंपें"। और ऐसी विशेष कक्षाएं पहले से ही मानक पुस्तकालय में हैं। ये std :: unique_ptr और std :: share_ptr हैं । इस तथ्य के कारण कि इन कक्षाओं का उपयोग करते समय कस्टम हटाने वाले सेट करना संभव है, आप उन्हें ऊपर वर्णित स्वामित्व की अधिकांश रणनीतियों को लागू करने के लिए उपयोग कर सकते हैं (कम से कम सबसे उपयोगी)। उदाहरण के लिए, यदि कोई वर्ग एक ऐसी वस्तु का मालिक है जिसके लिए संयुक्त स्वामित्व का कोई मतलब नहीं है या वह हानिकारक भी है (फाइल डिस्क्रिप्टर, म्यूटेक्स, स्ट्रीम, आदि), तो इस ऑब्जेक्ट को std में लपेटें :: unique_ptr with the deleter । अब हमारी कक्षा की वस्तु को कॉपी (केवल स्थानांतरित) नहीं किया जा सकता है, और हमारी वस्तु नष्ट होने पर संसाधन का सही विनाश स्वचालित रूप से सुनिश्चित हो जाएगा। यदि संग्रहीत हैंडलर का शब्दार्थ संसाधन के संयुक्त स्वामित्व के लिए अनुमति देता है, तो हम साझा_प्रति का उपयोग करते हैं । एक उदाहरण के रूप में, काउंटर के लिए एक सूचक के साथ उपरोक्त उदाहरण उपयुक्त है।

प्रतीक्षा करें ... लेकिन बहुरूपता विरासत के साथ स्थितियों में, हमें बस व्युत्पन्न वस्तुओं के सही विनाश को सुनिश्चित करने के लिए एक आभासी विध्वंसक घोषित करना होगा। यह पता चला है कि "शून्य नियम" यहां लागू नहीं है? वास्तव में ऐसा नहीं है। इस स्थिति में Shared_ptr हमारी मदद करेगा। तथ्य यह है कि डेलेर ने साझा किया है_प्ट्र 'में संग्रहीत पॉइंटर का वास्तविक प्रकार' याद रखता है।



 struct base {virtual void foo() = 0;}; struct derived : base {void foo() override {...}}; base* bad = new derived; delete bad; // !     base { ... std::shared_ptr<base> good = std::make_shared<derived>(); } // ! shared_ptr     .
      
      







यदि आप साझा किए गए ओवरहेड ओवरहेड से भ्रमित होते हैं, या यदि आप पॉलीमॉर्फिक ऑब्जेक्ट को पॉइंटर का विशेष स्वामित्व प्रदान करना चाहते हैं, तो आप इसे यूनीक_प्ट्र में भी लपेट सकते हैं, लेकिन फिर आपको अपना कस्टम डीलेटर लिखना होगा।



 typedef std::unique_ptr<base, void(*)(void*)> base_ptr; base_ptr good{new base, [](void* p){delete static_cast<derived*>(p);}};
      
      







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



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



संदर्भ


flamingdangerzone.com/cxx11/2012/08/15/rule-of-zero.html

scottmeyers.blogspot.ru/2014/03/a-concern-about-rule-of-zero.html

stackoverflow.com/questions/4172722/what-is-the-rule-of-three

stackoverflow.com/questions/4782757/rule-of-three-becomes-rule-of-five-with-c11



All Articles