दिलचस्प पोस्ट
मुक्केबाजी और अनबॉक्सिंग: यह कब आता है? TortoiseSVN में एक प्रतिबद्धता को पूर्ववत करना एंड्रॉइड में पार्सल का उपयोग कैसे करें? केंद्र फ़ॉर्म HTML / CSS सबमिट करें VB.NET में यादृच्छिक पूर्णांक MVVMCross एक MvxBindableListView के भीतर ViewModel बदल रहा है एक्सएमएल स्ट्रिंग में सफेद स्थान निकालें उद्देश्य- C #import लूप AngularJS – पृष्ठों के बीच डेटा पासिंग बाहरी निर्भरताओं के साथ मैं ब्राउजरर्मा का उपयोग कैसे करूं? जावा में ऑब्जेक्ट का आकार की गणना करें एंड्रॉइड स्टूडियो 3.0 बीटा 1 में उन्नयन के लिए ग्रिडल त्रुटि Android Google Analytics रेफ़रलकर्ता टैग प्राप्त करें एसोसिएटेटिव सरणी बनाम वस्तु जावास्क्रिप्ट में मैं जावा में एक मौजूदा ज़िप फ़ाइल में प्रविष्टियों को कैसे जोड़ सकता हूं?

स्मार्ट पॉइंटर्स पर मुझे क्यू पॉइंटर्स का उपयोग कब करना चाहिए?

इस उत्तर को पढ़ने के बाद, ऐसा लगता है कि यह स्मार्ट पॉइंटर्स का जितना संभव हो उतना अधिक उपयोग करने के लिए एक सर्वोत्तम अभ्यास है, और "सामान्य" / कच्चे पॉइंटर्स का उपयोग न्यूनतम करने के लिए

क्या यह सच है?

Solutions Collecting From Web of "स्मार्ट पॉइंटर्स पर मुझे क्यू पॉइंटर्स का उपयोग कब करना चाहिए?"

नहीं यह सच नहीं है। अगर किसी फ़ंक्शन को एक संकेतक की आवश्यकता होती है और उसे स्वामित्व के साथ कुछ नहीं करना पड़ता है, तो मुझे दृढ़ता से विश्वास है कि निम्नलिखित कारणों से एक नियमित सूचक दिया जाना चाहिए:

  • कोई स्वामित्व नहीं है, इसलिए आप नहीं जानते कि किस प्रकार का स्मार्ट पॉइंटर पास करना है
  • अगर आप एक विशिष्ट सूचक को पास करते हैं, जैसे कि shared_ptr , तो आप पास नहीं कर पाएंगे, कहते हैं, scoped_ptr

यह नियम होगा – यदि आप जानते हैं कि किसी संस्था को वस्तु का एक निश्चित प्रकार का स्वामित्व लेना चाहिए, तो हमेशा स्मार्ट संकेतक का उपयोग करें- वह व्यक्ति जिसकी आपकी आवश्यकता है यदि स्वामित्व का कोई धारणा नहीं है, तो स्मार्ट पॉइंटर्स का उपयोग कभी नहीं करें

उदाहरण 1:

 void PrintObject(shared_ptr<const Object> po) //bad { if(po) po->Print(); else log_error(); } void PrintObject(const Object* po) //good { if(po) po->Print(); else log_error(); } 

example2:

 Object* createObject() //bad { return new Object; } some_smart_ptr<Object> createObject() //good { return some_smart_ptr<Object>(new Object); } 

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

यहां कच्चे पॉइंटर्स के कुछ बिल्कुल वैध उपयोग हैं (याद रखें, यह हमेशा माना जाता है कि वे गैर-मालिक हैं):

जहां वे संदर्भों के साथ प्रतिस्पर्धा करते हैं

  • तर्क गुजर रहा है; लेकिन संदर्भ निरर्थक नहीं हो सकते, इसलिए बेहतर हैं
  • वर्ग के सदस्यों के रूप में संरचना के बजाय संघ निरूपित करने के लिए; आमतौर पर संदर्भ के लिए बेहतर है क्योंकि असाइनमेंट की शब्दावली अधिक सीधी हैं और इसके अलावा कंस्ट्रक्टर्स द्वारा अपरिवर्तनीय सेट अप सुनिश्चित कर सकते हैं कि वे ऑब्जेक्ट के जीवनकाल के लिए 0 नहीं हैं
  • किसी दूसरे (स्वामित्व वाली संभवतः बहुरूपता) वस्तु को संभाल के रूप में; संदर्भ निरर्थक नहीं हो सकते हैं इसलिए वे बेहतर हैं
  • std::bind एक ऐसे सम्मेलन का उपयोग करता है जहां पारित कर दिया गया तर्क परिणामी मकसद में कॉपी हो जाता है; हालांकि std::bind(&T::some_member, this, ...) केवल सूचक की एक प्रति बना देता है जबकि std::bind(&T::some_member, *this, ...) ऑब्जेक्ट कॉपी करता है; std::bind(&T::some_member, std::ref(*this), ...) एक विकल्प है

जहां वे संदर्भों के साथ प्रतिस्पर्धा नहीं करते हैं

  • के रूप में iterators!
  • वैकल्पिक पैरामीटरों का तर्क पारित करना; यहां वे boost::optional<T&> साथ प्रतिस्पर्धा करते हैं
  • एक (संभवतया बहुरूपता) ऑब्जेक्ट किसी दूसरे हिस्से के लिए संभाल के रूप में, जब उन्हें प्रारंभिक रूप से साइट पर घोषित नहीं किया जा सकता; फिर से, boost::optional<T&> साथ प्रतिस्पर्धा

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


रीकैप करने के लिए, यहां एक स्निपेट है जो उपरोक्त उपयोगों में से कई दर्शाता है हम लिख रहे हैं और एक क्लास का उपयोग कर रहे हैं जो एक फैक्टर को एक std::vector<int> प्रत्येक तत्व के लिए लागू करता है, जबकि कुछ आउटपुट लिखते समय

 class apply_and_log { public: // C++03 exception: it's acceptable to pass by pointer to const // to avoid apply_and_log(std::cout, std::vector<int>()) // notice that our pointer would be left dangling after call to constructor // this still adds a requirement on the caller that v != 0 or that we throw on 0 apply_and_log(std::ostream& os, std::vector<int> const* v) : log(&os) , data(v) {} // C++0x alternative // also usable for C++03 with requirement on v apply_and_log(std::ostream& os, std::vector<int> const& v) : log(&os) , data(&v) {} // now apply_and_log(std::cout, std::vector<int> {}) is invalid in C++0x // && is also acceptable instead of const&& apply_and_log(std::ostream& os, std::vector<int> const&&) = delete; // Notice that without effort copy (also move), assignment and destruction // are correct. // Class invariants: member pointers are never 0. // Requirements on construction: the passed stream and vector must outlive *this typedef std::function<void(std::vector<int> const&)> callback_type; // optional callback // alternative: boost::optional<callback_type&> void do_work(callback_type* callback) { // for convenience auto& v = *data; // using raw pointers as iterators int* begin = &v[0]; int* end = begin + v.size(); // ... if(callback) { callback(v); } } private: // association: we use a pointer // notice that the type is polymorphic and non-copyable, // so composition is not a reasonable option std::ostream* log; // association: we use a pointer to const // contrived example for the constructors std::vector<int> const* data; }; 

स्मार्ट पॉइंटर्स के उपयोग की हमेशा सिफारिश की जाती है क्योंकि वे स्वामित्व को स्पष्ट रूप से दस्तावेज करते हैं।

हम जो वास्तव में याद करते हैं, हालांकि, एक "रिक्त" स्मार्ट पॉइंटर है, जो कि स्वामित्व के किसी भी धारणा को नहीं दर्शाता है

 template <typename T> class ptr // thanks to Martinho for the name suggestion :) { public: ptr(T* p): _p(p) {} template <typename U> ptr(U* p): _p(p) {} template <typename SP> ptr(SP const& sp): _p(sp.get()) {} T& operator*() const { assert(_p); return *_p; } T* operator->() const { assert(_p); return _p; } private: T* _p; }; // class ptr<T> 

यह वास्तव में, किसी भी स्मार्ट पॉइंटर का सबसे सरल संस्करण है जो मौजूद हो सकता है: एक प्रकार का दस्तावेज जिसमें यह संसाधन के मालिक नहीं है, वह भी बात करता है

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

इस कारण से, जब भी मैं ऐसे चीजें पैदा कर रहा हूं जिनके माता-पिता के रिश्ते (जैसे ऑब्जेक्ट्स का एक पेड़) है, तो मैं अपने मूल ऑब्जेक्ट्स को पकड़ने के लिए मैन्युअल ऑब्जेक्ट्स में share_ptrs का उपयोग करूँगा, लेकिन अगर बच्चे ऑब्जेक्ट्स को अपने माता-पिता में एक पॉइंटर की ज़रूरत है , मैं इसके लिए एक सादे सी / सी + + पॉइंटर का प्रयोग करूंगा।

कुछ मामलों में, जहां आप संकेतक का उपयोग करना चाह सकते हैं:

  • फ़ंक्शन पॉइंटर्स (स्पष्ट रूप से कोई स्मार्ट पॉइंटर नहीं)
  • अपने स्मार्ट पॉइंटर या कंटेनर को परिभाषित करना
  • कम स्तर की प्रोग्रामिंग से निपटना, जहां कच्चे संकेत महत्वपूर्ण हैं
  • कच्चे सरणियों से गिरावट

मुझे लगता है कि थोड़ा और अधिक गहरा जवाब यहां दिया गया था: किस तरह का संकेतक मैं उपयोग करता हूँ जब?

उस लिंक से लिखित: "गूंगा संकेत (कच्चे पॉइंटर्स) या संसाधनों के संदर्भ में गैर-स्वामित्व वाले संदर्भों का उपयोग करें और जब आप जानते हैं कि संसाधन संदर्भित वस्तु / क्षेत्र को जीवित करेगा ।" (मूल से संरक्षित बोल्ड)

समस्या यह है कि यदि आप सामान्य उपयोग के लिए कोड लिख रहे हैं तो यह निश्चित रूप से आसान नहीं होगा कि ऑब्जेक्ट कच्चे पॉइंटर से निकलेगा इस उदाहरण पर विचार करें:

 struct employee_t { employee_t(const std::string& first_name, const std::string& last_name) : m_first_name(first_name), m_last_name(last_name) {} std::string m_first_name; std::string m_last_name; }; void replace_current_employees_with(const employee_t* p_new_employee, std::list<employee_t>& employee_list) { employee_list.clear(); employee_list.push_back(*p_new_employee); } void main(int argc, char* argv[]) { std::list<employee_t> current_employee_list; current_employee_list.push_back(employee_t("John", "Smith")); current_employee_list.push_back(employee_t("Julie", "Jones")); employee_t* p_person_who_convinces_boss_to_rehire_him = &(current_employee_list.front()); replace_current_employees_with(p_person_who_convinces_boss_to_rehire_him, current_employee_list); } 

इसके आश्चर्य की बात है, replace_current_employees_with() फ़ंक्शन किसी अनजाने में उसके एक पैरामीटर का उपयोग करके इसे समाप्त करने से पहले उसे समाप्त करने के लिए कारण दे सकता है

इसलिए भले ही इसे पहले की तरह लग सकता है replace_current_employees_with() फ़ंक्शन को इसके पैरामीटर के स्वामित्व की आवश्यकता नहीं होती है, इसके लिए उन्हें अपने मापदंडों की संभावना के खिलाफ किसी तरह की सुरक्षा की जरूरत है, इनकी उपयोग करके इसे खत्म करने से पहले इसे बेअसर से हटा दिया गया है। सबसे आसान उपाय वास्तव में पैरामीटर (एस) के स्वामित्व (अस्थायी साझा) लेना है, संभवत: एक shared_ptr माध्यम से

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

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

यह सत्य है। मैं स्मार्ट पॉइंटर्स पर कच्चे पॉइंटर्स के लाभों को नहीं देख सकता, खासकर जटिल परियोजना में।

सामान्य और हल्के उपयोग के लिए, कच्चे पॉइंटर्स ठीक हैं हालांकि।