2009-09-07 17 views
8

¿Por qué el siguiente código se imprime "xxY"? ¿No deberían las variables locales vivir en el ámbito de toda la función? ¿Puedo usar tal comportamiento o esto será cambiado en el futuro estándar de C++?Pregunta de alcance de variable local

pensé que de acuerdo a C++ estándar 3.3.2 "Un nombre declarado en un bloque es local a ese bloque. Su alcance potencial comienza en su punto de declaración y termina al final de su región declarativa."

#include <iostream> 
using namespace std; 

class MyClass 
{ 
public: 
    MyClass(int) { cout << "x" << endl; }; 
    ~MyClass() { cout << "x" << endl; }; 
}; 

int main(int argc,char* argv[]) 
{ 
    MyClass (12345); 
// changing it to the following will change the behavior 
//MyClass m(12345); 
    cout << "Y" << endl; 

    return 0; 
} 

Basándose en las respuestas I puede suponer que MyClass(12345); es la expresión (y alcance). Eso tiene sentido. Así que espero que el siguiente código imprimirá "Xyx" Siempre:

MyClass (12345), cout << "Y" << endl; 

y se deja de hacer dicha sustitución:

// this much strings with explicit scope 
{ 
    boost::scoped_lock lock(my_mutex); 
    int x = some_func(); // should be protected in multi-threaded program 
} 
// mutex released here 

//  

// I can replace with the following one string: 
int x = boost::scoped_lock (my_mutex), some_func(); // still multi-thread safe 
// mutex released here 
+2

Tu pregunta contiene la respuesta: * Un nombre declarado ... *. No hay nombre! – quamrana

+0

En este ejemplo: MyClass (12345) es un molde de estilo de función, no una declaración. –

+0

Aún así, no hay ningún nombre para la instancia – artificialidiot

Respuesta

4

Usted citó el estándar correctamente. Quisiera hacer hincapié en:

Un nombredeclarada en un bloque es local a ese bloque. Su alcance potencial comienza en su punto de declaración y finaliza al final de su región declarativa.

No ha declarado ningún nombre, en realidad. Su línea de

MyClass (12345); 

ni siquiera contiene una declaración! Lo que contiene es una expresión que crea una instancia de MyClass, calcula la expresión (sin embargo, en este caso particular no hay nada que calcular), y arroja su resultado al void, y destruye los objetos creados allí.

Una cosa menos confuso sonaría como

call_a_function(MyClass(12345)); 

que lo encontró muchas veces y sé cómo funciona, ¿verdad?

+0

Las especificaciones también hablan sobre el alcance "potencial". No "garantizado". Entonces el compilador puede destruir el objeto antes si no se usa en el alcance. –

+4

"alcance potencial" tiene un significado preciso: es el alcance más las partes donde el nombre está oculto debido a una nueva declaración. Una implementación no puede destruir un objeto con anterioridad, incluso si ya no se usa (lo que rompería algún uso importante de RAII por cierto). – AProgrammer

8

En realidad estás creando un objeto sin mantenerlo en su alcance, por lo que se destruye justo después de que se crea. De ahí el comportamiento que estás experimentando.

No puede acceder al objeto creado, entonces, ¿por qué el compilador lo mantendrá?

16

El objeto creado en su

MyClass(12345); 

es un objeto temporal que sólo está viva en esa expresión;

MyClass m(12345); 

es un objeto que está activo para todo el bloque.

+0

¿No es * esa expresión * la función principal? –

+1

Eso me parece correcto. Otra cosa podría ser la optimización: incluso si utiliza el segundo método, el compilador podría optimizarlo al primero. – Anna

+0

@Anna, en el segundo caso, siempre imprimirá xYx. –

5

Para responder a sus otras preguntas. La siguiente es la invocación del operador de coma.Crea un MyClass temporal, que incluye llamar a su constructor. Luego evalúa la segunda expresión cout << "Y" << endl que imprimirá la Y. Luego, al final de la expresión completa, destruirá la temporal, que llamará a su destructor. Entonces tus expectativas eran correctas.

MyClass (12345), cout << "Y" << endl; 

Para el siguiente para el trabajo, se debe añadir entre paréntesis, debido a la coma tiene un significado predefinido en las declaraciones. Empezaría declarando una función some_func devolviendo un int y sin tomar ningún parámetro y asignaría el objeto scoped_lock al x. Usando paréntesis, dices que todo es una expresión de operador de coma único.

int x = (boost::scoped_lock (my_mutex), some_func()); // still multi-thread safe 

Cabe señalar que las dos líneas siguientes son equivalentes. El primero no crea un objeto temporal sin nombre usando my_mutex como el argumento del constructor, pero en su lugar, los paréntesis alrededor del nombre son redundantes. No dejes que la sintaxis te confunda.

boost::scoped_lock(my_mutex); 
boost::scoped_lock my_mutex; 

he visto mal uso de los términos del alcance y la duración.

  • Scope es donde se puede hacer referencia a un nombre sin calificar su nombre. Los nombres tienen ámbitos y los objetos heredan el alcance del nombre utilizado para definirlos (por lo tanto, a veces el Estándar dice "objeto local"). Un objeto temporal no tiene alcance, porque no tiene nombre. Del mismo modo, un objeto creado por new no tiene alcance. El alcance es una propiedad de tiempo de compilación. Este término se utiliza con frecuencia de manera incorrecta en el Estándar, ver this defect report, por lo que es bastante confuso encontrar un significado real.

  • Lifetime es una propiedad en tiempo de ejecución. Significa cuando el objeto está configurado y listo para usar. Para un objeto de tipo clase, la duración comienza cuando el constructor finaliza la ejecución y finaliza cuando el destructor comienza la ejecución. Lifetime a menudo se confunde con el alcance, aunque estas dos cosas son completamente diferentes.

    La vida útil de los temporales está definida con precisión. La mayoría de ellos finaliza la vida después de la evaluación de la expresión completa en la que están contenidos (como, el operador de coma de arriba, o una expresión de asignación). Los temporales pueden estar ligados a referencias de const que alargarán su tiempo de vida. Los objetos lanzados en excepciones también son temporarios, y su vida útil termina cuando ya no hay ningún controlador para ellos.