Question

Je pense que cette question a déjà été posée, mais je ne parviens pas à la trouver sur SO, ni rien d’utile sur Google. Peut-être & "; Covariant &"; Ce n'est pas le mot que je cherche, mais ce concept est très similaire aux types de retour covariants sur les fonctions, donc je pense que c'est probablement correct. Voici ce que je veux faire et cela me donne une erreur de compilation:

class Base;
class Derived : public Base;

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

Supposons que ces cours soient pleinement développés ... Je pense que vous avez compris. Il ne peut pas convertir un SmartPtr<Derived> en SmartPtr<Base> pour une raison obscure. Je me souviens que c’est normal en C ++ et dans de nombreux autres langages, même si pour le moment je ne me souviens plus pourquoi.

Ma question fondamentale est la suivante: quel est le meilleur moyen d'effectuer cette opération d'attribution? Actuellement, je retire le pointeur du SmartPtr, le convertissant explicitement dans le type de base, puis l'enfermant dans un nouveau <=> du type approprié (notez qu'il ne s'agit pas d'une fuite de ressources, car notre < => la classe utilise le comptage de références intrusif). C'est long et compliqué, surtout quand il me faut ensuite emballer le <=> dans un autre objet ... des raccourcis?

Était-ce utile?

La solution

Le constructeur de copie et l'opérateur d'affectation doivent pouvoir prendre un SmartPtr d'un type différent et tenter de copier le pointeur de l'un à l'autre. Si les types ne sont pas compatibles, le compilateur se plaindra et, s'ils sont compatibles, vous avez résolu votre problème. Quelque chose comme ça:

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

Autres conseils

SmartPtr<Base> et SmartPtr<Derived> sont deux instanciations distinctes du modèle SmartPtr. Ces nouvelles classes ne partagent pas l'héritage que Base et Derived partagent. Par conséquent, votre problème.

  

quel est le meilleur moyen d'effectuer cette opération d'affectation?

 SmartPtr<Base> b = d; 

N'appelle pas l'opérateur d'affectation. Ceci appelle le copieur-copieur (la copie est éliée dans la plupart des cas) et est exactement comme si vous écriviez:

 SmartPtr<Base> b(d); 

Fournissez un copieur qui prend un SmartPtr<OtherType> et mettez-le en œuvre. Il en va de même pour l'opérateur d'affectation. Vous devrez écrire le copieur et op = en gardant à l’esprit la sémantique de SmartPtr.

Les modèles ne sont pas covariants, et c’est bien; imaginez ce qui se passerait dans le cas suivant:

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!

Pour atteindre vos objectifs, la classe SmartPointer doit avoir un constructeur basé sur un modèle, qui prend un autre SmartPointer ou un pointeur d'un autre type. Vous pouvez jeter un coup d'oeil à boost :: shared_ptr, qui fait exactement cela.

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

Dépend de la SmartPtr classe. S'il a un constructeur de copie (ou dans votre cas, un opérateur d'affectation) prenant SmartPtr<T>, où T est le type avec lequel il a été construit, cela ne fonctionnera pas, car SmartPtr<T1> n'a aucun lien avec SmartPtr<T2> même si T1 et T2 sont liés par héritage.

Cependant, si SmartPtr a un opérateur constructeur / affectation de copie modélisé , avec le paramètre de modèle TOther, acceptant SmartPtr<TOther>, cela devrait alors fonctionner.

En supposant que vous maîtrisiez la classe SmartPtr, la solution consiste à fournir un constructeur basé sur un modèle:

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

Si les types T et O sont compatibles, cela fonctionnera. Sinon, vous obtiendrez une erreur de compilation.

Je pense que le plus simple est de fournir une conversion automatique vers un autre SmartPtr selon les critères suivants:

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

Notez que cette implémentation est robuste en ce sens que le modèle d'opérateur de conversion n'a pas besoin de connaître la sémantique du pointeur intelligent, le décompte de références n'a donc pas besoin d'être répliqué, etc.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top