2010-07-08 25 views
5

Tengo una clase A, que consta de los objetos B y C. ¿Cómo escribir un constructor de A que obtiene objetos B y C? ¿Debería pasarlos por valor, por referencia (const) o por un puntero? ¿Dónde debería desasignarlos?¿Cómo pasar parámetros a un constructor?

pensé en punteros, porque entonces podría escribir:

A a(new B(1,2,3,4,5), new C('x','y','z')) 

Pero no sé si es una buena práctica o no. ¿Alguna sugerencia?

+1

Ver [_Cómo pasar objetos a funciones en C++? _] (Http://stackoverflow.com/questions/2139224/how-to-pass-objects-to-functions-in-c/2139254#2139254) – sbi

Respuesta

7

Por lo general se pasa por referencia const:

A a(B(1,2,3,4,5), C('x','y','z')) 

No hay necesidad de punteros aquí.

Normalmente se almacenan los valores a menos que la copia sea demasiado ineficaz. La definición de clase lee a continuación:

class A { 
private: 
    B b; 
    C c; 
public: 
    A(const B& b, const C& c): b(b), c(c) { } 
}; 
2

Lo que se pasa en depende de sus necesidades.

¿Necesita una copia de lo que está pasando? Luego pasa por const-reference.

struct A 
{ 
    A(const B& b, const C& c) : m_b(b), m_c(c) {} 

private: 
    B m_b; 
    C m_c; 
}; 

Y construir así:

A myA(B(1,2,3), C(4,5,6)); 

Si desea que el objeto A para referirse a algunos otros B y C objetos (pero no poseerlas) a continuación, utilizar punteros (o posiblemente referencias).

+0

Quiero que los tenga, así que este no es el caso. Gracias de todos modos :) – mik01aj

+1

¿Tiene una necesidad de objetos dinámicamente asignados? De lo contrario ** este ** es tu caso. La muestra de código proporciona la solución más simple y natural en C++ cuando se trata de composición. –

+0

Me refería a la última oración. Voy a usar referencias de const, como la mayoría de las personas aquí sugirieron. – mik01aj

4

¿Debo pasarlos por valor, por referencia (const), o un puntero?

  1. Por referencia const si el objeto es grande
  2. por valor si el objeto es pequeño
  3. por puntero const si es un argumento opcional que puede ser cero (es decir, "NULL")
  4. por puntero si es un argumento opcional que puede ser cero pero será propiedad (es decir, desasignado) por clase construida.

Tenga en cuenta que si su clase tiene instancias internas de B y C, y luego pasarlos por referencia, el valor o referencia constante, lo más probable implicar el uso de constructor de copia o de operador de asignación. Lo cual no será necesario con punteros.

A bis (nuevo B (1,2,3,4,5), nuevo C ('x', 'y', 'z'))

Normalmente (es decir, no siempre) es una mala idea, porque:

  1. si A no desasigna los argumentos, tiene una pérdida de memoria.
  2. Si A toma posesión de los argumentos y los desasigna, no podrá pasar los valores asignados en la pila como argumentos.Sin embargo, dependiendo de su diseño de código de esto puede ser aceptable (Qt 4 con frecuencia toma la propiedad de los objetos creados con nueva)

Dónde debo ellos desasignar?

La mejor idea es asegurarse de que el compilador desasigna automáticamente los argumentos para usted. Esto significa pasar por referencia, referencia constante o por valor. O usando punteros inteligentes.

+0

Existen alternativas para pasar un puntero sin formato. P.ej. 'Boost.Optional' para el caso 3, y un puntero inteligente como' boost :: shared_ptr' para el caso 4. Entonces no tiene que ocuparse de la desasignación manual. – Philipp

+2

Y creo que es importante decir que si un constructor arroja una excepción tiene una pérdida de memoria porque nadie desasigna B y C. Otra razón para usar punteros inteligentes :) –

+1

@Philipp: ¿puede tener una referencia opcional? Yo usaría 'shared_ptr' para el caso 3 y' auto_ptr' para el caso 4 (a menos que sea en realidad una matriz, en cuyo caso se pueden aplicar otros punteros inteligentes). –

1

Editar: Los ejemplos dados aquí no respetan la regla de los Tres Grandes (gracias @Philipp!). Si la definición de A se utiliza como se indica a continuación, el código se bloqueará en la construcción de copia para A o en la asignación de A. Para definir correctamente el código, el operador de asignación y el constructor de copia deben definirse explícitamente para A (o explícitamente prohibido: declarado como privado y nunca implementado). (Fin Editar)

¿Debo pasar por valor, por referencia (const), o un puntero?

Si A utiliza B y C, y luego retenerlos por referencia o puntero dentro de A. Para elegir entre la referencia y el puntero, ver cómo B y C están asignados.

Si se trata de objetos de pila local construidos en el mismo ámbito que A, entonces páselos por const referencia.

Si son objetos que utiliza un asignan dinámicamente, hacer una propia ellos: pasar por ellos punteros, y tienen destructor de A a eliminar.

Si son componentes opcionales de A, los pasan por puntero (que puede ser nulo).

Si A no se hace responsable de eliminarlos, páselos por * const.

¿Dónde debo desasignarlos?

Generalmente, cuando ya no los necesita :).

Si se necesitan más allá del alcance de A (si son objetos externos que A usa), elimínelos cuando se completa el alcance de A.

Si son propiedad de A, elimínelos en el destructor para A. También puede tener sentido eliminarlos durante la vida útil de A, si los punteros deben cambiarse.

Aquí hay un ejemplo, donde B es un componente reemplazable inyectado en A (y propiedad de A) y C es un componente opcional propiedad de A (pero también inyectado en A).

("propiedad de" significa que A es responsable de eliminar los dos objetos)

class B; 
class C; 

class A 
{ 
    B* b; 
    C* c; 
public: 
    A(B* const bb, C* const cc = 0) // cc is optional 
    : b(bb), c(cc) 
    { 
    } 

    void resetB(B* const bb = 0) 
    { 
     delete b; 
     b = bb; 
    } 

    ~A() 
    { 
     resetB(); 
     delete c; 
    } 
}; 

{ 
    A a(new B, new C); 
    a.resetB(); // delete B 
    a.resetB(new B); // delete former B and set a new one 
} // both members of A are deleted 

Pero no sé si es una buena práctica o no. ¿Alguna sugerencia?

Todo depende de que realmente, pero se puede escribir A a(B(1, 2, 4), C(1, 2, 3)) tan fácil como A a(new B(1, 2, 4), new C(1,2,3)); (en el primer caso - el uno sin nueva - la A :: B y A :: c debe ser referencias o objetos/valores dentro la clase, y A no debería eliminarlos en absoluto).

La pregunta no debería ser si desea escribir la declaración con la asignación dinámica para B y C, pero si es necesario. La asignación dinámica es lenta y, si no la necesita, no debería hacerlo.

+1

Su ejemplo no se apega a la Regla de los Tres Grandes. Considere usar un puntero inteligente en lugar del puntero sin formato. – Philipp

+0

@Philipp - Gracias, debería haber especificado eso. Mi ejemplo no es un ejemplo de código completo, solo de cómo pasar los parámetros. Agregaré un comentario a mi respuesta. – utnapistim

Cuestiones relacionadas