Kiedy obiekt jest "poza zasięgiem"?

W C++, kiedy obiekt jest zdefiniowany jako "poza zakresem"?

Dokładniej mówiąc, gdybym miał pojedynczo połączoną listę, co definiowałoby pojedynczy obiekt węzła listy jako "poza zakresem"? Lub jeśli obiekt istnieje i jest odwoływany przez zmienną ptr, czy poprawne jest stwierdzenie, że obiekt jest zdefiniowany jako "poza zakresem" w momencie, gdy odwołanie jest usuwane lub wskazuje na inny obiekt?

UPDATE: zakładając, że obiekt jest klasą, która ma zaimplementowany Destruktor. Wola Destruktor zostanie wywołany w momencie, gdy obiekt opuści lunetę?

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

Innymi słowy, czy węzeł wskazywany przez list_1 wywoła swój Destruktor po tej linii:

Node* list_1 = new Node (3);

?

Author: AAEM, 2012-04-09

5 answers

Po pierwsze, pamiętaj, że obiekty w C++ mogą być tworzone albo na stosie lub na na stosie.

Ramka stosu (lub zakres) jest zdefiniowana za pomocą instrukcji. Może być tak duży jak funkcja lub tak mały jak blok sterowania przepływem(while/if/for itd.). Dowolna para {} zamykająca dowolny blok kodu również stanowi ramkę stosu. Dowolna zmienna lokalna zdefiniowana w ramce wykracza poza zakres, gdy program opuści tę ramkę. Gdy zmienna stosu wychodzi poza zasięg, jego Destruktor jest nazywany.

Oto klasyczny przykład ramki stosu (wykonania funkcji) i zadeklarowanej w niej zmiennej lokalnej, która wyjdzie poza zakres po zakończeniu ramki stosu-po zakończeniu funkcji:

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.

Oto przykład, w którym widzimy ramkę stosu będącą ciałem if instrukcji:

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

Jedynym sposobem, aby obiekt utworzony na stosie "pozostał w zakresie" po zakończeniu ramki jest powrót wartość funkcji. Ale to nie jest tak naprawdę "pozostanie w zasięgu", ponieważ obiekt jest kopiowany. Więc oryginał wychodzi poza zakres, ale kopia jest wykonana. Przykład:

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.

Teraz obiekt może być również zadeklarowany na stercie. Dla dobra tej dyskusji, pomyśl o stercie jako amorficznej plamce pamięci. W przeciwieństwie do stosu, który automatycznie przydziela i usuwa niezbędną pamięć podczas wchodzenia i wychodzenia z ramek stosu, musisz ręcznie rezerwować i zwolnić pamięć stosu.

Obiekt zadeklarowany na stosie po pewnym czasie "przetrwa" pomiędzy ramkami stosu. Można powiedzieć, że obiekt zadeklarowany na stercie nigdy nie wychodzi poza zakres, ale tak naprawdę jest tak dlatego, że obiekt nigdy nie jest tak naprawdę powiązany z żadnym zakresem. Taki obiekt musi być utworzony za pomocą słowa kluczowego new i musi być odwołany za pomocą wskaźnika.

Twoim obowiązkiem jest uwolnić obiekt sterty, gdy skończysz z nim. Obiekty sterty można uwolnić używając słowa kluczowego delete. Na Destruktor na obiekcie sterty nie zostanie wywołany, dopóki go nie uwolnisz.

Wskaźniki odnoszące się do obiektów heap są zazwyczaj zmiennymi lokalnymi powiązanymi z zakresami. Gdy skończysz używać obiektu heap, pozwalasz, aby wskaźniki odnoszące się do niego wyszły poza zakres. Jeśli nie zwolniono jawnie obiektu, na który wskazuje wskaźnik, blok pamięci sterty nigdy nie zostanie zwolniony, dopóki proces się nie zakończy (nazywa się to wyciekiem pamięci).

Think of it all this sposób: obiekt utworzony na stosie jest jak balon przyklejony do krzesła w pokoju. Po wyjściu z pokoju, balon automatycznie wyskakuje. Obiekt stworzony na stercie jest jak balon na wstążce, przywiązany do krzesła w pokoju. Wstążka jest wskaźnikiem. Po wyjściu z pokoju wstążka automatycznie znika, ale balon po prostu unosi się do sufitu i zajmuje miejsce. Właściwą procedurą jest pop balon z pinezką, a następnie wyjść z pokoju, po czym wstążka zniknie. Ale dobrą rzeczą w balonie na sznurku jest to, że możesz również rozwiązać wstążkę, trzymać ją w dłoni i wyjść z pokoju i zabrać ze sobą balon.

Aby przejść do przykładu z linked list: zazwyczaj węzły takiej listy są deklarowane na stercie, z każdym węzłem trzymającym wskaźnik do następnego węzła. Wszystko to siedzi na stosie i nigdy nie wychodzi poza zasięg. Jedyną rzeczą, która może wyjść poza zakres jest wskaźnik, który wskazuje na główny punkt listy - wskaźnik, którego używasz aby odnieść się do listy w pierwszej kolejności. To może wyjść poza zasięg.

Na przykład tworzenie rzeczy na stercie, a wskaźnik roota wychodzi poza zakres:
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
 61
Author: sparc_spread,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-25 02:17:54
{ //scope is defined by the curly braces
    std::vector<int> vec;
}
// vec is out of scope here!
vec.push_back(15);
 5
Author: Tony The Lion,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-09 23:03:51

Gdy opuści zakres, w którym została zadeklarowana :)

Twoje pytanie w obecnym stanie nie odpowiada bez zapoznania się z implementacją. Sprowadza się do miejsca, w którym deklarujesz ten węzeł.

void Foo()
{
    int i = 10;

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

} // i is out of scope
 3
Author: Ed S.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-09 23:05:07

"Poza zakresem" jest metonimią: jak w przypadku używania nazwy lub terminologii jednego pojęcia, aby mówić o czymś blisko spokrewnionym, ale innym.

W C++ zakres jest statycznym obszarem tekstu programu, a więc coś" poza zakresem", wzięte dosłownie, oznacza fizycznie poza obszarem tekstu. Na przykład, { int x; } int y;: deklaracja y jest poza zakresem, w którym x jest widoczna.

Metonimia "wyjście poza zakres" jest używany do wyrażenia idei, że dynamiczny aktywacja / instancjacja środowiska związanego z pewnym zakresem zostaje zakończona. I tak zmienne zdefiniowane w tym zakresie odchodzą (a więc "poza zakresem").

To, co faktycznie wyszło "poza zakres", to wskaźnik instrukcji, że tak powiem; ocena programu odbywa się teraz w zakresie, który nie ma widoczności dla tego. Ale nie wszystko w lunecie znika! Zmienne statyczne nadal będą tam znajdować się przy następnym wprowadzeniu zakresu.

"wyjście poza zakres" nie jest bardzo dokładny, ale krótki i każdy rozumie, co to znaczy.

 2
Author: Kaz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-09 23:05:52

Obiekt zadeklarowany wewnątrz funkcji (lub w pewnych konstrukcjach z nawiasami klamrowymi wewnątrz funkcji) wypada poza zakres, gdy wykonanie opuszcza tę część kodu.

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

Odnosi się to tylko do rzeczy zadeklarowanych na stosie, więc ma to niewiele wspólnego z listami pojedynczo połączonymi, ponieważ węzły list zazwyczaj są tworzone jako instancje na stosie za pomocą new.

W tym przykładzie wskaźnik zwrócony przez new wyjdzie poza zakres, gdy funkcja zakończy działanie, ale nic stanie się z samym węzłem:

void make_a_node() {
  Node* p = new Node;
} // Oh noes a memory leak!
 2
Author: DSimon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-09 23:08:56