Как система времени выполнения C ++ знает, когда объекты выходят из области видимости - PullRequest
20 голосов
/ 26 марта 2011

Мне было интересно, как система времени выполнения C ++ обнаруживает, когда объект выходит из области видимости, так что соответственно, он вызывает деструктор, чтобы освободить занятую память.

Спасибо.

Ответы [ 7 ]

25 голосов
/ 26 марта 2011

Среда выполнения не работает - компилятор следит за областью действия и генерирует код для вызова деструктора. Если вы создадите простое тестовое приложение и посмотрите на сгенерированную разборку, вы увидите явные вызовы деструктора.

Фрагмент разборки из MSVC:

int main() {
    std::string s1;
...
00971416  call        dword ptr [__imp_std::basic_string<char,std::char_traits<char>,std::allocator<char> >::basic_string<char,std::char_traits<char>,std::allocator<char> > (979290h)] 
...
    {
        std::string s2;
00971440  call        dword ptr [__imp_std::basic_string<char,std::char_traits<char>,std::allocator<char> >::basic_string<char,std::char_traits<char>,std::allocator<char> > (979290h)] 
...    
    }
00971452  call        dword ptr [__imp_std::basic_string<char,std::char_traits<char>,std::allocator<char> >::~basic_string<char,std::char_traits<char>,std::allocator<char> > (979294h)] 
...
}
0097146B  call        dword ptr [__imp_std::basic_string<char,std::char_traits<char>,std::allocator<char> >::~basic_string<char,std::char_traits<char>,std::allocator<char> > (979294h)] 
18 голосов
/ 26 марта 2011

Это известно статически во время компиляции

{
  string s; /* ctor called here */
} /* dtor called here */

Иногда это сложнее

{
  again:
  {
    string s; /* ctor called here */
    goto again; /* now dtor of s is called here */
    string q; /* ctor not called. not reached. */
  } /* dtor of s and q would be called here. but not reached */
}
6 голосов
/ 26 марта 2011

Это не имеет ничего общего со временем выполнения.Компилятор отслеживает область действия каждой лексической переменной и добавляет вызовы деструкторов.

5 голосов
/ 27 марта 2011

«вызов деструктора» и «освобождение памяти, связанной с переменной» - это совершенно разные вещи.

Деструктор - это просто функция, которую C ++ достаточно хорош для вызова, когда ваш объект выходит из области видимости или явно удаляется. Компилятор генерирует это для вас, как говорили другие. Это удобный способ очистить все в своем классе, что вам нужно.

Освобождение памяти, связанной с чем-либо в стеке, требует изучения работы стека. Когда вызывается функция, память выделяется для всего в стеке, просто помещая в стек количество данных, необходимых для этих переменных. Хотя это явно не указано в спецификации C ++, «push» на самом деле просто включает в себя указатель на вершину стека, указывающий выше (или ниже), чтобы освободить место для дополнительных переменных. Это простое добавление указателя. Когда функция возвращается, происходит вычитание указателя.

void foo()
{
    HerClass y;
    YourClass x; // stack incremented sizeof(YourClass) + sizeof(HerClass)

    return; // destructor called, 
            // then stack decremented sizeof(YourClass) + sizeof(HerClass)
}

Все выскочило из стека. Вы можете узнать больше об этом, прочитав о соглашениях о вызовах .

Память кучи явно контролируется программой вручную. Если код не выполняет явное управление кучей для вас, вам необходимо убедиться, что вы удаляете все, что у вас есть.

5 голосов
/ 26 марта 2011

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

например. следующий код

if(something)
{
    MyClass test;
    test.doSomething();
}

В результате получится машинный код, который выглядит примерно так:

  • оценить прыжок
  • спрыгнуть вниз, если требуется
  • выделить память для теста
  • вызов конструктора MyClass на тесте
  • вызов сделать что-нибудь на тесте
  • вызов деструктора MyClass на тесте
  • освободить память
3 голосов
/ 27 марта 2011

Когда приложение входит в область действия (блок, вызов функции и т. Д.), Среда выполнения загружает контекст (включая локальные переменные) для этого блока в стек.Это фактическая структура данных стека.По мере того как выполнение становится все глубже и глубже во вложенных контекстах, стек становится все выше и выше.Если main() вызывает foo(), что вызывает bar(), стек будет иметь контекст main() s внизу стека, затем контекст foo, а затем bar.Вот почему бесконечная рекурсия приводит к «переполнению стека», а генерирование исключений вызывает «разматывание стека».

Когда выполнение выходит из этой области, этот контекст выталкивается из стека.В C ++ извлечение объектов из стека включает вызов деструктора для этих объектов.Поэтому, когда bar() returns, его локальные переменные будут выталкиваться из стека, и будут вызываться деструкторы для этих переменных.

1 голос
/ 26 марта 2011

Возможно, вы неправильно понимаете характеристики нескольких языков программирования.C ++ не имеет сборщика мусора, поэтому он не решает, когда объект вышел из области видимости: пользователь делает.

int main()
{
    Obj * obj = new Obj;
    Obj obj2;

    delete obj;
}

В приведенной выше функции вы создаете объект obj в кучу, и отпустите его, когда вы больше не собираетесь его использовать.С другой стороны, объект obj2 просто завершает свою жизнь в конце main () , как и любая другая переменная.Когда объект завершает свою жизнь, деструктор объекта вызывается автоматически;компилятор вставляет вызовы этих деструкторов автоматически: в конце функции или при вызове оператора delete .

...