2012-12-17 34 views
7

boost library documentation से मैं इस पढ़ें:क्लाइंट गुणों के रूप में स्मार्ट पॉइंटर्स के साथ ऑब्जेक्ट कॉपी करने के लिए कैसे संपर्क करें?

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

मुझे एक बहुत ही सरल समस्या है: मैं एक वर्ग के पॉइंटर विशेषताओं के लिए आरएआईआई का उपयोग करना चाहता हूं जो कॉपी करने योग्य और असाइन करने योग्य है।

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

चाहिए मैं एक प्रतिलिपि बनाने योग्य स्मार्ट सूचक के एक कार्यान्वयन के लिए खोज (डेटा छोटे हैं, इसलिए मैं Copy on Write संकेत की जरूरत नहीं है), या के रूप में this answer में दिखाया गया है मैं अपने वस्तुओं के प्रति निर्माताओं के प्रति आपरेशन प्रतिनिधि करते हो ?

मैं कौन सा स्मार्ट सूचक एक वर्ग के सरल आरएआईआई के लिए चुनता हूं जो कॉपी करने योग्य और असाइन करने योग्य है? (मैं सोच रहा हूँ कि वर्ग प्रति निर्माता और असाइनमेंट ऑपरेटर को प्रत्यायोजित कॉपी/काम से संचालित होने वाले unique_ptr एक उचित विकल्प बनाने होगा, लेकिन मुझे यकीन है कि नहीं कर रहा हूँ)

यहाँ कच्चे संकेत का उपयोग कर समस्या के लिए एक स्यूडोकोड है, यह सिर्फ एक समस्या विवरण, नहीं एक चल सी ++ कोड है:

// Operation interface 
class ModelOperation 
{ 
    public: 
     virtual void operate =(); 
}; 

// Implementation of an operation called Special 
class SpecialModelOperation 
: 
    public ModelOperation 
{ 
    private: 
     // Private attributes are present here in a real implementation. 

    public: 

     // Implement operation 
     void operate() {}; 
}; 

// All operations conform to ModelOperation interface 
// These are possible operation names: 
// class MoreSpecialOperation; 
// class DifferentOperation; 

// Concrete model with different operations 
class MyModel 
{ 
    private: 
     ModelOperation* firstOperation_; 
     ModelOperation* secondOperation_; 

    public: 

     MyModel() 
      : 
       firstOperation_(0), 
       secondOperation_(0) 
     { 
      // Forgetting about run-time type definition from input files here. 
      firstOperation_ = new MoreSpecialOperation(); 
      secondOperation_ = new DifferentOperation(); 
     } 

     void operate() 
     { 
      firstOperation_->operate(); 
      secondOperation_->operate(); 
     } 

     ~MyModel() 
     { 
      delete firstOperation_; 
      firstOperation_ = 0; 

      delete secondOperation_; 
      secondOperation_ = 0; 
     } 
}; 

int main() 
{ 

    MyModel modelOne; 

    // Some internal scope 
    { 
     // I want modelTwo to have its own set of copied, not referenced 
     // operations, and at the same time I need RAII to for the operations, 
     // deleting them automatically as soon as it goes out of scope. 
     // This saves me from writing destructors for different concrete models. 
     MyModel modelTwo (modelOne); 
    } 


    return 0; 
} 
+0

यदि आप [shared_ptr'] का विवरण पढ़ते हैं (http://www.boost.org/doc/libs/1_52_0/libs/smart_ptr/shared_ptr.htm) तो आप देखेंगे कि वे प्रतिलिपि बनाने योग्य और असाइन करने योग्य हैं। –

+1

@ जोचिम हां, bu वे एक ही वस्तु का संदर्भ साझा करते हैं। मुझे एक गहरी प्रतिलिपि करने के लिए कॉपी ऑपरेशन की आवश्यकता है। – tmaric

+0

किसी भी विकल्प पर किसी भी स्मार्ट सूचक का उपयोग नहीं कर रहा है? –

उत्तर

5

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

template <typename Base> 
struct clonable { 
    // virtual copy 
    // this clone function will be generated via templates 
    // no boilerplate is involved 
    virtual std::unique_ptr<clonable<Base>> clone() const = 0; 

    // expose the actual data 
    virtual Base* get() = 0; 
    virtual Base const* get() const = 0; 

    // virtual destructor 
    // note that this also obviates the need for a virtual destructor on Base 
    // I would probably still make it virtual, though, just in case 
    virtual ~clonable() = default; 
}; 

इस इंटरफ़ेस एक वर्ग सबसे व्युत्पन्न प्रकार पर टेम्प्लेट द्वारा कार्यान्वित किया जाता है और इस तरह जानता है:

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

template <typename Base, typename Derived> 
struct clonable_holder : clonable<Base> { 
    // I suppose other constructors could be provided 
    // like a forwarding one for emplacing, but I am going for minimal here 
    clonable_holder(Derived value) 
    : storage(std::move(value)) {} 

    // here we know the most derived type, so we can use the copy constructor 
    // without risk of slicing 
    std::unique_ptr<clonable<Base>> clone() const override { 
     return std::unique_ptr<clonable<Base>>(new clonable_holder(storage)); 
    } 

    Base* get() override { return &storage; } 
    Base const* get() const override { return &storage; } 

private: 
    Derived storage; 
}; 

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

template <typename Base> 
struct polymorphic_value { 
    // this constructor captures the most derived type and erases it 
    // this is a point where slicing may still occur 
    // so making it explicit may be desirable 
    // we could force constructions through a forwarding factory class for extra safety 
    template <typename Derived> 
    polymorphic_value(Derived value) 
    : handle(new clonable_holder<Base, Derived>(std::move(value))) { 
     static_assert(std::is_base_of<Base, Derived>::value, 
      "value must be derived from Base"); 
    } 

    // moving is free thanks to unique_ptr 
    polymorphic_value(polymorphic_value&&) = default; 
    polymorphic_value& operator=(polymorphic_value&&) = default; 

    // copying uses our virtual interface 
    polymorphic_value(polymorphic_value const& that) 
    : handle(that.handle->clone()) {} 
    polymorphic_value& operator=(polymorphic_value const& that) { 
     handle = that.handle->clone(); 
     return *this; 
    } 

    // other useful constructors involve upcasting and so on 

    // and then useful stuff for actually using the value 
    Base* operator->() { return handle.get(); } 
    Base const* operator->() const { return handle.get(); } 
    // ... 

private: 
    std::unique_ptr<clonable<Base>> handle; 
}; 

यह केवल एक न्यूनतम इंटरफ़ेस है, लेकिन इसे आसानी से अधिक उपयोग परिदृश्यों को कवर करने के लिए यहां से बाहर निकाला जा सकता है।

+0

विस्तृत उत्तर के लिए निश्चित रूप से एक, धन्यवाद! हालांकि, यह एक सूचक को लपेटने और इसके लिए प्रतिलिपि बनाने और असाइनमेंट की तुलना में अधिक जटिल लगता है। यह अनुभव की कमी हो सकती है ... लेकिन यह समस्या के लिए एक जटिल दृष्टिकोण के रूप में प्रतीत होता है ... – tmaric

+0

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

+0

यह भी ध्यान दें कि आपका उदाहरण अपवाद सुरक्षित नहीं है: क्या होता है यदि 'secondOperation_ = new DifferentOperation();' फेंकता है? ऐसा नहीं है कि 'MyModel' के विनाशक को कन्स्ट्रक्टर पूरा नहीं होने पर बुलाया नहीं जाता है, इसलिए पिछली पंक्ति में बनाए गए' MoreSpecialOperation' को केवल रिसाव होगा। एक स्मार्ट पॉइंटर-जैसी कक्षा का उपयोग करना इसे स्वचालित रूप से सुरक्षित कर देगा। और यह सब किसी भी बॉयलरप्लेट के बिना: आपका उदाहरण 'क्लास माईमोडेल {polymorphic_value firstOperation बन जाता है; polymorphic_value <मोडऑपरेशन> दूसरा ऑपरेशन;/* कोई प्रतिलिपि ctors, न ही असाइनमेंट ओप, न ही विनाशकों को लिखने के लिए * /}; ' –

1

मैं तैयार के लिए उपयोग प्राप्ति के बारे में कभी नहीं सुना है, लेकिन आप इसे खुद से बस कर सकते हैं।

सबसे पहले आपको कुछ टेम्पलेट रैपर क्लास लिखना चाहिए जिसमें वर्चुअल क्लोन विधि है, संग्रहीत ऑब्जेक्ट की प्रतिलिपि बनाना। और फिर उस वर्ग के कुछ polymophic धारक जो copyable

हो सकता है और के बारे में भूल नहीं है लिखने जाँच हटाने http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Checked_delete

1

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

यदि मैंने सवाल सही ढंग से समझा है, तो आपको वर्चुअल क्लोन विधि की आवश्यकता होगी। व्युत्पन्न वर्ग के कन्स्ट्रक्टर को सही तरीके से कॉल करने का कोई और तरीका नहीं है।

struct Clonable { 
    virtual ~Clonable() {} 
    virtual Clonable* clone() = 0; 
}; 
struct AutoPtrClonable { 
    AutoPtrClonable(Clonable* cl=0) : obj(cl) { } 
    AutoPtrClonable(const AutoPtrClonable& apc) : obj(apc.obj->clone()) { } 
    ~AutoPtrClonable() { delete obj; } 
    // operator->, operator*, etc 
    Clonable* obj; 
}; 

नमूना कोड का उपयोग करने के लिए, यह एक टेम्पलेट में बनाने, आदि

+0

मैं स्मार्ट पॉइंटर्स को पूरी तरह से छोड़ने के बारे में सोच रहा हूं।ऐसा लगता है कि आरएआईआई के बीच एक संघर्ष होता है जो स्मार्ट पॉइंटर्स की बात करते समय स्वामित्व आधारित होता है, और तथ्य यह है कि स्वामित्व के हस्तांतरण या संदर्भों के उपयोग के बिना std :: या boost :: में कोई प्रतिलिपि योग्य स्मार्ट पॉइंटर उपलब्ध नहीं है। – tmaric

+1

@tomislav आपको स्वामित्व या संदर्भों का उपयोग तब तक करना होगा जब तक आपके पास एक विशेष इंटरफ़ेस (क्लोनबल) न हो: पॉइंटर द्वारा ऑब्जेक्ट दिया गया हो, उदाहरण के लिए सामान्य प्रतिलिपि जैसे प्रतिलिपि बनाने वाला ऑब्जेक्ट डुप्लिकेट नहीं कर सकता है। संकलक कैसे कहेंगे कि किस ऑब्जेक्ट के कन्स्ट्रक्टर को कॉल करना है - सबसे व्युत्पन्न? इसके लिए वर्चुअल लुकअप की आवश्यकता है, और सी ++ में, आपको इसे प्राप्त करने के लिए अपना क्लोन() लिखना होगा। –

+1

यदि आप अपने प्रकारों पर कुछ आवश्यकताओं के साथ रख सकते हैं, तो आप इसे क्लोन फ़ंक्शन के बिना कर सकते हैं और नियमित प्रतिलिपि कन्स्ट्रक्टर के साथ काम कर सकते हैं। समाधान में टेम्पलेट्स के माध्यम से बाहरी रूप से क्लोन फ़ंक्शन की स्वत: पीढ़ी शामिल है। (मैं देखूंगा कि जैसे ही मुझे कुछ खाली समय मिलता है, मैं इसके बारे में एक उत्तर लिख सकता हूं; दुख की बात है कि मेरा वर्तमान प्रोजेक्ट वास्तव में तेज़ हो जाता है) –

0

आप दो समाधान है (वास्तव में आप कई और अधिक है, लेकिन इन मेरे लिए सबसे अधिक उपयुक्त बनाने के :)):

सबसे पहले, आप std::unique_ptr का उपयोग कर सकते हैं। यह एक अच्छा समाधान है क्योंकि यह आपको प्रति संकेत एक उदाहरण देने के लिए मजबूर करता है।(std::shared_ptr का उपयोग करके भी काम करेगा, लेकिन यदि आप कोड को स्पष्ट रूप से नहीं जोड़ते हैं, तो साझा_प्टर के लिए प्रतिलिपि और असाइनमेंट "साझा करें" - विशेष रूप से आप जो टालना चाहते हैं)।

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

दूसरा, आप अपना खुद का रोल कर सकते हैं। इसके बारे में कुछ भी जटिल नहीं है, और हम एक छोटी (10-20 लाइनें या तो) उपयोगिता वर्ग के बारे में बात कर रहे हैं।

व्यक्तिगत रूप से, अगर मुझे एक ही स्थान पर इस स्मार्ट पॉइंटर क्लास का उपयोग करना पड़ा, तो मैं std :: unique_ptr का उपयोग करूंगा। अन्यथा (एकाधिक पॉइंटर्स, एक ही व्यवहार) मैं अपना खुद का रोल करूंगा, बस मुझे कई उदाहरणों (डीआरवाई सिद्धांत के साथ रखने के लिए) की गहरी प्रतिलिपि दोहराना नहीं होगा।

4

यह थोड़ा देर हो चुकी है, लेकिन भविष्य के दर्शकों के लिए: मेरे हेडर-केवल लाइब्रेरी Aurora और इसके SmartPtr tutorial में उपयोग में आसान कार्यान्वयन है। अरोड़ा के साथ, स्मार्ट पॉइंटर्स के माध्यम से गहरी प्रतिलिपि लागू करना मुश्किल है। निम्नलिखित कोड किसी भी copyable प्रकार T के लिए काम करता है:

aurora::CopiedPtr<T> first(new T); 
aurora::CopiedPtr<T> second = first; // deep copy 

यह अक्सर अनावश्यक तीन बड़ी/पांच को लागू करने की अपनी कक्षाओं सूचक सदस्य अगर बनाता है।

+3

यह बहुत देर हो चुकी नहीं है। मैंने आपको जवाब दिया और पाया कि आपकी लाइब्रेरी ठीक वही है जो मैं ढूंढ रहा था। धन्यवाद! –