2012-04-09 2 views
18

В C++, когда объект определяется как «вне сферы действия»?Когда объект «выходит из сферы действия»?

Более конкретно, если бы у меня был отдельный список, что бы определить единый объект узла списка как «вне сферы видимости»? Или, если объект существует и ссылается на переменную «ptr», правильно ли утверждать, что объект определен как «вне сферы действия» в момент удаления ссылки или указывает на другой объект?

UPDATE: Предполагая, что объект является классом, который имеет реализованный деструктор. Будет ли деструктор вызываться в тот момент, когда объект выходит из области?

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

Другими словами, будет проверяемый узел, на который указывает list_1 вызвать его деструктор после этой строки:

Node * list_1 = новый узел (3);

?

+1

Ваши вопросы обновления очень хороший, и ответ, нет. Когда указатель выходит из области видимости, деструктор объекта, на который он указывает, ** не ** автоматически вызывается. Вы должны называть это самостоятельно, и это происходит, когда вы вызываете 'delete' на узле, чтобы освободить память. Это хорошая вещь, о которой следует помнить, когда у вас есть массив или список указателей на объекты со значимыми реализациями деструктора. К счастью, поскольку вы все равно должны «освобождать» себя, вы будете запускать деструкторы. –

+0

Кстати, если вам действительно нужен связанный список для чего-то, вам гораздо лучше использовать std :: list, чем собственный собственный связанный список. – DSimon

+1

Я должен был сказать 'delete' вместо' free'. Игнорировать 'free', это C наследие. Используйте только 'new' и' delete' для размещения/удаления кучи. –

ответ

41

Во-первых, помните, что объекты в C++ может быть либо на стеке или на на куче.

Кадр стека (или область действия) определяется инструкцией. Это может быть такой же большой, как функция или малый, как блок управления потоком (while/if/for и т. Д.). Произвольная пара {}, включающая произвольный блок кода, также представляет собой стек стека. Любая локальная переменная, определенная в кадре, выйдет за пределы области действия, как только программа выйдет из этого кадра. Когда переменная стека выходит из области видимости, ее деструктор вызывается.

Так вот классический пример кадра стека (Казнь функции) и локальной переменной, объявленной внутри него, который будет выходить за рамки после выхода из стека кадра - когда функция заканчивает:

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. 

Вот пример, где мы видим кадр стека быть только телом if заявления:

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

Единственным способом для стека созданного объекта «остается в рамках» после того, как кадр вышел в если это возвращаемое значение функции. Но это действительно не «остается в области видимости», потому что объект копируется. Таким образом, оригинал выходит за рамки, но копия сделана. Пример:

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. 

Теперь объект может быть объявлен в куче. Ради этой дискуссии подумайте о куче как о аморфном блоке памяти. В отличие от стека, который автоматически выделяет и деблокирует необходимую память при входе и выходе из фреймов стека, вы должны вручную зарезервировать и освободить память кучи.

Объект, объявленный в куче, после моды «выживает» между кадрами стека. Можно сказать, что объект, объявленный в куче, никогда не выходит за рамки, но это действительно потому, что объект никогда не ассоциируется с какой-либо областью. Такой объект должен быть создан с помощью ключевого слова new и должен называться указателем.

Вы несете ответственность за освобождение объекта кучи, как только вы закончите с ним. Вы получаете свободные объекты кучи с ключевым словом delete. Деструктор объекта кучи не вызывается до тех пор, пока вы не освободите объект.

Указатели, относящиеся к объектам кучи, обычно являются локальными переменными, связанными с областями. Как только вы закончите использовать объект кучи, вы разрешаете указателю (указателям) ссылаться на него, чтобы выйти из области видимости. Если вы явно не освободили объект, на который указывает указатель, блок памяти кучи никогда не будет освобожден до тех пор, пока процесс не завершится (это называется утечкой памяти).

Подумайте об этом так: объект, созданный в стеке, подобен воздушному шару, прикрепленному к стулу в комнате. Когда вы выходите из комнаты, воздушный шар автоматически появляется. Объект, созданный на куче, похож на воздушный шар на ленте, привязанный к стулу в комнате. Лента - это указатель. Когда вы выходите из комнаты, лента автоматически исчезает, но воздушный шар просто плывет к потолку и занимает пространство. Правильная процедура состоит в том, чтобы вытащить воздушный шар с помощью штифта, а затем выйти из комнаты, после чего лента исчезнет.Но, хорошая вещь о воздушном шаре на струне, вы также можете развязать ленту, держать ее в руке и выйти из комнаты и взять с собой воздушный шар.

Итак, чтобы перейти к примеру связанного списка: обычно узлы такого списка объявляются в куче, причем каждый узел содержит указатель на следующий узел. Все это сидит на куче и никогда не выходит за рамки. Единственное, что может выйти из области видимости - указатель, указывающий на корень списка - указатель, который вы используете для ссылки в списке в первую очередь. Это может выйти из сферы действия.

Вот пример создания вещи в куче, а указатель корень выходит из области видимости:

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

Спасибо за ввод. Поэтому мой последний вопрос состоял бы в том, что (если list_1 имел деструктор), вызывающий деструктор должен быть вызван, как только ... list_1-> next = list_2; ... выполняется? –

+0

Это немного отличается от вашего последующего действия, поэтому я отвечу на оба. Ответ отрицательный, потому что все, что вы делаете здесь, устанавливает поле 'next', чтобы указать на выделенный узел - ничто не выходит за рамки. Но даже если что-то выходит из сферы действия, никакой деструктор не будет вызван, потому что это что-то будет указателем (см. Комментарий к вашему основному вопросу.) –

+0

Обратите внимание, что, кстати, объект, созданный в стеке **, будет ** вызвать его деструктор, когда он выходит за рамки. –

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

Когда он выходит из сферы, что он был объявлен в :)

Ваш вопрос, как он стоит не отвечает, не видя реализации. Это сводится к тому, где вы объявляете этот узел.

void Foo() 
{ 
    int i = 10; 

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

} // i is out of scope 
2

«вне области» является метонимия: как, используя имя или терминологию одной концепции, чтобы говорить о чем-то тесно связаны, но разные.

В C++ область действия представляет собой статическую область текста программы, и поэтому что-то «вне сферы действия», взятое буквально, означает физически за пределами области текста. Например, { int x; } int y;: объявление y выходит за пределы области, в которой видна x.

Метонимия «выход из сферы действия» используется для выражения идеи о том, что динамическая активация/создание среды, связанной с некоторой областью, заканчивается. И поэтому переменные, определенные в этой области, уходят (таким образом, «вне сферы действия»).

То, что фактически исчезло из сферы действия, является указателем инструкции, так сказать; оценка программы в настоящее время происходит в области, которая не имеет видимости для этого. Но не все в масштабах уходит! Статические переменные все еще будут присутствовать при следующем вводе области.

«Выход из сферы действия» не очень точен, но короткий, и каждый понимает, что это значит.

2

Объект, который объявлен внутри функции (или внутри определенных внутрикорневых конструкций внутри фигурных скобок), выходит из сферы действия, когда выполнение оставляет эту часть кода.

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

Это относится только к StuFF объявлен в стеке, поэтому он имеет мало общего с однократно связанных списков, так как список узлов, как правило, будут экземпляры в куче с new.

В этом примере указатель возвращаемый new будет выходить из сферы при выходе из нее, но ничего не произойдет с самим Node:

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

Если класс Node был реализован деструктор, будет ли Node, на который ссылается p, вызвать деструктор, момент p выходит из области? –

+0

Нет. В моем первом примере, однако, std :: string destructor * будет * вызываться, когда x оставляет область. Во втором примере то, что выходит из области, не является узлом, а узлом *, а сами указатели не имеют деструкторов. – DSimon

+0

, но если вместо необработанного указателя вы использовали 'std :: unique_ptr p = new Node;' деструктор объекта Node будет вызываться при выходе, когда 'p' выходит за рамки. – d7samurai

Смежные вопросы