2012-04-09 27 views
18

En C++, ¿cuándo se define un objeto como "fuera de alcance"?¿Cuándo un objeto está "fuera del alcance"?

Más específicamente, si tuviera una lista vinculada individualmente, ¿qué definiría un objeto de nodo de lista único como "fuera de alcance"? O si un objeto existe y está siendo referenciado por una variable 'ptr', ¿es correcto decir que el objeto se define como "fuera del alcance" en el momento en que se borra la referencia o apunta a un objeto diferente?

ACTUALIZACIÓN: Suponiendo que un objeto es una clase que tiene un destructor implementado. ¿Se llamará al destructor en el momento en que el objeto sale del alcance?

if (myCondition) { 
    Node* list_1 = new Node (3); 
    Node* list_2 = new Node (4); 
    Node* list_3 = new Node (5); 

    list_1->next = list_2; 
    list_2->next = list_3; 
    list_3->next = null; 
} 

En otras palabras, ¿el nodo que se está apuntado por list_1 llamar a su destructor después de esta línea:

Nodo * list_1 = new Nodo (3);

?

+1

Sus preguntas actualización es una muy buena, y la respuesta es, no. Cuando un puntero sale del alcance, el destructor del objeto al que apunta es ** ** llamado automáticamente. Tienes que llamarlo tú mismo, y esto sucede cuando llamas a 'delete' en el nodo para liberar la memoria. Esto es algo bueno a tener en cuenta cuando tiene una matriz o una lista de punteros a objetos con implementaciones de destructor significativas. Afortunadamente, ya que tienes que 'libre' estos tú mismo de todos modos, estarás activando los destructores. –

+0

Por cierto, si realmente necesita una lista vinculada para algo, es mucho mejor usar std :: list que su propia lista vinculada personalizada. – DSimon

+1

Debería haber dicho 'eliminar' en lugar de' libre'. Ignore 'free', es C legacy. Use solo 'new' y' delete' para la asignación/eliminación de heap. –

Respuesta

41

En primer lugar, recordar que los objetos en C++ pueden ser creado ya sea en la pila o en en el montón.

Un marco de pila (o ámbito) se define mediante una instrucción. Puede ser tan grande como una función o tan pequeño como un bloque de control de flujo (while/if/for etc.). Un par arbitrario {} que encierra un bloque arbitrario de código también constituye un marco de pila. Cualquier variable local definida dentro de un marco saldrá del alcance una vez que el programa salga del marco. Cuando una variable de pila sale del alcance, se llama a su destructor.

Así que aquí es un ejemplo clásico de un marco de pila (una ejecución de una función) y una variable local declarada en él, lo que va a pasar de alcance una vez que las salidas marco de pila - una vez que la función termina:

void bigSideEffectGuy() { 
    BigHeavyObject b (200); 
    b.doSomeBigHeavyStuff(); 
} 
bigSideEffectGuy(); 
// a BigHeavyObject called b was created during the call, 
// and it went out of scope after the call finished. 
// The destructor ~BigHeavyObject() was called when that happened. 

Aquí se muestra un ejemplo en el que vemos un marco de pila de ser sólo el cuerpo de un comunicado if:

if (myCondition) { 
    Circle c (20); 
    c.draw(); 
} 
// c is now out of scope 
// The destructor ~Circle() has been called 

la única manera de que un objeto pila-creado para "permanecen en su alcance" después de la trama se sale es si es el valor de retorno de una función. Pero eso no es realmente "permanecer en el alcance" porque el objeto se está copiando. Entonces el original sale del alcance, pero se hace una copia. Ejemplo:

Circle myFunc() { 
    Circle c (20); 
    return c; 
} 
// The original c went out of scope. 
// But, the object was copied back to another 
// scope (the previous stack frame) as a return value. 
// No destructor was called. 

Ahora, un objeto también se puede declarar en el montón. Por el bien de esta discusión, piense en el montón como una mancha de memoria amorfa. A diferencia de la pila, que asigna y desasigna automáticamente la memoria necesaria a medida que ingresa y sale de los fotogramas de pila, debe reservar manualmente y liberar la memoria de pila.

Un objeto declarado en el montón hace, de algún modo, "sobrevivir" entre los marcos de la pila. Se podría decir que un objeto declarado en el montón nunca sale del alcance, pero eso es realmente porque el objeto nunca está realmente asociado con ningún ámbito. Tal objeto se debe crear a través de la palabra clave new, y se debe hacer referencia a él mediante un puntero.

Es su responsabilidad liberar el objeto Heap una vez que haya terminado con él. Libera objetos de montón con la palabra clave delete. El destructor en un objeto de montón no se llama hasta que libere el objeto.

Los punteros que hacen referencia a los objetos del montón son generalmente variables locales asociadas con los ámbitos. Una vez que haya terminado de usar el objeto Heap, permitirá que el puntero (s) que hace referencia a él se salga del alcance. Si no ha liberado explícitamente el objeto al que apunta el puntero, entonces el bloque de la memoria del montón nunca se liberará hasta que el proceso finalice (esto se conoce como pérdida de memoria).

Piénselo de esta manera: un objeto creado en la pila es como un globo pegado a una silla en una habitación. Cuando salgas de la habitación, el globo se abrirá automáticamente. Un objeto creado en el montón es como un globo en una cinta, atado a una silla en una habitación. La cinta es el puntero. Cuando sale de la habitación, la cinta desaparece automáticamente, pero el globo simplemente flota hacia el techo y ocupa espacio. El procedimiento correcto es hacer estallar el globo con un alfiler, y luego salir de la habitación, con lo cual la cinta desaparecerá.Pero, lo bueno del globo en la cuerda es que también puedes desatar la cinta, sostenerla en tu mano, salir de la habitación y llevar el globo contigo.

Para ir a su lista vinculada ejemplo: normalmente, los nodos de dicha lista se declaran en el montón, con cada nodo sosteniendo un puntero al siguiente nodo. Todo esto está sentado en el montón y nunca sale de su alcance. Lo único que podría salir del alcance es el puntero que apunta a la raíz de la lista, el puntero que utiliza para hacer referencia a la lista en primer lugar. Eso puede ir fuera del alcance.

He aquí un ejemplo de cómo crear cosas en la pila, y el puntero raíz de salir del ámbito de aplicación:

if (myCondition) { 
    Node* list_1 = new Node (3); 
    Node* list_2 = new Node (4); 
    Node* list_3 = new Node (5); 

    list_1->next = list_2; 
    list_2->next = list_3; 
    list_3->next = null; 
} 
// The list still exists 
// However list_1 just went out of scope 
// So the list is "marooned" as a memory leak 
+0

Gracias por la entrada. Así que mi última pregunta sería que (si list_1 tuviera un destructor) se llamaría al destructor tan pronto como ... list_1-> next = list_2; ... ¿es ejecutado? –

+0

Esto es ligeramente diferente de su otro seguimiento, así que responderé a ambos. La respuesta es no, porque todo lo que hace aquí es establecer el campo 'next' para apuntar a un nodo asignado; nada ha salido del alcance. Pero incluso si algo saliera del alcance, no se invocará ningún destructor, porque ese algo sería un puntero (vea el comentario a continuación de su pregunta principal). –

+0

Note, por cierto, que un objeto creado en la pila ** lo hará ** se llama a su destructor cuando sale del alcance. –

5
{ //scope is defined by the curly braces 
    std::vector<int> vec; 
} 
// vec is out of scope here! 
vec.push_back(15); 
1

Cuando sale el alcance que se declaró en :)

Su pregunta tal como está no debe asumir sin ver la aplicación. Todo se reduce a donde declaras este nodo.

void Foo() 
{ 
    int i = 10; 

    { 
     int j = 20; 
    } // j is out of scope 

} // i is out of scope 
2

"Fuera de alcance" es una metonimia: como en, usando el nombre o la terminología de un concepto para hablar de algo estrechamente relacionada pero diferente.

En C++, un ámbito es una región estática de texto de programa, por lo que algo "fuera de alcance", tomado literalmente, significa físicamente fuera de una región de texto. Por ejemplo, { int x; } int y;: la declaración de y está fuera del alcance en el que x es visible.

La metonimia "saliendo de alcance" se usa para expresar la idea de que la activación/instanciación dinámica del entorno asociado con algún ámbito está finalizando. Y entonces las variables definidas en ese ámbito se van a ir (por lo tanto, "fuera del alcance").

Lo que realmente ha salido "fuera del alcance" es el puntero de instrucción, por así decirlo; la evaluación del programa se está llevando a cabo en un ámbito que no tiene visibilidad para ese. ¡Pero no todo en un telescopio desaparece! Las variables estáticas seguirán allí la próxima vez que se ingrese el alcance.

"Salir de alcance" no es muy preciso, pero breve y todos entienden lo que significa.

2

Un objeto que se declara dentro de una función (o dentro de ciertas construcciones entre corchetes dentro de las funciones) queda fuera del alcance cuando la ejecución abandona esa parte del código.

void some_func() { 
    std::string x("Hello!"); 
    // x is in scope here 
} 
// But as soon as some_func returns, x is out of scope 

Esto sólo se aplica a la materia declarado en la pila, por lo que tiene poco que ver con listas enlazadas por enlaces sencillos, ya que los nodos de la lista normalmente se creará una instancia en el montón con new.

En este ejemplo, el puntero devuelto por new irá fuera de alcance cuando se cierra la función, pero nada va a pasar con el propio nodo:

void make_a_node() { 
    Node* p = new Node; 
} // Oh noes a memory leak! 
+0

Si la clase Node tenía un destructor implementado, ¿se hace referencia al nodo por p llamando al destructor en el momento en que p sale del alcance? –

+0

No. Sin embargo, en mi primer ejemplo, std :: string destructor * se invocará cuando x abandone el alcance. En el segundo ejemplo, lo que deja alcance no es un Nodo sino un Nodo *, y los punteros en sí mismos no tienen destructores. – DSimon

+0

pero si en lugar de un puntero sin formato utiliza 'std :: unique_ptr p = new Node;' se llamará al destructor del objeto Node al salir, cuando 'p' salga del alcance .. – d7samurai

Cuestiones relacionadas