2010-11-05 12 views
5

Mi pregunta es simple, ¿es seguro el siguiente código?listas de inicialización del constructor orden/asignación pregunta

struct Parent { 
    B* _a; 
    Parent(B* a) : _a(a) {} 
}; 

struct Child : public Parent { 
    B _b; 
    Child() : Parent(&_b), _b(2){}; 
}; 

int main() { 
    Child c; 
    return 0; 
} 

dos puntos más:

  • Estoy interesado en la parte de pasar una referencia a un objeto miembro de la matriz.
  • Por seguridad quiero decir que se asignará _b (y su dirección de memoria) y que este código funcionará independientemente de qué compilador use.

Gracias de antemano.

aclaración
por seguro que en realidad quería decir que la dirección de memoria era válido, puesto que ya sabía que no se ha inicializado. otros

notas
En mi código real que querían almacenar el objeto de tipo B como un puntero a su clase base A, así:

struct Parent { 
    A* _a; 
    Parent(A* a) : _a(a) {} 
}; 

struct Child : public Parent { 
    B _b; 
    Child() : Parent(&_b), _b(2){}; 
}; 

int main() { 
    Child c; 
    return 0; 
} 

Lo cual, si entiendo AndreyT responder correctamente , es ilegal. Creo que intentaré hacer esto de otra manera, ya que este enfoque era propenso a errores. (Podría olvidar que no podría usar ese puntero y hacer algo con él en mi próximo refactor).

+0

¿Qué son A y B? Porque está inicializando un miembro B * con A *. A deriva B? Además, ¿B tiene constructor, que toma 'int' o algo así? –

+1

Probablemente, el constructor 'Parent' debe ser' Parent (B * a) '? –

+0

@Kiril Kirov typo, en mi ejemplo inicial A era una superclase de B. Eso era para mostrar por qué lo necesitaría. –

Respuesta

8

En el sentido que usted describe, sí, es seguro: la memoria está asignada y está perfectamente bien pasar ese puntero al padre. La memoria para Child::_b es en realidad parte integral de la memoria de Child. No requiere ninguna "asignación" adicional explícita. En el momento en que se invoca el constructor Child::Child, la memoria obviamente ya está allí.

Sin embargo, la memoria a la que apunta el puntero solo se puede utilizar de varias maneras (el estándar describe en 3.8 lo que puede y lo que no se puede hacer), ya que el objeto al que apunta no se ha inicializado todavía. En su ejemplo específico, simplemente almacena el puntero. Eso está perfectamente bien.

Pero si, por ejemplo, quería convertir ese puntero a algún tipo de clase base (asumiendo por un segundo que B tiene cierta clase base), su código sería ilegal. Es ilegal convertir punteros a objetos no inicializados en sus punteros de clase base (de nuevo, consulte 3.8).

1

Primero se llamará al constructor padre y se le pasará la dirección de una variable miembro uniliado a ese constructor. No es seguro.

edición:

Creo AndreyT manifestó más claramente y con mayor intensidad el tipo de problemas que vino a la mente cuando escribí mi respuesta. Esos son los tipos de errores que no son inmediatamente perceptibles. Del tipo que te mantendrá despierto por la noche intentando averiguar dónde está el puntero colgando en tu código o dónde está ocurriendo la corrupción de la memoria.

+0

¿Esto no funcionará? diseño no ideal) siempre que la clase base no use el miembro derivado de la clase en su construcción. –

+0

Eso fue mi pensamiento inmediato; pero ¿está seguro de que no es seguro (como en "indefinido") simplemente pasar la * dirección * de una variable no inicializada? –

+3

Diría que es inseguro en cierto sentido que más tarde alguien podría tener la tentación de usar el puntero en el constructor de 'A' .No es obvio por el código de 'A' por qué esto no es legal. Dejar tales trampillas en el código de uno es inseguro. –

6

El orden de inicialización es el siguiente:

// Base classes: 
Parent(&_b) 

// Members: 
_b(2) 

// Constructor Body: 
Child() { } 

si esto es seguro depende de su definición de "seguro". Según su definición ("¿Funcionará?"), Sí, es seguro. La vida útil de Child::_b comienza cuando se crea el objeto Child, por lo que puede obtener un puntero y ese puntero se refiere a un objeto. Sin embargo, no puede usar el valor apuntado hasta que se haya inicializado _b, que es después de que el constructor de la clase base, Parent, haya regresado.

Cuestiones relacionadas