2012-05-04 9 views
5

मैं कई वर्गों निम्नलिखित clone समारोह की जरूरत है कि परिभाषित किया जाना है:सी ++ Cloneable mixin

template <typename BASE, typename TYPE> 
class CloneableMixin 
{ 
public: 
    BASE*clone() const { 
    return new TYPE(dynamic_cast<const TYPE &>(*this)); 
    } 
}; 

struct A : public Base, public CloneableMixin<Base, A> 
{ 
}; 

:

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

struct A : public Base 
{ 
    Base * clone() const { 
     return new A(*this); 
    } 
}; 

struct B : public Base 
{ 
    Base * clone() const { 
     return new B(*this); 
    } 
}; 

struct X : public Base2 
{ 
    Base2 * clone() const { 
     return new X(*this); 
    } 
}; 

मैं एक Cloneable mixin के साथ ऐसा करने के लिए इस निरर्थक कोड से बचने के लिए कोशिश कर रहा हूँ हालांकि, यह काम नहीं करता है, क्योंकि CloneableMixin से *thisCloneableMixin<BASE, TYPE> प्रकार का है।

अद्यतन:CloneableMixin को सही प्रकार पर dynamic_cast कर सकते हैं। लेकिन अब मुझे एक और समस्या है: CloneableMixin::clone सफलतापूर्वक Base::clone ओवरराइड नहीं करता है, और इसलिए संकलक रिपोर्ट ए एक सार प्रकार है।

virtual विरासत का कुछ चालाक उपयोग CloneableMixin::cloneBase::clone को ओवरराइड करने की अनुमति देता है? क्या इसके लिए कुछ मैक्रो का उपयोग करना चाहिए?

क्या आप इस अनावश्यक कोड के आसपास एक तरीका जानते हैं?

+0

http://stackoverflow.com/questions/9422760/inheritance-in-curiously-recurring-template-pattern-polymorphic-copy-c – TemplateRex

+1

'बेस :: क्लोन' को संभावित रूप से 'आभासी' होने की आवश्यकता है संभावित संभावित डुप्लिकेट '= 0' वैध होने के लिए। – TemplateRex

+0

@rhalbersma मुझे यकीन नहीं है कि यह काफी वही है। मेरे सभी क्लोनेबल वर्गों में समान आधार प्रकार नहीं होगा (उदा। 'बेस',' बेस 2')। – user

उत्तर

0

आपके क्लोनेबल मिक्सर के तत्कालता के दौरान, व्युत्पन्न कक्षा अभी भी अपूर्ण प्रकार में है। आप इस तरह अविवेक की लौकिक अतिरिक्त leval जोड़ने की कोशिश कर सकते हैं:

template 
< 
    typename Derived 
> 
class Cloneable 
:  
    private CloneableBase 
{ 
public: 
    Derived* clone() const 
    { 
     return static_cast<Derived*>(this->do_clone()); 
    } 

private: 
    virtual Cloneable* do_clone() const 
    { 
     return new Derived(static_cast<const Derived&>(*this)); 
    } 
}; 

class CloneableBase 
{ 
public: 
    CloneableBase* clone() const 
    { 
     return do_clone(); 
    } 

private: 
    virtual CloneableBase* do_clone() const=0; 
}; 

class MyClass: public Cloneable<MyClass>; 
+0

यह समाधान मेरे लिए काम नहीं करता है। मेरे मामले में, ए <- बी <- सी <- डी। ए बेस क्लास (सार) है, बी भी एक अमूर्त वर्ग है।सी और डी कंक्रीट कक्षाएं हैं। एक 'आभासी ए * क्लोन() const = 0; 'और बी समान करता है। जब मैंने सी <- क्लोनेबल दिया, तो जीसीसी ने 'सी' के लिए 'vtable' के अनिर्धारित संदर्भ को कहा। अगर मैं इसे हाथ से कार्यान्वित करता हूं (टेम्पलेट का उपयोग नहीं कर रहा है) कोड ठीक काम करता है। [जीसीसी 4.5.3, सिगविन] –

+0

@ AsukaKenji-SiuChingPong- क्या आप अपना कोड दिखाने के लिए लाइववॉर्स्पेस उदाहरण प्रदान कर सकते हैं? – TemplateRex

4

आभासी विरासत में से कुछ चतुर उपयोग CloneableMixin :: क्लोन बेस :: क्लोन ओवरराइड करने की अनुमति कर सकते हैं?

आपका CloneableMixin<Base,Derived>Base का कोई भी तरीका ओवरराइड नहीं कर सकते हैं - या तो polymorphically या छिपने से - क्योंकि CloneableMixin<Base,Derived> Base से प्राप्त नहीं है।

class Derived : public CloneableMixin<Base,Derived> {....}; 

Base वारिस होगा -

दूसरी ओर, यदि CloneableMixin<Base,Derived>Base से प्राप्त किए गए आपके पास कोई भी जरूरत है कि यह एक mixin होने के लिए है, क्योंकि होगा।

तो अपने उदाहरण की जरूरतों के लिए समाधान यहाँ सचित्र पर्याप्त होगा:

#include <iostream> 

// cloner v1.0 
template <class Base, class Derived> 
struct cloner : Base 
{ 
    Base *clone() const override { 
     return new Derived(dynamic_cast<const Derived &>(*this)); 
    } 
    ~cloner() override {}; 
}; 

struct Base 
{ 
    virtual Base * clone() const = 0; 
    Base() { 
     std::cout << "Base()" << std::endl; 
    } 
    virtual ~Base() { 
     std::cout << "~Base()" << std::endl; 
    } 
}; 


struct A : cloner<Base,A> 
{ 
    A() { 
     std::cout << "A()" << std::endl; 
    } 
    ~A() override { 
     std::cout << "~A()" << std::endl; 
    } 
}; 

int main() 
{ 
    A a; 
    Base * pb = a.clone(); 
    delete pb; 
} 

(यदि आप इसके बजाय सी ++ 11 से करने के लिए सी ++ 03 मानक संकलन कर रहे हैं, तो आप बस हटा सकते हैं override कीवर्ड की घटनाओं।)

यह समाधान कुछ और वास्तविक सांसारिक वर्ग पदानुक्रम, उदाहरण के लिए नीचे टूट जाएगा

#include <iostream> 
#include <memory> 

using namespace std; 

// cloner v1.0 
template<class B, class D> 
struct cloner : B 
{ 
    B *clone() const override { 
     return new D(dynamic_cast<D const&>(*this)); 
    } 
    ~cloner() override {}  
}; 

/* Abstract base class `abstract` keeps the state for all derivatives 
    and has some pure virtual methods. It has some non-default 
    constructors. 
*/ 
struct abstract 
{ 
    virtual ~abstract() { 
     cout << "~abstract()" << endl; 
    } 
    int get_state() const { 
     return _state; 
    } 
    void run() { 
     cout << "abstract::run()" << endl; 
     a_root_method(); 
     another_root_method(); 
    } 
    virtual void a_root_method() = 0; 
    virtual void another_root_method() = 0; 
    virtual abstract * clone() const = 0; 

protected: 

    abstract() 
    : _state(0) { 
     cout << "abstract(): state = " << get_state() << endl; 
    } 
    explicit abstract(int state) : _state(state) { 
     cout << "abstract(" << state << ") : state = " 
     << get_state() << endl; 
    } 
    int _state; 
}; 

/* Concrete class `concrete` inherits `abstract` 
    and implements the pure virtual methods. 
    It echoes the constructors of `abstract`. Since `concrete` 
    is concrete, it requires cloneability. 
*/ 
struct concrete : cloner<abstract,concrete> 
{ 
    concrete() { 
     cout << "concrete(): state = " << get_state() << endl; 
    } 
    explicit concrete(int state) : abstract(state) { //<- Barf! 
     cout << "concrete(" << state << ") : state = " 
      << get_state() << endl; 
    } 
    ~concrete() override { 
     cout << "~concrete()" << endl; 
    } 
    void a_root_method() override { 
     ++_state; 
     cout << "concrete::a_root_method() : state = " 
      << get_state() << endl; 
    } 
    void another_root_method() override { 
     --_state; 
     cout << "concrete::another_root_method() : state = " 
      << get_state() << endl; 
    }  
}; 

int main(int argc, char **argv) 
{ 
    concrete c1; 
    unique_ptr<abstract> pr(new concrete(c1)); 
    pr->a_root_method(); 
    pr->another_root_method(); 
    unique_ptr<abstract> pr1(pr->clone()); 
    pr1->a_root_method(); 
    return 0; 
} 

जब हम इस निर्माण करने के लिए प्रयास करते हैं, संकलक एक त्रुटि पर प्रारंभ abstract(state)concrete की constuctor (Barf! टिप्पणी पर) में दे देंगे, कह रही::

Template Method Pattern के इस चित्र में
error: type 'abstract' is not a direct or virtual base of 'concrete' 

या उस प्रभाव के लिए शब्द। वास्तव में, concrete का प्रत्यक्ष आधार abstract लेकिन cloner<abstract,concrete> नहीं है। हालांकि, हम निर्माता पुनर्लेखन नहीं कर सकते हैं के रूप में:

/*Plan B*/ explicit concrete(int state) : cloner<abstract,concrete>(state){....} 

क्योंकि वहाँ है

cloner<abstract,concrete>::cloner<abstract,concrete>(int) 

जैसी कोई निर्माता है लेकिन संकलक निदान के एक ठीक चलता है। यह वर्चुअल विरासत सहायता कर सकता है। हम abstract जरूरत concrete के आभासी आधार है, जो प्रभावी ढंग से मतलब है "concrete की मानद प्रत्यक्ष आधार" बनने के लिए, और हम सिर्फ B बनाने cloner<B,D> के आभासी आधार से कि प्राप्त कर सकते हैं:

// cloner v1.1 
template<class B, class D> 
struct cloner : virtual B 
{ 
    B *clone() const override { 
     return new D(dynamic_cast<D const&>(*this)); 
    } 
    ~cloner() override {}  
}; 

abstract(): state = 0 
concrete(): state = 0 
concrete::a_root_method() : state = 1 
concrete::another_root_method() : state = 0 
concrete::a_root_method() : state = 1 
~concrete() 
~abstract() 
~concrete() 
~abstract() 
~concrete() 
~abstract() 

अच्छे कारणों आभासी विरासत ओ से सावधान रहना करने के लिए कर रहे हैं:

कि के साथ, हम एक साफ निर्माण और उत्पादन एन सिद्धांत और कम से कम उन मामलों के लिए इसका उपयोग आरक्षित करने के लिए जिसमें आर्किटेक्चरल तर्क है - कामकाज के लिए नहीं, जैसा कि हमने अभी इसका उपयोग किया है।

हम इस समस्या के लिए आभासी विरासत के बिना ऐसा करने के लिए पसंद करते हैं, तो हम किसी भी तरह यह सुनिश्चित करना चाहिए कि cloner<B,D> गूँज B के किसी भी constuctor, मनमाने ढंग से B के लिए के एक निर्माता नहीं है। फिर D का कोई भी संबंधित कन्स्ट्रक्टर इसके प्रत्यक्ष आधार cloner<B,D> जो भी तर्क हैं, आरंभ करने में सक्षम हो जाएगा।

इस के लिए सी ++ 03 एक pipedream है, लेकिन सी ++ 11 में मापदंडों variadic टेम्पलेट के जादू के साथ यह आसान है:

// cloner v1.2 
template<class B, class D> 
struct cloner : B 
{ 
    B *clone() const override { 
     return new D(dynamic_cast<D const&>(*this)); 
    } 
    ~cloner() override {} 
    // "All purpose constructor" 
    template<typename... Args> 
    explicit cloner(Args... args) 
    : B(args...){} 
}; 
इस के साथ

, हम पुनर्लेखन कर सकते हैं concrete निर्माता /*Plan B*/, और के रूप में हमारे पास एक सही निर्माण और निष्पादन योग्य है।

+0

पीएस संकलन जीसीसी 4.7.2 और क्लैंग 3.2 के साथ था –

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