2011-09-05 13 views
8

He buscado en Internet y este hilo en busca de una respuesta completa de esta situación que estoy enfrentando. He leído que arrojar punteros inteligentes a los objetos no es muy inteligente. Solo quiero entender por qué está sucediendo esto. Explicaré la situación. Imaginemos esta jerarquía simple:Punteros inteligentes y manejo de excepciones

class Foo 
{ 
public: virtual ~Foo() {} 
}; 

typedef tr1::shared_ptr<Foo> SPFoo; 

class FooInherited: public Foo { }; 

typedef tr1::shared_ptr<FooInherited> SPFooInherited; 

Y vamos a ver este código de prueba:

int main(int argc, char** argv) 
{ 
    try 
    { 
    throw FooInherited(); 
    } 
    catch(const Foo& f) 
    { 
    cout << "Foo& caught!" << endl; 
    } 
    try 
    { 
    throw SPFooInherited(new FooInherited()); 
    } 
    catch(const SPFoo& f) 
    { 
    cout << "SPFoo& caught!" << endl; 
    } 
    return 0; 
} 

Todo compila pero en tiempo de ejecución el segundo try-catch no será ejecutados. ¿Puede alguien explicarme por qué? Especialmente si las líneas de código como funcionan perfectamente bien en tiempo de ejecución.

void function(const SPFoo& f) 
{ 
} 

... 

SPFooInherited fi(new FooInherited()); 
function(fi); 

entiendo que el problema es que SPFooInherited no hereda de SPFoo (a pesar de que FooInherited hereda de Foo), pero profundamente nos gustaría saber lo que es el compilador/RTE haciendo de manera diferente a partir del ejemplo llamada a la función al capturar una excepción no ser capaz de resolver la situación. ¿Es porque el parámetro de captura no es lo mismo que un parámetro de llamada de función? ¿Por qué funciona Foo & y SPFoo no?

Muchas gracias de antemano.

Saludos, Iker.

Respuesta

13

Como dijo en su pregunta, SPFooInherited no es una subclase de SPFoo. Esto significa que catch(SPFoo const&) no detectará instancias de SPFooInherited. Por otro lado, FooInherited se hereda de Foo, por lo que catch(Foo const&) detectará instancias de FooInherited.

Para entender esto, no necesita ninguna comprensión especial del compilador o del entorno de tiempo de ejecución. Es simplemente una parte del lenguaje.

El motivo por el que funciona la llamada a la función es que tr1::shared_ptr tiene un constructor no explícito con plantilla que permite que se realice una conversión implícita en los sitios de llamadas de función.

Es decir: tr1::shared_ptr tiene el siguiente constructor:

//Note the lack of explicit 
template<class Y> shared_ptr(shared_ptr<Y> const & r); 

Esto permite que un shared_ptr que se construye a partir de un tipo de puntero compartida diferente. La implementación de este constructor se basa en la conversión implícita de FooInherited* a Foo* para almacenar realmente el puntero en el SPFooInherited en el SPFoo. Si esta conversión implícita no existe, el código no se compilará y, por lo tanto, no se producirán conversiones inseguras entre shared_ptr y tipos no relacionados.

La diferencia fundamental entre la llamada a la función y el problema es que las conversiones implícitas se producirán en la inicialización de los argumentos de funciones, pero una captura sólo puede coincidir con un solo tipo (FooInherited is-a Foo, por lo que coincidirá).

3

Porque SPFoo no es una subclase de SPFooInherited.Los bloques catch solo captarán las cosas que están en su lista de capturas, o una clase secundaria pública de lo que está en su lista de capturas. FooInherited hace inhertit desde Foo, por lo que la captura de Foo también le permitirá tomar un FooInherited. SPFoo y SPFooInherited son clases completamente diferentes y no relacionadas.