2012-04-09 16 views
18

In C++, quando un oggetto è definito come "fuori ambito"?Quando è un oggetto "fuori ambito"?

In particolare, se avessi un elenco collegato singolarmente, cosa definirebbe un oggetto nodo elenco singolo come "fuori ambito"? Oppure se un oggetto esiste e viene referenziato da una variabile 'ptr', è corretto dire che l'oggetto è definito come "fuori ambito" nel momento in cui il riferimento viene cancellato o punta a un oggetto diverso?

AGGIORNAMENTO: Assumendo che un oggetto sia una classe che ha un distruttore implementato. Il distruttore sarà chiamato nel momento in cui l'oggetto esce dall'oscilloscopio?

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; 
} 

In altre parole, sarebbe il nodo essendo puntato da list_1 chiamare suo distruttore dopo questa riga:

Node * list_1 = new Node (3);

?

+1

vostre domande aggiornamento è molto buona, e la risposta è no. Quando un puntatore esce dall'ambito, il distruttore dell'oggetto a cui sta puntando è ** non ** chiamato automaticamente. Devi chiamarlo tu stesso, e questo succede quando chiami 'delete' sul nodo per liberare la memoria. Questa è una buona cosa da tenere a mente quando si ha una matrice o un elenco di puntatori ad oggetti con implementazioni significative del distruttore. Fortunatamente, dato che devi comunque "liberare" te stesso, innescherai i distruttori. –

+0

A proposito, se effettivamente hai bisogno di un elenco collegato per qualcosa, stai molto meglio usando std :: list rispetto al tuo elenco personalizzato collegato. – DSimon

+1

Avrei dovuto dire 'delete' invece di' free'. Ignora 'gratis', è un retaggio di C. Usa solo 'new' e' delete' per l'allocazione/eliminazione dell'heap. –

risposta

41

In primo luogo, ricordare che gli oggetti in C++ possono essere creato nello stack o nell'heap.

Uno stack frame (o scope) è definito da un'istruzione. Può essere grande quanto una funzione o piccolo come un blocco di controllo del flusso (while/if/for ecc.). Una coppia arbitraria {} che racchiude un blocco arbitrario di codice costituisce anche una cornice di stack. Qualsiasi variabile locale definita all'interno di un frame andrà fuori campo una volta che il programma sarà uscito da quel frame. Quando una variabile stack esce dall'ambito, viene chiamato il suo distruttore.

ecco un classico esempio di uno stack frame (a esecuzione di una funzione) e una variabile locale dichiarata all'interno di esso, che uscirà di portata, una volta uscite pila telaio - una volta che la funzione 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. 

Ecco un esempio in cui si vede uno stack frame essendo appena il corpo di un if dichiarazione:

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

l'unico modo per un oggetto stack creato a "rimangono in scope" dopo il telaio si esce è se è il valore di ritorno di una funzione. Ma questo non è veramente "rimanendo nell'ambito" perché l'oggetto è stato copiato. Quindi l'originale va fuori portata, ma viene fatta una copia. Esempio:

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. 

Ora, un oggetto può anche essere dichiarato nello heap. Per il gusto di questa discussione, pensa all'heap come a un amorfo frammento di memoria. A differenza dello stack, che assegna e deseleziona automaticamente la memoria necessaria mentre si entra e si esce dai frame dello stack, è necessario riservare manualmente e liberare memoria heap.

Un oggetto dichiarato nell'heap, dopo una moda, "sopravvive" tra i frame dello stack. Si potrebbe dire che un oggetto dichiarato sull'heap non va mai fuori portata, ma questo è dovuto al fatto che l'oggetto non è mai realmente associato ad alcun ambito. Tale oggetto deve essere creato tramite la parola chiave new e deve essere definito da un puntatore.

È responsabilità dell'utente liberare l'oggetto heap una volta terminato. Si liberano oggetti heap con la parola chiave delete. Il distruttore su un oggetto heap non viene chiamato finché non si libera l'oggetto.

I puntatori che fanno riferimento a oggetti heap sono di solito variabili locali associate agli ambiti. Una volta terminato di utilizzare l'oggetto heap, si consente ai puntatori che fanno riferimento ad esso di uscire dall'ambito. Se non si è liberato in modo esplicito l'oggetto puntato dal puntatore, il blocco della memoria heap non verrà mai liberato fino alla chiusura del processo (chiamata perdita di memoria).

Pensa a tutto questo: un oggetto creato in pila è come un palloncino attaccato a una sedia in una stanza. Quando esci dalla stanza, il fumetto si apre automaticamente. Un oggetto creato sull'heap è come un palloncino su un nastro, legato a una sedia in una stanza. Il nastro è il puntatore. Quando esci dalla stanza, il nastro scompare automaticamente, ma il pallone galleggia sul soffitto e occupa spazio. La procedura corretta è far scoppiare il palloncino con uno spillo, quindi uscire dalla stanza, dopodiché il nastro scomparirà.Ma, la cosa buona del palloncino sulla corda è che puoi anche slegare il nastro, tenerlo in mano e uscire dalla stanza e portare il palloncino con te.

Quindi, per andare all'esempio dell'elenco collegato: in genere, i nodi di tale elenco vengono dichiarati nell'heap, con ogni nodo che contiene un puntatore al nodo successivo. Tutto ciò è in bilico e non va mai fuori portata. L'unica cosa che potrebbe uscire dall'ambito è il puntatore che punta alla radice della lista - il puntatore che usi per fare riferimento alla lista in primo luogo. Questo può andare fuori portata.

Ecco un esempio di creazione di roba sul mucchio, e il puntatore radice uscire di portata:

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

Grazie per l'input. Quindi la mia ultima domanda sarebbe che (se list_1 avesse un distruttore) il distruttore sarebbe chiamato non appena ... list_1-> next = list_2; ... viene eseguito? –

+0

Questo è leggermente diverso dal tuo altro follow-up, quindi risponderò ad entrambi. La risposta è no, perché tutto quello che stai facendo qui è impostare il campo 'next' per puntare a un nodo allocato - nulla è andato fuori dal campo di applicazione. Ma anche se qualcosa andasse al di fuori dell'ambito, non verrà chiamato nessun distruttore, perché quel qualcosa sarebbe un puntatore (si veda il commento seguito alla tua domanda principale). –

+0

Nota, a proposito, che un oggetto creato nello stack ** sarà ** ha chiamato il suo distruttore quando esce dal campo di applicazione. –

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

Quando si lascia l'ambito che è stato dichiarato in :)

tua domanda così com'è non è responsabile senza vedere l'implementazione. Arriva al punto in cui dichiari questo nodo.

void Foo() 
{ 
    int i = 10; 

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

} // i is out of scope 
2

"Fuori campo" è una metonimia: come in, utilizzando il nome o la terminologia di un concetto per parlare di qualcosa di strettamente correlati ma diversi.

In C++ un ambito è una regione statica di testo del programma e quindi qualcosa "fuori ambito", preso alla lettera, significa fisicamente al di fuori di una regione di testo. Ad esempio, { int x; } int y;: la dichiarazione di non rientra nell'ambito in cui è visibile x.

La metonimia "going out of scope" viene utilizzata per esprimere l'idea che l'attivazione/istanza dinamica dell'ambiente associato a un determinato ambito si sta esaurendo. E così le variabili definite in quell'ambito vanno via (quindi "fuori dal campo").

Ciò che è effettivamente andato "fuori dal campo" è il puntatore dell'istruzione, per così dire; la valutazione del programma si sta ora svolgendo in un ambito che non ha visibilità per quello. Ma non tutto in un ambito va via! Le variabili statiche saranno ancora presenti al prossimo inserimento dell'oscilloscopio.

"Uscire dal campo" non è molto preciso, ma breve e tutti capiscono cosa significa.

2

Un oggetto dichiarato all'interno di una funzione (o all'interno di determinati costrutti racchiusi tra parentesi graffe all'interno delle funzioni) non rientra nell'ambito di applicazione quando l'esecuzione lascia quella parte di codice.

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

questo vale solo per roba dichiarato in pila, quindi ha poco a che fare con le liste singolarmente-collegate, dal momento che i nodi della lista in genere essere istanziati sul mucchio con new.

In questo esempio, il puntatore restituito dal new andrà fuori del campo di applicazione, quando la funzione termina, ma niente accadrà al nodo stesso:

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

Se la classe Node ha implementato un distruttore, il nodo a cui fa riferimento p chiama il distruttore nel momento in cui p esce dall'ambito? –

+0

No. Nel mio primo esempio, però, lo std :: string destructor * sarà chiamato quando x lascia scope. Nel secondo esempio, ciò che lascia l'ambito non è un nodo ma un nodo * e i puntatori stessi non hanno distruttori. – DSimon

+0

ma se invece di un puntatore raw hai usato 'std :: unique_ptr p = new Node;' il distruttore dell'oggetto Node verrebbe richiamato all'uscita, quando 'p' esce dall'ambito .. – d7samurai

Problemi correlati