2015-02-15 11 views
5

Qui è il mio caso:Prevenire metodo di scrittura clone per ogni sottoclasse

class A 
{ 
public: 
    virtual A* clone() = 0; 
}; 

class B : public A 
{ 
    virtual B* clone() override 
    { 
     return new B(); 
    } 
}; 

Nel mio codice ora il 100% del sublcasses di A solo implementare il metodo clone esattamente allo stesso modo solo per la classe D Ho tipo di ritorno D e creo l'oggetto di D ovviamente.

Come posso evitare questa duplicazione? Quali schemi o trucchi possono aiutare?

Ho pensato di utilizzare CRTP pattern, ma utilizza i modelli e la mia funzione è virtuale. Come comprendiamo i modelli e le funzioni virtuali sono incompatibili.

NVI pattern non funziona, poiché il tipo di ritorno dei metodi di clonazione è diverso.

+0

È possibile utilizzare CRTP, i modelli e le funzioni virtuali non sono incompatibili di per sé. –

+0

Forse non puoi, non sono sicuro se puoi utilizzare l'ereditarietà per soddisfare le funzioni virtuali pure. Questo è ancora indipendente dai modelli però. Forse si può aggirare il problema non usando l'ereditarietà multipla, ma piuttosto usando una classe intermedia, cioè 'classe B: clonabile pubblico {...}' e 'modello classe clonabile: pubblico B {.. .} '. –

+0

Qual è il tuo codice duplicato? La definizione dei metodi clone in ciascuna sottoclasse con solo la nuova chiamata T?O è un codice più grande nei metodi clone nel tuo vero progetto? Se è il secondo caso, puoi semplicemente avere una funzione template che clona un oggetto e chiama questa funzione template dai tuoi metodi clone. – Johnmph

risposta

3

[class.virtual]/8:

Se il tipo di classe nel tipo restituito covariante di D::f differisce da quella della B::f, il tipo di classe nel tipo restituito di D::f è completo a il punto di dichiarazione di D::f o deve essere la classe tipo D.

Quindi CRTP non può lavorare perfettamente, poiché l'argomento di template che si riferisce alla classe derivata sarebbe un tipo incompleto, e tipi restituiti covarianti quindi sono difficili da simulare.

Tuttavia, qui è una prova:

class A 
{ 
public: 
    virtual A* clone() = 0; 
}; 

namespace detail { 
    template <typename T> 
    struct Cloner : A { 
     using A::A; // For constructors 
     virtual A* clone() override {return new T;} 
    }; 
} 

template <typename T> 
struct Cloner : detail::Cloner<T> { 
    using detail::Cloner<T>::Cloner; // For constructors 

    // For callers 
    template <class=void> 
    T* clone() {return static_cast<T*>(detail::Cloner<T>::clone());} 
}; 

class B : public Cloner<B> 
{ 
}; 

Demo.

notare due cose:

  • Il sovraccarico di clone restituire un puntatore al tipo di classe derivata è un modello di funzione. Ecco perché non ignoriamo la funzione virtuale clone: Se lo faremmo, il codice sarebbe mal formato, perché il tipo restituito non è covariante (vedi sopra).

  • Poiché clone è un modello di funzione, per assicurarsi che venga chiamato, possiamo nascondere la funzione virtuale clone. Ciò è ottenuto tramite l'ereditarietà: i nomi nelle classi derivate nascondono i nomi nelle classi base. Quindi, se chiamiamo tramite un riferimento/riferimento B, otteniamo il tipo di restituzione appropriato (B*), poiché il nome viene cercato in ::Cloner prima dello detail::Cloner.

+0

Perché abbiamo bisogno del secondo 'struct Cloner'? Anche perché abbiamo bisogno di 'modello '. Forse potresti dare alcune piccole spiegazioni. Grazie. – Narek

+0

@Narek 1. Nascondere il nome 2. Non sovrascrivere. – Columbo

+0

Non sono così bravo in C++ a capire in due parole, mi dispiace. Gradirei una spiegazione più dettagliata, per favore. – Narek

Problemi correlati