2009-02-02 17 views
12

मैं विभिन्न स्मार्ट सूचक कार्यान्वयन (वाह, वहाँ एक बहुत सारे हैं) और यह कि उनमें से ज्यादातर दो व्यापक वर्गीकरण में वर्गीकृत किया जा सकता है मुझे लगता है का मूल्यांकन किया गया है:सी ++ में स्मार्ट पॉइंटर्स को लागू करने का सबसे अच्छा तरीका क्या है?

1) इस श्रेणी में पर उत्तराधिकार का उपयोग करता है वस्तुओं का संदर्भ दिया गया है ताकि उनके पास संदर्भ संख्याएं हों और आमतौर पर ऊपर() और नीचे() (या उनके समकक्ष) लागू हों। आईई, स्मार्ट पॉइंटर का उपयोग करने के लिए, जिन वस्तुओं को आप इंगित कर रहे हैं उन्हें कुछ कक्षा से प्राप्त होना चाहिए जो रेफरी कार्यान्वयन प्रदान करता है।

2) यह श्रेणी संदर्भ गणना रखने के लिए एक द्वितीयक वस्तु का उपयोग करती है। उदाहरण के लिए, किसी ऑब्जेक्ट पर स्मार्ट पॉइंटर को इंगित करने के बजाय, यह वास्तव में इस मेटा डेटा ऑब्जेक्ट पर इंगित करता है ... संदर्भ संदर्भ संख्या और ऊपर() और नीचे() कार्यान्वयन (और जो आमतौर पर सूचक के लिए एक तंत्र प्रदान करता है वास्तविक वस्तु को इंगित किया जा रहा है, ताकि स्मार्ट सूचक ऑपरेटर ->()) को सही ढंग से कार्यान्वित कर सके।

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

2 समस्या है क्योंकि गिनती किसी अन्य ऑब्जेक्ट में संग्रहीत की जाती है, यदि आपके पास कभी ऐसी स्थिति है कि किसी मौजूदा संदर्भ के लिए पॉइंटर को संदर्भ में संदर्भित किया जा रहा है, तो आपके पास शायद एक बग (आईई, गिनती वास्तविक वस्तु में नहीं है, गिनती प्राप्त करने के नए संदर्भ के लिए कोई रास्ता नहीं है ... निर्माण या असाइनमेंट की प्रतिलिपि बनाने से इनकार करना ठीक है, क्योंकि वे गिनती ऑब्जेक्ट साझा कर सकते हैं, लेकिन यदि आपको कभी भी एक से कनवर्ट करना है सूचक, आप पूरी तरह से छिपे हुए हैं) ...

अब, जैसा कि मैं इसे समझता हूं, boost :: shared_pointer तंत्र 2 का उपयोग करता है, या ऐसा कुछ ... उसने कहा, मैं अपने दिमाग को काफी नहीं बना सकता और भी बदतर है! मैंने उत्पादन कोड में केवल तंत्र 1 का उपयोग किया है ... क्या किसी को दोनों शैलियों के साथ अनुभव है? या शायद इन दोनों से बेहतर एक और तरीका है?

उत्तर

27

  1. मत करो "C++ में स्मार्ट संकेत लागू करने के लिए सबसे अच्छा तरीका क्या है"! मौजूदा, अच्छी तरह से परीक्षण किए गए स्मार्ट पॉइंटर का उपयोग करें, जैसे boost :: shared_ptr या std :: tr1 :: shared_ptr (std :: unique_ptr और std :: shared_ptr सी ++ 11 के साथ)
  2. यदि आपको करना है, तो याद रखें:
    1. उपयोग सुरक्षित bool मुहावरा
    2. एक ऑपरेटर>
    3. मजबूत अपवाद गारंटी
    4. दस्तावेज़ प्रदान प्रदान अपवाद आवश्यकताओं अपनी कक्षा Deleter पर बनाता है
    5. उपयोग कॉपी-संशोधित-स्वैप जहां संभव मजबूत अपवाद gua लागू करने के लिए rantee
    6. दस्तावेज़ है कि क्या आप सही ढंग से multithreading संभाल
    7. लिखने व्यापक इकाई परीक्षण
    8. इस तरह से है कि यह व्युत्पन्न सूचक प्रकार पर नष्ट करेगा में रूपांतरण-से-आधार को लागू (policied स्मार्ट संकेत/गतिशील Deleter स्मार्ट संकेत)
    9. कच्चे सूचक को
    10. समर्थन मिल रहा है पहुँच
    11. पर विचार कमजोर संकेत उपलब्ध कराने के अपने स्मार्ट संकेत के लिए चक्र
    12. उचित कास्टिंग ऑपरेटरों प्रदान तोड़ने के लिए
    13. बनाने की लागत/लाभ अपने व्युत्पन्न से बेस पॉइंटर बनाने के लिए कन्स्ट्रक्टर टेम्पलेट किया गया।

और कुछ भी मैं ऊपर अपूर्ण सूची में भूल गए हैं मत भूलना।

7

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

मेरे होमब्री गेम इंजन में मैंने सामान्य पॉइंटर्स को जितना संभव हो सके shared_ptr के साथ बदल दिया है। प्रदर्शन इस कारण को हिट करता है वास्तव में इतना बुरा नहीं है यदि आप संदर्भ द्वारा अधिकांश कार्यों को कॉल कर रहे हैं ताकि संकलक को बहुत अधिक अस्थायी shared_ptr उदाहरण बनाने की आवश्यकता न हो।

+1

इसके अतिरिक्त, shared_ptr का बढ़ावा संस्करण TR1 में माइग्रेट हो गया है और अंत में मानक सी ++ लाइब्रेरी होगी। –

+1

बूस्ट स्मार्ट पॉइंटर्स की एक प्रदर्शन तुलना यहां है: http://www.boost.org/doc/libs/1_39_0/libs/smart_ptr/smarttests.htm –

1

2 के साथ समस्या को चारों ओर काम किया जा सकता है। बूस्ट इसी कारण से boost :: shared_from_this प्रदान करता है। व्यवहार में, यह एक बड़ी समस्या नहीं है।

लेकिन आपके विकल्प # 2 के साथ जाने का कारण यह है कि इसका उपयोग सभी मामलों में किया जा सकता है। विरासत पर निर्भर करना हमेशा एक विकल्प नहीं होता है, और फिर आपको एक स्मार्ट पॉइंटर के साथ छोड़ दिया जाता है जिसका उपयोग आप अपने कोड के आधे के लिए नहीं कर सकते।

मुझे कहना होगा कि # 2 सबसे अच्छा है, क्योंकि इसका उपयोग किसी भी परिस्थिति में किया जा सकता है।

3

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

उदाहरण:

class Event { 
public: 
typedef boost::intrusive_ptr<Event> Ptr; 
void addRef(); 
unsigned release(); 
\\ ... 
private: 
unsigned fRefCount; 
}; 

inline void Event::addRef() 
{ 
    fRefCount++; 
} 
inline unsigned Event::release(){ 
    fRefCount--; 
    return fRefCount; 
} 

inline void intrusive_ptr_add_ref(Event* e) 
{ 
    e->addRef(); 
} 

inline void intrusive_ptr_release(Event* e) 
{ 
    if (e->release() == 0) 
    delete e; 
} 

PTR typedef प्रयोग किया जाता है, ताकि मैं आसानी से बढ़ावा :: shared_ptr < के बीच switcth> और बढ़ावा देने :: < intrusive_ptr> किसी भी ग्राहक कोड

3

आप छड़ी तो बदले बिना मानक लाइब्रेरी में मौजूद लोगों के साथ आप ठीक होंगे।
हालांकि आपके द्वारा निर्दिष्ट किए गए कुछ अन्य प्रकार हैं।

  • साझा: जहां एक वस्तु वस्तु का मालिक है लेकिन हस्तांतरण की अनुमति दी है: जहां स्वामित्व से अधिक ऑब्जेक्ट
  • स्वामित्व के बीच साझा किया जाता है।
  • अनावश्यक: जहां एक वस्तु ऑब्जेक्ट का मालिक है और इसे स्थानांतरित नहीं किया जा सकता है।

मानक पुस्तकालय है:

  • std :: auto_ptr

बूस्ट से tr1 द्वारा रूपांतरित किया गया है (मानक के अगले संस्करण)

    कुछ और है
  • std :: tr1 :: shared_ptr
  • std :: tr1 :: weak_ptr

और जो अभी भी बूस्ट में हैं (जो अपेक्षाकृत एक होना चाहिए) जो आशा है कि इसे tr2 में बनाएं।

  • बढ़ावा :: scoped_ptr
  • बढ़ावा :: scoped_array
  • बढ़ावा :: shared_array
  • बढ़ावा :: intrusive_ptr

देखें: Smart Pointers: Or who owns you baby?

+0

मुझे विश्वास नहीं है कि boost :: scoped_ptr ने इसे बनाया tr1, तो यह अभी भी बढ़ावा है :: scoped_ptr, std :: tr1 :: scoped_ptr नहीं। –

+0

अप्स। मेरी गलती। –

2

यह मुझे लगता है यह प्रश्न पूछने की तरह है "सबसे अच्छा सॉर्ट एल्गोरिदम कौन सा है?" कोई जवाब नहीं है, यह आपकी परिस्थितियों पर निर्भर करता है।

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

दूसरी तरफ, जो किसी भी व्यक्ति के लिए TR1 का उपयोग कर सकता है, मुझे लगता है कि टाइप 2 std :: tr1 :: shared_ptr क्लास एक समझदार डिफ़ॉल्ट विकल्प होगी, जब भी कुछ दबाव न हो, इसके प्रयेाग के लिए।

9

बस सर्वव्यापक बूस्ट जवाब (भले ही यह कई उपयोगों के लिए सही जवाब है) करने के लिए एक अलग दृष्टिकोण की आपूर्ति करने, स्मार्ट संकेत के Loki के कार्यान्वयन पर एक नज़र डालें। डिजाइन दर्शन पर एक प्रवचन के लिए, लोकी के मूल निर्माता ने Modern C++ Design पुस्तक लिखी।

+0

+1 क्योंकि बूस्ट इसके स्मार्ट पॉइंटर्स के लिए deep_copy विकल्प प्रदान नहीं करता है और मुझे लगता है कि यह शर्म की बात है। – n1ckp

1

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

सामान्यतः, दो सूचक प्रकारों के बीच अंतर महत्वपूर्ण नहीं थे। एकमात्र अपवाद जा रहा है हमारे गैर दखल सूचक परोक्ष एक कच्चे सूचक से परिवर्तित की है कि प्रारंभिक संस्करणों और यदि संकेत दिए गए गलत तरीके से उपयोग किया जाता है यह आसानी से स्मृति समस्याओं को जन्म दे सकते हैं:

void doSomething (NIPtr<int> const &); 

void foo() { 
    NIPtr<int> i = new int; 
    int & j = *i; 
    doSomething (&j);   // Ooops - owned by two pointers! :(
} 

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

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

+0

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

1

आप क्या बात कर रहे हैं घुसपैठ और गैर दखल स्मार्ट संकेत दिए गए हैं। बूस्ट दोनों है। boost::intrusive_ptr आपके ऑब्जेक्ट की संदर्भ गणना को कम करने और बढ़ाने के लिए एक फ़ंक्शन को कॉल करता है, हर बार इसे संदर्भ गणना बदलने की आवश्यकता होती है। यह सदस्य कार्यों को कॉल नहीं कर रहा है, लेकिन मुफ्त कार्यों। तो यह अपने प्रकार की परिभाषा को बदलने की जरूरत के बिना वस्तुओं को प्रबंधित करने की अनुमति देता है। और जैसा कि आप कहते हैं, boost::shared_ptr गैर-घुसपैठिया है, आपकी श्रेणी 2.

मेरे पास intrusive_ptr: Making shared_ptr not use delete समझाते हुए एक उत्तर है। संक्षेप में, आप इसका उपयोग करते हैं यदि आपके पास कोई ऑब्जेक्ट है जो पहले से ही संदर्भ गणना कर रहा है, या किसी ऑब्जेक्ट की आवश्यकता है (जैसा कि आप समझाते हैं) जिसे पहले से ही एक intrusive_ptr के स्वामित्व में संदर्भित किया गया है।

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

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