Pregunta

Me siento como este uno se ha preguntado antes, pero soy incapaz de encontrarlo en eso, no puedo encontrar nada útil en Google.Tal vez "covariante" no es la palabra que estoy buscando, pero este concepto es muy similar a la covariante tipos de retorno en funciones, por lo que creo que es probablemente la correcta.Aquí es lo que yo quiero hacer, y me da un error del compilador:

class Base;
class Derived : public Base;

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

Asumir esas clases son totalmente desarrollado...Creo que usted consigue la idea.No puede convertir un SmartPtr<Derived> en un SmartPtr<Base> para algunos claro la razón.Recuerdo que esto es normal en C++ y en muchos otros idiomas, aunque por el momento no puedo recordar por qué.

Mi raíz pregunta es:¿cuál es la mejor forma de realizar esta operación de asignación?Actualmente, estoy tirando el puntero fuera de la SmartPtr, explícitamente upcasting para el tipo base, para luego envolver en una nueva SmartPtr del tipo adecuado (tenga en cuenta que esto no es una fuga de recursos debido a que nuestro cultivados en casa SmartPtr clase utiliza intrusivo de conteo de referencias).Que es largo y complicado, especialmente cuando se necesita para envolver el SmartPtr en todavía otro objeto...los accesos directos?

¿Fue útil?

Solución

Tanto el constructor de copia como el operador de asignación deberían poder tomar un SmartPtr de un tipo diferente e intentar copiar el puntero de uno a otro. Si los tipos no son compatibles, el compilador se quejará, y si son compatibles, habrá resuelto su problema. Algo como esto:

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

Otros consejos

SmartPtr<Base> y SmartPtr<Derived> son dos distintas instancias de la SmartPtr de la plantilla.Estas nuevas clases no compartir la herencia que Base y Derived hacer.Por lo tanto, su problema.

¿cuál es la mejor forma de realizar esta operación de asignación?

 SmartPtr<Base> b = d; 

No invoca operador de asignación.Esto invoca el copia-cto r (la copia se elide en la mayoría de los casos) y es exactamente como si usted escribió:

 SmartPtr<Base> b(d); 

Proporcionar una copia-cto r que toma un SmartPtr<OtherType> y ponerlo en práctica.Lo mismo va para el operador de asignación.Usted tendrá que escribir la copia-cto r y op= teniendo en cuenta la semántica de SmartPtr.

Las plantillas no son covariantes, y eso es bueno; imagine lo que sucedería en el siguiente caso:

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!

Para lograr lo que está intentando hacer, la clase SmartPointer debe tener un constructor con plantilla, que tome otro SmartPointer o un puntero de otro tipo. Podría echar un vistazo a boost :: shared_ptr, que hace exactamente eso.

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

Depende de la clase SmartPtr. Si tiene un constructor de copia (o en su caso, un operador de asignación) que toma SmartPtr<T>, donde T es el tipo con el que fue construido, entonces no funcionará, porque SmartPtr<T1> no está relacionado con SmartPtr<T2> incluso si T1 y T2 están relacionados por herencia.

Sin embargo, si SmartPtr tiene un constructor / operador de copia templatizado , con el parámetro de plantilla TOther, que acepta SmartPtr<TOther>, entonces debería funcionar.

Suponiendo que tiene el control de la clase SmartPtr, la solución es proporcionar un constructor con plantilla:

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 los tipos T y O son compatibles, funcionará, si no lo son, obtendrá un error de compilación.

Creo que lo más fácil es proporcionar una conversión automática a otro SmartPtr de acuerdo con lo siguiente:

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

Tenga en cuenta que esta implementación es robusta en el sentido de que la plantilla del operador de conversión no necesita conocer la semántica del puntero inteligente, por lo que no es necesario replicar el recuento de referencias, etc.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top