2012-05-04 14 views
5

ho diverse classi che hanno bisogno della seguente funzione clone da definire:C++ Cloneable mixin

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); 
    } 
}; 

che sto cercando di fare questo con un mixin Cloneable per evitare questo codice ridondante:

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> 
{ 
}; 

Tuttavia, questo non funziona, perché in *this è di tipo CloneableMixin<BASE, TYPE>.

Aggiornamento: il CloneableMixin può dynamic_cast al tipo corretto. Ma ora ho un altro problema: CloneableMixin::clone non sovrascrive con successo Base::clone, e quindi i rapporti del compilatore A sono di tipo astratto.

È possibile utilizzare in modo intelligente l'ereditarietà virtual per consentire a CloneableMixin::clone di ignorare Base::clone? C'è qualche macro che dovrei usare per questo?

Sei a conoscenza di un modo per aggirare tutto questo codice ridondante?

+0

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

+1

'Base :: clone' deve essere' virtuale' in ordine perché '= 0' sia valido. – TemplateRex

+0

@rhalbersma Non sono sicuro se è lo stesso. Non tutte le mie classi clonabili avranno lo stesso tipo di base (ad esempio 'Base',' Base2'). – user

risposta

0

Durante l'istanziazione del mixaggio Cloneable, la classe derivata è ancora in un tipo incompleto. Si potrebbe provare a aggiungere il proverbiale leval aggiuntivo di riferimento indiretto in questo modo:

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

Questa soluzione non funziona per me. Nel mio caso A <- B <- C <- D. A è la classe base (abstract), B è anche una classe astratta.C e D sono lezioni concrete. A dichiara 'virtuale A * clone() const = 0;' e B fa simile. Quando lascio C <- Cloneable , GCC dice "riferimento non definito a" vtable per C''. Il codice funziona bene se lo implemento a mano (non usando i modelli). [GCC 4.5.3, Cygwin] –

+0

@ AsukaKenji-SiuChingPong- Potresti fornire un esempio di LiveWorkspace per mostrare il tuo codice? – TemplateRex

4

può certo uso intelligente di eredità virtuale permetterà CloneableMixin :: clone di ignorare Base :: clone?

tuo CloneableMixin<Base,Derived> non può ignorare qualsiasi metodo di Base - sia polimorfico o nascondendo - perché CloneableMixin<Base,Derived> è non derivati ​​da Base.

D'altra parte, se CloneableMixin<Base,Derived>erano derivato da Base voi non avrebbero più alcun bisogno per essere un mixin, perché -

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

avrebbero ereditato Base.

Così, per le esigenze del vostro esempio, la soluzione illustrata qui sarà sufficiente:

#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; 
} 

(Se si sta compilando allo standard C++ 03 invece di C++ 11, allora si può sufficiente eliminare le occorrenze della parola chiave override.)

Questa soluzione si romperà per alcune gerarchie di classi più reali, ad es.in questa illustrazione della Template Method Pattern:

#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; 
} 

Quando tentiamo di costruire questo, il compilatore darà un errore in l'inizializzazione abstract(state) nel constuctor di concrete (al Barf! commento), dicendo:

error: type 'abstract' is not a direct or virtual base of 'concrete' 

o parole in tal senso. Infatti, la base diretta di concrete non è abstract ma cloner<abstract,concrete>. Tuttavia, non siamo in grado di riscrivere il costruttore come:

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

Poiché non si ha alcuna tale costruttore come

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

Ma il compilatore diagnostica suggerisce una correzione. Questo era virtuale l'ereditarietà può essere d'aiuto. Abbiamo bisogno abstract per diventare una base virtuale di concrete, che significa effettivamente "una base diretta onorario di concrete", e siamo in grado di raggiungere questo solo facendo B una base virtuale di 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 {}  
}; 

Con questo, abbiamo una generazione pulita e uscita:

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() 

ci sono buone ragioni per essere cauti virtuale eredità o n principio e di riservare il suo uso per almeno per i casi in cui ha una giustificazione architetturale - non per soluzioni alternative, come lo abbiamo usato proprio ora.

Se preferiamo fare a meno l'ereditarietà virtuale per questo problema, allora noi dobbiamo garantire in qualche modo che ci sia un costruttore di cloner<B,D> che echi qualsiasi constuctor di B, per arbitraria B. Quindi qualsiasi costruttore corrispondente di D sarà in grado di inizializzare la sua base diretta cloner<B,D> indipendentemente dagli argomenti.

Questa è una chimera per C++ 03, ma con la magia del modello variadic parametri in C++ 11 è facile:

// 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...){} 
}; 

Con questo, siamo in grado di riscrivere il costruttore concrete come /*Plan B*/ e ancora una volta abbiamo una build corretta ed eseguibile.

+0

P.S. La compilazione era con GCC 4.7.2 e Clang 3.2 –