2010-06-22 17 views
14

Si interpreto las referencias de C++ correctamente, son como punteros, pero con integridad de datos garantizada (no NULL, no (int *) 0x12345). Pero, ¿qué ocurre cuando se abandona el alcance del objeto al que se hace referencia? Si no hay magia involucrada (y probablemente no lo sea), el objeto referenciado se destruirá a mis espaldas.¿Qué sucede cuando la referencia C++ deja su alcance?

me escribió una pieza de código para comprobar esto:

#include <iostream> 
using namespace std; 

class A { 
public: 
     A(int k) { _k = k; }; 
     int get() { return _k; }; 
     int _k; 
}; 

class B { 
public: 
     B(A& a) : _a(a) {} 
     void b() { cout << _a.get(); } 
     A& _a; 
}; 

B* f() { 
     A a(10); 
     return new B(a); 
} 

int main() { 
     f()->b(); 
} 

La variable _k ejemplo, se puso en la pila para comprobar la existencia de trama.

Sorprendentemente, no segfault y en su lugar imprime '10' correctamente, mientras que supongo que A se asigna en la pila y que el marco de pila de f() se sobrescribirán con al menos cout<< llamada.

+5

C++ no tiene "enlaces", ¿tal vez quiere decir "referencias"? –

+3

* "aunque supongo que A se asigna en la pila y que el marco de la pila de f() se sobrescribirá al menos cout << call" * - parece darse cuenta de que esto dará un comportamiento indefinido, entonces ¿por qué siquiera preguntar? –

+0

@Evan: seguro, solo el problema de traducción. – whitequark

Respuesta

22

esto es un comportamiento indefinido y usted simplemente era afortunado de que la memoria para a no se haya usado para nada más todavía. En un escenario más complejo, casi con seguridad obtendrás basura. En mi máquina consigo basura al azar con este código. Para mí, esto es probable porque estoy usando una máquina de 64 bits que usa una convención de llamadas de registro. Los registros se vuelven a utilizar mucho más a menudo que la memoria principal (idealmente ...).

Para responder a su pregunta de "qué pasa". Bueno en este escenario, la referencia es poco más que un puntero limitado con una sintaxis más amigable :-). Debajo del capó se almacena la dirección de a. Más tarde, el objeto a sale del alcance, pero la referencia del objeto B a ese a no se actualizará "automáticamente" para reflejar esto. Por lo tanto, ahora tiene una referencia inválida.

El uso de esta referencia no válida producirá casi cualquier cosa, a veces fallos, a veces solo datos de literas.


EDIT: Gracias a de todo género, he estado pensando en esto. Hay una regla en C++ que básicamente dice que si tienes una referencia constante a una temporal, entonces la vida del temporal es al menos tan larga como la referencia constante. Lo cual introdujo una nueva pregunta.

EDIT: Se traslada a la pregunta separada para los interesados ​​(const reference to temporary oddity)

+0

Estaba casi seguro de eso, pero me preguntó por si C++ tiene un contador de referencia oculto en algún lugar;) Gracias de todos modos. – whitequark

+0

Para abordar esto claramente: C++ no tiene recuento de referencias incorporado ** en cualquier lugar **. Obtienes exactamente lo que pides y nada más. Sin embargo, existen bibliotecas que implementan el recuento de referencias (como 'boost :: shared_ptr' o incluso' std :: tr1 :: shared_ptr'). –

+3

@whitequark: el tipo de compilador hace el recuento de referencias en tiempo de compilación en algunos casos limitados. Si haces algo como 'A & x = A (10);' tienes garantizado que el objeto 'A' al que 'x' hace referencia no se destruirá hasta que' x' salga del alcance. Eso solo funciona si 'x' tiene alcance de bloque. No funcionará si 'x' es una variable miembro o (creo) una variable global o estática. – Omnifarious

6

En el lenguaje C++ el tiempo de vida del objeto de la referencia está obligado a no está ligada en forma alguna a la vida útil de la propia referencia, con solo una excepción (ver a continuación).

Si el objeto se destruye antes que la referencia, la referencia se vuelve inválida (como un puntero colgante). Cualquier intento de acceder a esa referencia da como resultado un comportamiento indefinido. Esto es lo que sucede en tu ejemplo.

La referencia finaliza su vida útil antes que el objeto, entonces ... bueno, no pasa nada. La referencia desaparece, el objeto continúa vivo.

La única excepción a la que me refería anteriormente es cuando se inicializa una referencia const con un objeto temporal inmediato. En este caso, la vida útil del objeto temporal queda vinculada a la duración de la referencia. Cuando la referencia muere, el objeto muere con ella

{ 
    const std::string& str = "Hello!"; 
    // A temporary `std::string` object is created here... 
    ... 
    // ... and it lives as long as the reference lives 
    ... 
} // ... and it dies here, together with the reference 

P.S.Está utilizando el término ámbito incorrectamente. El alcance es la región de visibilidad para un identificador. El alcance por sí mismo no tiene nada que ver con la duración del objeto. Cuando algo "deja alcance" en el caso general de que algo no se destruye. Es un concepto erróneo popular utilizar la redacción de "hojas de alcance" para referirse al punto de destrucción de un objeto.

+1

Gracias por señalar el mal uso _scope_ - a veces tratando de mapear la terminología en un idioma a otro produce resultados muy extraños - pero toda esta pregunta es sobre cosas que suceden cuando el objeto asignado a la pila se destruye cuando su identificador queda fuera del alcance. (Sí, la memoria solo se libera en 'leave', pero se debe llamar al destructor de todos modos, por eso se destruye). – whitequark

+0

¿Incluye la excepción esta situación: const T & f() {T t; return t;} const T & tt = f(); usar tt aquí? Gracias – jean

+0

@jean: No, no es así. – AnT

Cuestiones relacionadas