2012-09-17 15 views
5

Tengo algunos proyectos que usan boost::shared_ptr o std::shared_ptr extensamente (puedo convertir a cualquier implementación lo suficientemente pronto, si hay una buena respuesta a esta pregunta para uno, pero no para el otro). La implementación de Boost usa Boost.Assert para evitar el retorno en el caso de encontrar un puntero vacío (NULL) en operator* o operator-> en tiempo de ejecución; mientras que la implementación de libC++ parece carecer de cualquier control.Personalizar std :: shared_ptr o boost :: shared_ptr para lanzar una excepción en NULL deferencia

Si bien, por supuesto, la validez de un shared_ptr debe verificarse antes de su uso, una base de código de paradigma mixta grande me lleva a desear probar una variación de arrojar excepciones; ya que la mayor parte del código es relativamente sensible a las excepciones y, como mucho, fallará en un estado de alto nivel pero reanudable, en lugar de std::terminate() o segfault.

¿Cómo puedo personalizar mejor estos accesos mientras mantengo la robustez de shared_ptr? Parece que encapsular shared_ptr en un throwing_shared_ptr puede ser la mejor opción, pero soy cauteloso de romper la magia. ¿Es mejor que copie la fuente de Boost y simplemente cambie el ASSERT a una declaración apropiada de throw?


El nombre de tipo real que se utiliza en todas partes para el apropiado tipo smart_ptr<T> es un typedef expandido desde una macro; es decir, ForwardDeclarePtr(Class) amplía a algo como:

class Class; 
typedef boost::smart_ptr<Class> ClassPtr; 

Todo pasa, toma, o almacena un ClassPtr - para que pueda reemplazar el tipo subyacente con bastante libertad; y sospecho que esto alivia el posible problema de corte/ocultación.

Respuesta

5

Realmente no hay "magia" en std::shared_ptr<T> que se eliminaría si lo incluyese dentro de una clase personalizada que lanzaría una excepción al desreferenciar un puntero compartido NULL. Así que no veo por qué ese enfoque no funcionaría, siempre y cuando su nueva clase contenedora siga todas las semánticas del tipo std::shared_ptr<T>.

Por cierto, también podría tomar un enfoque ligeramente diferente, y eso es crear una clase contenedora que simplemente no permita que otros pasen los punteros al miembro de datos envuelto std::shared_ptr<T> en primer lugar. Básicamente, sería una clase que haría cumplir la expresión std::make_shared<T> en su constructor. No estoy seguro, basado en el funcionamiento de su código si esto es posible, pero es otra forma de eludir el problema utilizando un enfoque RAII en lugar de arrojar excepciones.

+0

Aceptando esta respuesta porque es un poco más fácil explicar el diseño, la seguridad y el uso de la encapsulación en los comentarios (las clases tienen miembros 'shared_ptr' todo el tiempo). @KevinBallard también es correcto, sin embargo. – rvalue

5

Solo subclase std::shared_ptr en throwing_shared_ptr, anule esos dos métodos, y haga que afirmen y llamen a std::shared_ptr's impl. Esto debería funcionar bien siempre que use throwing_shared_ptr en todas partes en lugar de dividirlo en std::shared_ptr.

+0

Incluso rebanar no será un problema catastrófico siempre que el tipo derivado no agregue ningún miembro de datos. –

+0

@MarkRansom: Correcto, pero eliminará la naturaleza del lanzamiento. Por supuesto, si su valor se escribe como 'std :: shared_ptr' entonces supongo que debe esperar eso de todos modos. –

+0

@KevinBallard Esto realmente puede ser el problema más grande que la pregunta; qué hacer si algo espera un simple 'shared_ptr' - hay al menos un caso de código de terceros que lo hace.Dudo que lanzar una excepción a través de dicha biblioteca sea * peor * que 'std :: terminate()' o 'SIGSEGV', pero merece una consideración seria. – rvalue

Cuestiones relacionadas