जब मैंने C ++ को बहुत पहले सीखा था, तो मुझे इस बात पर ज़ोर दिया गया था कि C ++ के बिंदु का हिस्सा यह है कि जैसे लूप में "लूप-इनवेरिएंट" होता है, कक्षाओं में भी ऑब्जेक्ट के जीवनकाल से जुड़े इन्वर्टर होते हैं - चीजें जो सच होनी चाहिए जब तक वस्तु जीवित है। ऐसी चीजें जिन्हें निर्माणकर्ताओं द्वारा स्थापित किया जाना चाहिए, और विधियों द्वारा संरक्षित किया जाना चाहिए। इनकैप्सुलेशन / ऐक्सेस कंट्रोल वहां मौजूद है जो आपको इनवियर्स लागू करने में मदद करता है। RAII एक चीज है जिसे आप इस विचार के साथ कर सकते हैं।
C ++ 11 के बाद से अब हमारे पास शब्दार्थ हैं। एक वर्ग के लिए जो गति का समर्थन करता है, किसी वस्तु से आगे बढ़ना औपचारिक रूप से अपने जीवन-समय को समाप्त नहीं करता है - यह कदम इसे कुछ "मान्य" स्थिति में छोड़ने वाला है।
एक कक्षा डिजाइन करने में, क्या यह बुरा अभ्यास है यदि आप इसे डिजाइन करते हैं ताकि वर्ग के आक्रमणकारियों को केवल उस बिंदु तक संरक्षित किया जाए जिससे इसे स्थानांतरित किया जाता है? या यह ठीक है कि अगर यह आपको तेजी से आगे बढ़ने की अनुमति देगा।
इसे ठोस बनाने के लिए, मान लें कि मेरे पास एक गैर-प्रतिलिपि योग्य लेकिन जंगम संसाधन प्रकार है जैसे:
class opaque {
opaque(const opaque &) = delete;
public:
opaque(opaque &&);
...
void mysterious();
void mysterious(int);
void mysterious(std::vector<std::string>);
};
और जो भी कारण के लिए, मुझे इस ऑब्जेक्ट के लिए एक कॉपी करने योग्य आवरण बनाने की आवश्यकता है, ताकि इसका उपयोग किया जा सके, शायद कुछ मौजूदा प्रेषण प्रणाली में।
class copyable_opaque {
std::shared_ptr<opaque> o_;
copyable_opaque() = delete;
public:
explicit copyable_opaque(opaque _o)
: o_(std::make_shared<opaque>(std::move(_o)))
{}
void operator()() { o_->mysterious(); }
void operator()(int i) { o_->mysterious(i); }
void operator()(std::vector<std::string> v) { o_->mysterious(v); }
};
इस copyable_opaque
ऑब्जेक्ट में, निर्माण पर स्थापित वर्ग का एक अपरिवर्तनीय यह है कि सदस्य o_
हमेशा एक वैध ऑब्जेक्ट को इंगित करता है, क्योंकि कोई डिफ़ॉल्ट ctor नहीं है, और केवल ctor जो प्रतिलिपि ctor नहीं है, इनकी गारंटी देता है। सभी operator()
विधियां मानती हैं कि यह अपरिवर्तनीय धारण करता है, और बाद में इसे संरक्षित करता है।
हालाँकि, यदि ऑब्जेक्ट से ले जाया जाता है, तो o_
फिर कुछ नहीं करने के लिए इंगित करेगा। और उस बिंदु के बाद, किसी भी तरीके operator()
को कॉल करने से यूबी / क्रैश हो जाएगा।
यदि ऑब्जेक्ट को कभी भी वहां से नहीं ले जाया जाता है, तो आक्रमणकर्ता को सीधे डॉटर कॉल तक संरक्षित किया जाएगा।
मान लीजिए कि काल्पनिक रूप से, मैंने इस वर्ग को लिखा था, और महीनों बाद, मेरे काल्पनिक सहकर्मी ने यूबी का अनुभव किया, क्योंकि किसी जटिल कार्य में, जहां इन वस्तुओं में से कई को किसी कारण से इधर-उधर किया जा रहा था, वह इन चीजों में से एक से चले गए और बाद में इसकी विधियाँ। स्पष्ट रूप से यह दिन के अंत में उसकी गलती है, लेकिन क्या यह वर्ग "खराब तरीके से डिजाइन किया गया है?"
विचार:
यदि आप उन्हें छूते हैं तो ज़ोंबी ऑब्जेक्ट बनाने के लिए यह आमतौर पर C ++ में खराब रूप है।
यदि आप किसी वस्तु का निर्माण नहीं कर सकते, तो आक्रमणकारियों को स्थापित नहीं कर सकते, तो ctor से एक अपवाद फेंक दें। यदि आप किसी विधि में आक्रमणकारियों को संरक्षित नहीं कर सकते हैं, तो किसी तरह त्रुटि का संकेत दें और रोल-बैक करें। क्या यह स्थानांतरित वस्तुओं से अलग होना चाहिए?क्या यह केवल "दस्तावेज़ के लिए पर्याप्त है" इस ऑब्जेक्ट को स्थानांतरित करने के बाद, हेडर में इसे नष्ट करने के अलावा इसके साथ कुछ भी करना अवैध (यूबी) है?
क्या लगातार यह दावा करना बेहतर है कि यह प्रत्येक विधि कॉल में मान्य है?
इस तरह:
class copyable_opaque {
std::shared_ptr<opaque> o_;
copyable_opaque() = delete;
public:
explicit copyable_opaque(opaque _o)
: o_(std::make_shared<opaque>(std::move(_o)))
{}
void operator()() { assert(o_); o_->mysterious(); }
void operator()(int i) { assert(o_); o_->mysterious(i); }
void operator()(std::vector<std::string> v) { assert(o_); o_->mysterious(v); }
};
कथनों में बहुत हद तक व्यवहार में सुधार नहीं होता है, और वे एक धीमी गति का कारण बनते हैं। यदि आपकी परियोजना "रिलीज बिल्ड / डीबग बिल्ड" योजना का उपयोग करती है, तो हमेशा केवल दावे के साथ चलने के बजाय, मुझे लगता है कि यह अधिक आकर्षक है, क्योंकि आप रिलीज बिल्ड में चेक का भुगतान नहीं करते हैं। यदि आपके पास वास्तव में डिबग बिल्ड नहीं है, तो यह काफी अनाकर्षक लगता है।
- क्या कक्षा को नकल योग्य बनाना बेहतर है, लेकिन चल नहीं सकता?
यह भी बुरा लगता है और एक प्रदर्शन हिट का कारण बनता है, लेकिन यह "अपरिवर्तनीय" मुद्दे को एक सरल तरीके से हल करता है।
आप यहां प्रासंगिक "सर्वोत्तम प्रथाओं" को क्या मानेंगे?