En mi lugar de trabajo veo que este estilo se usa ampliamente:
#include <iostream>
using namespace std;
class A
{
public:
A(int& thing) : m_thing(thing) {}
void printit() { cout << m_thing << endl; }
protected:
const int& m_thing; //usually would be more complex object
};
int main(int argc, char* argv[])
{
int myint = 5;
A myA(myint);
myA.printit();
return 0;
}
¿Hay un nombre para describir este idioma? ¿Supongo que es para evitar la sobrecarga posiblemente grande de copiar un objeto complejo grande?
¿Es esta una buena práctica en general? ¿Hay dificultades en este enfoque?
Respuestas:
En UML se llama agregación. Se diferencia de la composición en que el objeto miembro no es propiedad de la clase de referencia. En C ++ puede implementar la agregación de dos formas diferentes, mediante referencias o punteros.
No, esa sería una muy mala razón para usar esto. La razón principal de la agregación es que el objeto contenido no es propiedad del objeto contenedor y, por lo tanto, su vida útil no está limitada. En particular, la vida útil del objeto referenciado debe sobrevivir a la de referencia. Es posible que se haya creado mucho antes y que viva más allá del final de la vida útil del contenedor. Además de eso, el estado del objeto referenciado no está controlado por la clase, pero puede cambiar externamente. Si la referencia no lo es
const
, entonces la clase puede cambiar el estado de un objeto que vive fuera de él.Es una herramienta de diseño. En algunos casos será una buena idea, en otros no. El error más común es que la vida útil del objeto que contiene la referencia nunca debe exceder la vida útil del objeto referenciado. Si el objeto adjunto usa la referencia después de que se destruyó el objeto al que se hace referencia, tendrá un comportamiento indefinido. En general, es mejor preferir la composición a la agregación, pero si lo necesita, es una herramienta tan buena como cualquier otra.
fuente
to prevent the possibly large overhead of copying a big complex object?
Se llama inyección de dependencia mediante inyección de constructor : la clase
A
obtiene la dependencia como argumento para su constructor y guarda la referencia a la clase dependiente como una variable privada.Hay una interesante introducción en wikipedia .
Para una corrección constante , escribiría:
using T = int; class A { public: A(const T &thing) : m_thing(thing) {} // ... private: const T &m_thing; };
pero un problema con esta clase es que acepta referencias a objetos temporales:
T t; A a1{t}; // this is ok, but... A a2{T()}; // ... this is BAD.
Es mejor agregar (requiere C ++ 11 como mínimo):
class A { public: A(const T &thing) : m_thing(thing) {} A(const T &&) = delete; // prevents rvalue binding // ... private: const T &m_thing; };
De todos modos si cambia el constructor:class A { public: A(const T *thing) : m_thing(*thing) { assert(thing); } // ... private: const T &m_thing; };
está prácticamente garantizado que no tendrá un puntero a un archivo temporal .
Además, dado que el constructor toma un puntero, es más claro para los usuariosA
que deben prestar atención a la vida útil del objeto que pasan.Temas algo relacionados son:
fuente
No hay un nombre para este uso, simplemente se conoce como "Referencia como miembro de clase" .
Sí, y también escenarios en los que desea asociar la vida útil de un objeto con otro objeto.
Depende de su uso. Usar cualquier característica del idioma es como "elegir caballos para los cursos" . Es importante tener en cuenta que todas ( casi todas ) las características del idioma existen porque son útiles en algunos escenarios.
Hay algunos puntos importantes a tener en cuenta al usar referencias como miembros de la clase:
operator=()
y tendrá que proporcionar una usted mismo. Es complicado determinar qué acción=
tomará su operador en tal caso. Entonces, básicamente, su clase se vuelve no asignable .NULL
hacer referencias ni hacer referencia a ningún otro objeto. Si necesita volver a colocarlo, entonces no es posible con una referencia como en el caso de un puntero.Para la mayoría de los propósitos prácticos (a menos que esté realmente preocupado por el alto uso de memoria debido al tamaño del miembro), basta con tener una instancia de miembro, en lugar de un puntero o miembro de referencia. Esto le ahorra una gran cantidad de preocupaciones sobre otros problemas que traen consigo los miembros de referencia / puntero, aunque a costa del uso de memoria adicional.
Si debe utilizar un puntero, asegúrese de utilizar un puntero inteligente en lugar de un puntero sin formato. Eso haría tu vida mucho más fácil con los consejos.
fuente
C ++ proporciona un buen mecanismo para administrar el tiempo de vida de un objeto a través de construcciones de clase / estructura. Esta es una de las mejores características de C ++ sobre otros lenguajes.
Cuando tiene variables miembro expuestas a través de ref o puntero, viola la encapsulación en principio. Este modismo permite al consumidor de la clase cambiar el estado de un objeto de A sin que (A) tenga conocimiento o control del mismo. También permite al consumidor aferrarse a una referencia / puntero al estado interno de A, más allá de la vida útil del objeto de A. Este es un mal diseño. En su lugar, la clase podría refactorizarse para contener una referencia / puntero al objeto compartido (no poseerlo) y estos podrían establecerse usando el constructor (Exigir las reglas de tiempo de vida). La clase del objeto compartido puede diseñarse para admitir subprocesos múltiples / concurrencia, según corresponda.
fuente
Las referencias de miembros generalmente se consideran malas. Hacen la vida más difícil en comparación con los indicadores de los miembros. Pero no es particularmente inusual, ni es un modismo especial con nombre o algo. Es solo un alias.
fuente