2008-11-03 16 views
23

स्कॉट मायर्स ने लिखा है, आप क्लोन() को वास्तविक प्रकार के घोषित करने के लिए क्लोन() घोषित करने के लिए सी ++ के प्रकार-सिस्टम में छूट का लाभ उठा सकते हैं:सी ++ में क्लोन() के लिए सबसे अच्छा हस्ताक्षर क्या है?

class Base 
{ 
    virtual Base* clone() const = 0; 
}; 

class Derived : public Base 
{ 
    virtual Derived* clone() const 
}; 

संकलक यह पता लगाता है कि क्लोन() एक क्लोन() देता है ऑब्जेक्ट के प्रकार के लिए पॉइंटर, और व्युत्पन्न करने के लिए एक पॉइंटर वापस करने के लिए व्युत्पन्न करने के लिए व्युत्पन्न की अनुमति देता है।

क्लोन() एक स्मार्ट पॉइंटर लौटने के लिए वांछनीय होगा जो स्वामित्व अर्थशास्त्र का हस्तांतरण निम्नानुसार करता है:

class Base 
{ 
    virtual std::auto_ptr<Base> clone() const = 0; 
}; 

class Derived : public Base 
{ 
    virtual std::auto_ptr<Derived> clone() const; 
}; 

दुर्भाग्यवश, सम्मेलनों की छूट टेम्पलेट स्मार्ट पॉइंटर्स पर लागू नहीं होती है, और कंपाइलर ओवरराइड की अनुमति नहीं देगा।

तो, ऐसा लगता है कि मुझे दो विकल्पों के साथ छोड़ा गया है:

  1. क्लोन() एक "गूंगा" पॉइंटर लौटाएं, और दस्तावेज़ जो ग्राहक इसका निपटान करने के लिए ज़िम्मेदार हैं।
  2. क्लोन() एक स्मार्ट बेस पॉइंटर लौटाएं, और क्लाइंट्स को गतिशील पॉइंटर पर सहेजने के लिए डायनामिक_कास्ट का उपयोग करें यदि उन्हें इसकी आवश्यकता हो।

इन दृष्टिकोणों में से एक पसंदीदा है? या क्या मेरे पास स्वामित्व अर्थशास्त्र के हस्तांतरण को खाने का कोई तरीका है और मेरी मजबूत प्रकार की सुरक्षा भी है?

उत्तर

5

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

+0

वास्तव में, यह मैं क्या मतलब शुरू हो रहा है - मैं सीधे नए कह सकते हैं कॉपी निर्माता के साथ जब मुझे उस विशेष कक्षा की आवश्यकता थी। – JohnMcG

0

आपके पास दो विधियां, वर्चुअल क्लोन() हो सकती हैं जो मूल प्रकार के चारों ओर एक स्मार्ट पॉइंटर रैपर लौटाती हैं, और एक गैर वर्चुअल क्लोन 2() जो सही प्रकार के स्मार्ट पॉइंटर को लौटाती है।

क्लोन 2 स्पष्ट रूप से क्लोन के संदर्भ में लागू किया जाएगा और कलाकारों को घेर लिया जाएगा।

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

एक और विकल्प क्लोन का एक टेम्पलेट संस्करण बनाना होगा जो आपके द्वारा अपेक्षित प्रकार को स्वीकार करता है, लेकिन यह कॉलर पर अधिक बोझ जोड़ता है।

7

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

+0

+1, उपयोगकर्ता को यह चुनने दें कि कौन सी मेमोरी प्रबंधन रणनीति उसके लिए सबसे अच्छी है ... इसके अलावा बूस्ट पॉइंटर कंटेनर के साथ उपयोग के लिए भी सिफारिश की जाती है :) –

19

वाक्यविन्यास काफी अच्छा नहीं है, लेकिन यदि आप इसे ऊपर दिए गए कोड में जोड़ते हैं, तो क्या यह आपकी सभी समस्याओं का समाधान नहीं करता है?

template <typename T> 
std::auto_ptr<T> clone(T const* t) 
{ 
    return t->clone(); 
} 
+4

इस तरह। इसे मानक से चीजों की तरह दिखने के लिए एक नाम बदलने के लिए make_clone() (make_pair <>) की तरह बदलें। –

+0

बोनस टिप: इसे एक दोस्त बनाएं, और सदस्य क्लोन() निजी बनाएं। – MSalters

+0

नाइटपिक: मैं फ़ंक्शन को 'टी कॉन्स्ट एंड टी' के बजाय स्वीकार करता हूं - यहां पॉइंटर का उपयोग करने की कोई आवश्यकता नहीं है। इसके अलावा मुझे वास्तव में यह दृष्टिकोण पसंद है। – cdhowie

1

boost::intrusive_ptr बजाय shared_ptr या auto/unique_ptr उपयोग करने के लिए एक कारण है।कच्चे सूचक में संदर्भ गणना होती है और इस तरह की स्थितियों में अधिक सहजता से उपयोग किया जा सकता है।

2

Tr1::shared_ptr<> जा सकता है जैसे कि यह एक कच्चा सूचक था।

मुझे लगता है कि क्लोन() shared_ptr<Base> पॉइंटर वापस एक सुंदर साफ समाधान है। आप मामले में tr1::static_pointer_cast<Derived> या tr1::dynamic_pointer_cast<Derived> के माध्यम से shared_ptr<Derived> सूचक डाल सकता यह संकलन समय पर क्लोन वस्तु की तरह निर्धारित करने के लिए संभव नहीं है।

वस्तु की तरह सुनिश्चित करने के लिए predictible है आप इस तरह shared_ptr के लिए एक बहुरूपी कलाकारों का उपयोग कर सकते हैं:

template <typename R, typename T> 
inline std::tr1::shared_ptr<R> polymorphic_pointer_downcast(T &p) 
{ 
    assert(std::tr1::dynamic_pointer_cast<R>(p)); 
    return std::tr1::static_pointer_cast<R>(p); 
} 

भूमि के ऊपर ज़ोर से जोड़ा रिलीज़ संस्करण में दूर फेंक दिया जाएगा।

+0

क्या है !! के लिये? – mmocny

+0

मैंने सोचा कि इसे बूल को अंतर्निहित रूपांतरण को मजबूर करना आवश्यक था: इसके बजाय यह अनावश्यक है। धन्यवाद –

26

उपयोग सार्वजनिक गैर आभासी/निजी आभासी पैटर्न:

class Base { 
    public: 
    std::auto_ptr<Base> clone() { return doClone(); } 
    private: 
    virtual Base* doClone() { return new (*this); } 
}; 
class Derived : public Base { 
    public: 
    std::auto_ptr<Derived> clone() { return doClone(); } 
    private: 
    virtual Derived* doClone() { return new (*this); } 
}; 
+8

या सी ++ 11 के बाद 'std :: unique_ptr <>'। – MSalters

1

के लिए सी ++ 14 MSalters answer अपडेट कर रहा है:

#include <memory> 

class Base 
{ 
public: 
    std::unique_ptr<Base> clone() const 
    { 
     return do_clone(); 
    } 
private: 
    virtual std::unique_ptr<Base> do_clone() const 
    { 
     return std::make_unique<Base>(*this); 
    } 
}; 

class Derived : public Base 
{ 
private: 
    virtual std::unique_ptr<Base> do_clone() const override 
    { 
     return std::make_unique<Derived>(*this); 
    } 
} 
+0

एक गैर वर्चुअल क्लोन? !!! – curiousguy

+0

@curiousguy Whoops! बहुत आभारी... – Daniel

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