inyeccion - Inyección de Dependencia de C++: ¿Duración de los Objetos?
inyeccion de dependencias youtube (2)
La respuesta es: no es necesario que sepa, ya que su diseño está roto, de todos modos.
Primero, un Destroy
suena como una mala idea, además si se invoca en un objeto que no es responsable de la destrucción del otro objeto. El código del método Destroy
pertenece al destructor ApplicationService
(que es, con suerte, virtual, aunque en este caso no es necesario), que a diferencia de C # se llama en un momento perfectamente determinado.
Una vez que haya hecho esto, (con suerte) se dará cuenta de que no es responsabilidad de MyClass
destruir appService_
, ya que no es de su propiedad. Es la responsabilidad de ConsumerClass
(o mejor dicho, el método DoSomething
), que realmente administra el servicio real y que realmente lo destruye automáticamente una vez que ha movido el código de Destroy
al destructor. ¿No es agradable cómo RAII hace que suceda todo de una manera limpia y automática?
class MyClass
{
public:
MyClass(ApplicationService& app): appService_(app)
{
}
private:
ApplicationService& appService_;
}
class ConsumerClass
{
DoSomething()
{
CustomApplicationService customAppService;
MyClass myclass(customAppService);
myclass...
}
}
class ApplicationService
{
public:
virtual ~ApplicationService()
{
//code from former Destroy method
}
}
class CustomApplicationService
{
public:
virtual ~CustomApplicationService()
{
//code from former Destroy method
}
}
Esto es en mi humilde opinión la forma perfecta de limpieza en C ++ y definitivamente el problema no es una razón para shared_ptr
spam a shared_ptr
s. Incluso si realmente necesita un método Destroy
dedicado y no puede mover el código al destructor (lo que tomaría como una motivación para pensar demasiado el diseño), aún llamaría DoSomething
de DoSomething
como de nuevo, MyClass no es responsable de destruir la aplicaciónService_ .
EDITAR: de acuerdo con su actualización (y mi estúpida visión del argumento something
), su diseño parece bastante correcto (al menos si no puede meterse con el cambio de ApplicationService
), lo siento.
Aunque los miembros de la clase deberían ser destruidos en el orden inverso de la construcción, no estoy seguro de que esto también sea válido para las variables automáticas locales. Lo que podría hacer para asegurarse de que los destructores se llamen en un orden definido es introducir ámbitos anidados usando bloques simples:
void DoSomething()
{
CustomApplicationService customAppService;
{
MyClass myclass(customAppService);
myclass...
} // myclass destroyed
} // customAppService destroyed
Por supuesto, todavía no es necesario utilizar la asignación dinámica, deje de lado shared_ptr
s. Aunque los bloques anidados hacen volar el código un poco, no es nada contra la fealdad de la asignación dinámica aplicada de una manera no dinámica y sin razón, y al menos "se ve bien de una manera semántica" con la declaración de customAppService
en la parte superior del bloquear;)
Vengo de C # y trato de traducir algunas de mis prácticas en C ++. He usado la inyección de dependencia en varios lugares a lo largo de mi código usando punteros crudos. Luego decido reemplazar los punteros sin procesar con std :: shared_ptr''s. Como parte de ese proceso, se sugirió que considere usar las variables automáticas asignadas a la pila en lugar de asignarlas dinámicamente (consulte esta pregunta, aunque esa pregunta se realizó en el contexto de unique_ptr, por lo que tal vez sea diferente).
Creo que el siguiente ejemplo muestra el uso de variables automáticas.
class MyClass
{
public:
MyClass(ApplicationService& app): appService_(app)
{
}
~MyClass()
{
appService_.Destroy(something);
}
private:
ApplicationService& appService_;
}
class ConsumerClass
{
DoSomething()
{
CustomApplicationService customAppService;
MyClass myclass(customAppService);
myclass...
}
}
En el ejemplo anterior, cuando customAppservice y myclass quedan fuera del alcance, ¿cómo sé cuál será el primero en ser destruido? Si customAppService se destruye antes que el destructor MyClass fallará. ¿Es esta una buena razón para usar shared_ptr en este escenario o hay una forma limpia de solucionarlo?
ACTUALIZAR
ApplicationService es una clase que es un contenedor de las funciones globales necesarias para interactuar con una biblioteca de terceros que utiliza mi código. Tengo esta clase, ya que creo que es la manera estándar de admitir las pruebas unitarias y el troquelado / burla de las funciones autónomas. Esta clase simplemente delega llamadas a las funciones globales correspondientes. La llamada appService_.Destroy (algo); en realidad está destruyendo un objeto utilizado por cada instancia específica de MyClass, sin destruir nada, con la clase Application.
En C ++, los objetos, en general, se destruyen en el orden que es exactamente opuesto al orden en que se crearon.
Según su ejemplo, MyClass
se destruirá antes de CustomApplicationService
La excepción es cuando se llama explícitamente a un destructor. Sin embargo, no creo que deba preocuparse por esta excepción en esta etapa.
Otra sutilidad se llama fiasco de orden de inicialización estática . Sin embargo, esto no se aplica a las variables automáticas (pila).
Editar:
Desde C ++ 2003: buscó ''orden inverso''
6.6.0.2
On exit from a scope (however accomplished), destructors (12.4) are called for all
constructed objects with automatic storage duration (3.7.2) (named objects or
temporaries) that are declared in that scope, in the reverse order of their
declaration. ... [Note: However, the program can be terminated (by calling exit()
or abort()(18.3), for example) without destroying class objects with automatic
storage duration. ]