2009-08-31 16 views
67

Actualización: el shared_ptr en este ejemplo es como el de Boost, pero no es compatible con shared_polymorphic_downcast (o dynamic_pointer_cast o static_pointer_cast para el caso).Downcasting shared_ptr <Base> to shared_ptr <Derived>?

estoy tratando de inicializar un puntero compartida a una clase derivada sin perder la cuenta de referencia:

struct Base { }; 
struct Derived : public Base { }; 
shared_ptr<Base> base(new Base()); 
shared_ptr<Derived> derived; 

// error: invalid conversion from 'Base* const' to 'Derived*' 
derived = base; 

Hasta ahora, todo bien. No esperaba que C++ implícitamente convirtiera Base * en Derived *. Sin embargo, sí quiero la funcionalidad expresada por el código (es decir, mantener el recuento de referencias mientras se baja el puntero base). Mi primera idea era proporcionar un operador de conversión de la base de modo que una conversión implícita a Derivado podría tener lugar (por pedantes: Me gustaría comprobar que el reparto inicial es válida, no se preocupe):

struct Base { 
    operator Derived*(); 
} 
// ... 
Base::operator Derived*() { 
    return down_cast<Derived*>(this); 
} 

Bueno, no ayudó. Parece que el compilador ignoró por completo a mi operador de typecast. ¿Alguna idea de cómo podría hacer que la asignación shared_ptr funcione? Para puntos extra: ¿qué tipo de tipo es Base* const? const Base* Entiendo, pero Base* const? ¿A qué se refiere const en este caso?

+0

¿Por qué necesita un shared_ptr , en lugar de shared_ptr ? – Bill

+2

Porque quiero acceder a la funcionalidad en Derivados que no están en Base, sin clonar el objeto (quiero un único objeto al que hagan referencia dos punteros compartidos). Por cierto, ¿por qué no funcionan los operadores de elenco? –

Respuesta

45

Supongo que está usando boost::shared_ptr ... Creo que quiere dynamic_pointer_cast o shared_polymorphic_downcast.

Sin embargo, estos requieren tipos polimórficos.

tipo de tipo Base* const es? const Base* Entiendo, pero Base* const? ¿A qué se refiere const en este caso?

  • const Base * es un puntero a una constante mutable Base.
  • Base const * es un puntero mutable a una constante Base.
  • Base * const es un puntero constante a un Base mutable.
  • Base const * const es un puntero constante a una constante Base.

Aquí está un ejemplo mínimo:

struct Base { virtual ~Base() { } }; // dynamic casts require polymorphic types 
struct Derived : public Base { }; 

boost::shared_ptr<Base> base(new Base()); 
boost::shared_ptr<Derived> derived; 
derived = boost::static_pointer_cast<Derived>(base); 
derived = boost::dynamic_pointer_cast<Derived>(base); 
derived = boost::shared_polymorphic_downcast<Derived>(base); 

No estoy seguro si fue intencional que su ejemplo se crea una instancia del tipo de base y la proyecta, pero sirve para ilustrar la diferencia muy bien.

El static_pointer_cast "simplemente lo hará". Esto dará como resultado un comportamiento indefinido (un Derived* apuntando a la memoria asignada e inicializada por Base) y probablemente cause un bloqueo, o algo peor. El recuento de referencias en base se incrementará.

El dynamic_pointer_cast dará como resultado un puntero nulo. El recuento de referencia en base no se modificará.

shared_polymorphic_downcast tendrá el mismo resultado que un molde estático, pero activará una afirmación, más bien parece tener éxito y conduce a un comportamiento indefinido. El recuento de referencias en base se incrementará.

Ver (dead link):

A veces es un poco difícil de decidir si usar o static_castdynamic_cast, y usted desea que usted podría tener un poco de ambos mundos. Es bien sabido que dynamic_cast tiene una sobrecarga de tiempo de ejecución, pero es más seguro, mientras que static_cast no tiene ninguna sobrecarga, pero puede fallar silenciosamente. Qué bueno sería si pudiera usar shared_dynamic_cast en compilaciones de depuración y shared_static_cast en compilaciones de lanzamiento. Bueno, tal cosa ya está disponible y se llama shared_polymorphic_downcast.

+0

Lamentablemente, su solución depende de la funcionalidad de Boost que se excluyó deliberadamente de la implementación particular de shared_ptr que estamos utilizando (no pregunte por qué). En cuanto a la explicación const, tiene mucho más sentido ahora. –

+3

A falta de implementar los otros constructores 'shared_ptr' (tomando' static_cast_tag' y 'dynamic_cast_tag'), no hay mucho que puedas hacer. Cualquier cosa que hagas fuera 'shared_ptr' no podrá administrar el recuento. - En un diseño de OO "perfecto", siempre puede usar el tipo de base, y nunca necesita saber ni importar cuál es el tipo derivado, ya que toda su funcionalidad está expuesta a través de interfaces de clase base. Tal vez solo necesites volver a pensar por qué tienes que lanzar hacia abajo en primer lugar. –

+1

@Tim Sylvester: ¡pero, C++ no es un lenguaje de OO "perfecto"! :-) down-cast tiene su lugar en un lenguaje OO no perfecto –

55

Puede usar dynamic_pointer_cast. Es compatible con std::shared_ptr.

std::shared_ptr<Base> base (new Derived()); 
std::shared_ptr<Derived> derived = 
       std::dynamic_pointer_cast<Derived> (base); 

Además, no recomiendo utilizar el operador de conversión en la clase base. La conversión implícita como esta puede convertirse en la fuente de errores y errores.

-Update: Si el tipo no es polimórfico, se puede usar std::static_pointer_cast.

+3

No entendí desde la primera línea que no está usando 'std :: shared_ptr'. Pero de los comentarios de la primera respuesta deduje que no está usando boost, por lo que puede estar usando 'std :: shared_ptr'. –

+0

OK. Lo siento. Debería aclarar mejor que está usando una implementación personalizada. –

3

Si alguien llega aquí con impulso :: shared_ptr ...

Así es como se puede abatido a la shared_ptr derivada Boost. Suponiendo que Derived hereda de Base.

boost::shared_ptr<Base> bS; 
bS.reset(new Derived()); 

boost::shared_ptr<Derived> dS = boost::dynamic_pointer_cast<Derived,Base>(bS); 
std::cout << "DerivedSPtr is: " << std::boolalpha << (dS.get() != 0) << std::endl; 

Asegúrese de 'base' de clase/estructura tiene al menos una función virtual. Un destructor virtual también funciona.

Cuestiones relacionadas