Domanda

Sento che questo è stato chiesto prima, ma non riesco a trovarlo su SO, né posso trovare nulla di utile su Google. Forse & Quot; covariant & Quot; non è la parola che sto cercando, ma questo concetto è molto simile ai tipi di ritorno covarianti sulle funzioni, quindi penso che sia probabilmente corretto. Ecco cosa voglio fare e mi dà un errore del compilatore:

class Base;
class Derived : public Base;

SmartPtr<Derived> d = new Derived;
SmartPtr<Base> b = d; // compiler error

Supponi che quelle classi siano completamente arricchite ... Penso che tu abbia avuto l'idea. Non può convertire un SmartPtr<Derived> in SmartPtr<Base> per qualche motivo poco chiaro. Ricordo che questo è normale in C ++ e in molte altre lingue, anche se al momento non ricordo il perché.

La mia domanda principale è: qual è il modo migliore per eseguire questa operazione di assegnazione? Attualmente sto estraendo il puntatore dal SmartPtr, eseguendo l'upgrade esplicito al tipo base, quindi avvolgendolo in un nuovo <=> del tipo appropriato (si noti che questo non sta perdendo risorse perché il nostro < => classe utilizza il conteggio di riferimenti intrusivi). È lungo e disordinato, soprattutto quando devo quindi avvolgere <=> in un altro oggetto ... qualche scorciatoia?

È stato utile?

Soluzione

Sia il costruttore della copia che l'operatore di assegnazione dovrebbero essere in grado di prendere uno SmartPtr di un tipo diverso e tentare di copiare il puntatore dall'uno all'altro. Se i tipi non sono compatibili, il compilatore si lamenterà e se sono compatibili, hai risolto il problema. Qualcosa del genere:

template<class Type> class SmartPtr
{
    ....
    template<class OtherType> SmartPtr(const SmartPtr<OtherType> &blah) // same logic as the SmartPtr<Type> copy constructor

    template<class OtherType> SmartPtr<Type> &operator=(const SmartPtr<OtherType> &blah) // same logic as the SmartPtr<Type> assignment operator
};

Altri suggerimenti

SmartPtr<Base> e SmartPtr<Derived> sono due istanze distinte di un modello SmartPtr. Queste nuove classi non condividono l'eredità che fanno Base e Derived. Quindi, il tuo problema.

  

qual è il modo migliore per eseguire questa operazione di assegnazione?

 SmartPtr<Base> b = d; 

Non invoca l'operatore di assegnazione. Questo invoca il copy-ctor (la copia è elisa nella maggior parte dei casi) ed è esattamente come se tu scrivessi:

 SmartPtr<Base> b(d); 

Fornire un copia-ctor che accetta un SmartPtr<OtherType> e lo implementa. Lo stesso vale per l'operatore di assegnazione. Dovrai scrivere il copy-ctor e op = tenendo presente la semantica di SmartPtr.

I modelli non sono covarianti, e questo va bene; immagina cosa accadrebbe nel caso seguente:

vector<Apple*> va;
va.push_back(new Apple);

// Now, if templates were covariants, a vector<Apple*> could be
// cast to a vector<Fruit*>
vector<Fruit*> & vf = va;
vf.push_back(new Orange); // Bam, we just added an Orange among the Apples!

Per ottenere ciò che stai cercando di fare, la classe SmartPointer deve avere un costruttore templatized, che accetta un altro SmartPointer o un puntatore di un altro tipo. Puoi dare un'occhiata a boost :: shared_ptr, che fa esattamente questo.

template <typename T>
class SmartPointer {

    T * ptr;

  public:
    SmartPointer(T * p) : ptr(p) {}
    SmartPointer(const SmartPointer & sp) : ptr(sp.ptr) {}

    template <typename U>
    SmartPointer(U * p) : ptr(p) {}

    template <typename U>
    SmartPointer(const SmartPointer<U> & sp) : ptr(sp.ptr) {}

    // Do the same for operator= (even though it's not used in your example)
};

Dipende dalla classe SmartPtr. Se ha un costruttore di copie (o nel tuo caso, operatore di assegnazione) che accetta SmartPtr<T>, dove T è il tipo con cui è stato costruito, allora non funzionerà, poiché SmartPtr<T1> non è correlato a SmartPtr<T2> anche se T1 e T2 sono correlati per eredità.

Tuttavia, se SmartPtr ha un templatized costruttore di copia / operatore di assegnazione, con il parametro modello TOther, che accetta SmartPtr<TOther>, allora dovrebbe funzionare.

Supponendo che tu abbia il controllo della classe SmartPtr, la soluzione è fornire un costruttore basato su modelli:

template <class T>
class SmartPtr
{
    T *ptr;
public:

    // Note that this IS NOT a copy constructor, just another constructor that takes 
    // a similar looking class.
    template <class O>
    SmartPtr(const SmartPtr<O> &src)
    {
        ptr = src.GetPtr();
    }
    // And likewise with assignment operator.
};

Se i tipi T e O sono compatibili, funzionerà, in caso contrario verrà visualizzato un errore di compilazione.

Penso che la cosa più semplice sia fornire la conversione automatica in un altro SmartPtr in base a quanto segue:

template <class T>
class SmartPtr
{
public:
    SmartPtr(T *ptr) { t = ptr; }
    operator T * () const { return t; }
    template <class Q> operator SmartPtr<Q> () const
    { return SmartPtr<Q>(static_cast<Q *>(static_cast<T *>(* this))); }
private:
    T *t;
};

Nota che questa implementazione è solida nel senso che il modello dell'operatore di conversione non ha bisogno di conoscere la semantica del puntatore intelligente, quindi il conteggio dei riferimenti non deve essere replicato ecc.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top