Мне было интересно, как система выполнения С++ обнаруживает, когда объект выходит из области действия, так что он вызывает деструктор, чтобы освободить занятую память.
Спасибо.
Мне было интересно, как система выполнения С++ обнаруживает, когда объект выходит из области действия, так что он вызывает деструктор, чтобы освободить занятую память.
Спасибо.
Это известно статически во время компиляции
{
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 */
}
Время выполнения не выполняется - компилятор хранит вкладки в области видимости и генерирует код для вызова деструктора. Если вы создадите простое тестовое приложение и посмотрите на сгенерированную разборку, вы увидите явные вызовы деструктора.
Демонтаж фрагмента из 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)]
Это не имеет никакого отношения к времени исполнения. Компилятор отслеживает объем каждой лексической переменной и добавляет вызовы деструктора.
Объем заканчивается областью. Он не "обнаруживает" его, компилятор записывает код так, как деструктор будет вызван вовремя.
например. следующий код
if(something)
{
MyClass test;
test.doSomething();
}
Появится машинный код, который сделает что-то вроде этого:
"вызов деструктора" и "освобождение памяти, связанной с переменной", - это две разные вещи целиком.
Деструктор - это просто функция, которая С++ достаточно хороша, чтобы вызывать вас, когда ваш объект выходит из области видимости или явно удаляется. Компилятор генерирует это для вас, как говорили другие. Это удобный способ для вас очистить что-нибудь в своем классе, которое вам нужно очистить.
Освобождение памяти, связанной с чем-то в стеке, связано с изучением того, как работает стек. Когда вызывается функция, память выделяется для всего в стеке, просто нажимая на стек объем данных, необходимых для этих переменных. Хотя явно не указано в спецификации С++, "push" действительно просто включает в себя указание указателя на верхнюю часть стека выше (или ниже), чтобы освободить место для дополнительных переменных. Это простое добавление указателя. Когда функция возвращается, происходит вычитание указателя.
void foo()
{
HerClass y;
YourClass x; // stack incremented sizeof(YourClass) + sizeof(HerClass)
return; // destructor called,
// then stack decremented sizeof(YourClass) + sizeof(HerClass)
}
Все выскочило из стека. Вы можете узнать больше об этом, прочитав соглашения о вызовах.
Память кучи явно управляется программой вручную. Если код не выполняет явное управление кучей для вас, вам нужно убедиться, что вы удалите все новое.
Когда приложение входит в среду с областью (блок, вызов функции и т.д.), среда выполнения загружает контекст (включая локальные переменные) для этого блока в стек. Это фактическая структура данных стека. Поскольку выполнение происходит глубже и глубже в вложенных контекстах, стек становится все выше и выше. Если main()
вызывает foo()
, который вызывает bar()
, стек будет иметь контекст main()
в нижней части стека, затем foo
контекст, затем bar
. Вот почему бесконечная рекурсия приводит к "переполнению стека" и срабатыванию триггеров исключений "разматывание стека".
Когда выполнение завершает эту область, этот контекст выталкивается из стека. В С++ всплывающие объекты стека включают вызов деструктора для этих объектов. Поэтому, когда bar()
returns
, его локальные переменные будут удалены из стека, и будут вызваны деструкторы для этих переменных.
Вероятно, вы неправильно понимаете характеристики нескольких языков программирования. У С++ нет сборщика мусора, поэтому он не решает, когда объект вышел из сферы действия: пользователь делает.
int main()
{
Obj * obj = new Obj;
Obj obj2;
delete obj;
}
В вышеприведенной функции вы создаете объект obj в куче и отпустите его, когда вы больше не собираетесь его использовать. С другой стороны, объект obj2 просто заканчивает свою жизнь в конце main(), как и любую другую переменную. Когда объект завершает свою жизнь, деструктор объекта вызывается автоматически; компилятор автоматически вставляет вызовы этих деструкторов: в конце функции или при вызове оператора delete.