Вопросы по ООП в C++
Я недавно начал изучать ООП. И возникло ряд вопросов.
1.После выполнения деструктора как я понимаю происходит отчистка полей-членов класса. То есть если я правильно понимаю, то после выполнения первого деструктора объекта object освобождается память выделенная на стеке(в данном случае это int A). То что происходит после выполнения деструктора второй раз. Будет UB?
#include <iostream>
class Alpha{
public:
int A;
Alpha(){}
~Alpha(){}
};
int main(){
Alpha object;
object.~Alpha();
object.~Alpha();
}
2.Что будет если вызвать явно деструктор для динамически выделенного объекта класса object. Будет ли освобождена память, которую занимают поля-члены(в данном случае - int A)? Ведь (int A) будет выделена на куче. Я думаю, что память не будет освобождена. Но тогда получается, что после выполнения деструктора может освобождаться память только для полей-членов, которые были выделены на стеке.
#include <iostream>
class Alpha{
public:
int A;
Alpha(){}
~Alpha(){}
};
int main(){
Alpha* object = new Alpha;
object->~Alpha();
}
3.Почему для класса Bion деструктор вызывается один раз. А для класса Alpha деструктор вызывается два раза.
#include <iostream>
using std::cout;
class Alpha{
public:
int A;
Alpha(){}
~Alpha(){cout << "~Alpha" << '\n';}
};
class Bion{
public:
int B;
Bion(){}
~Bion(){cout << "~Bion" << '\n';}
};
int main(){
Alpha object_alpha;
Bion* object_bion = new Bion;
delete object_bion;
object_alpha.~Alpha();
}
вывод:
~Bion
~Alpha
~Alpha
Ответы (3 шт):
- Деструктор не освобождает память.
- Деструктор вызывается автоматически в конце области видимости локальной переменной с объектом. А так же, при использовании
delete. - Явно вызывать деструктор не надо.
Кроме деструктора базового класса в деструкторе дочернего. - В твоём коде деструктор ничего не делает, так что его можно безвредно вызывать сколько угодно, как и любой обычный метод. По крайней мере, я так думаю, но могу и ошибаться.
- Память не освобождается, потому что вызван деструктор, а деструктор вызывается при уничтожении объекта. Раз объекта нет, значит память свободна.
- Если объект владеет некоторым участком памяти, тогда его деструктор должен освобождать эту память или это нужно сделать в ручную, но перед уничтожением объекта:
- Но бывают случаи, когда деструктор ничего не делает или не делает ничего, связанное с некоторым участком памяти.
Можно написать маленький пример для демонстрации:
struct A {
~A() { cout << " object destroyed\n"; }
};
int main()
{
int k = 1;
cout << k;
//некоторая область видимости, после чего объект
//уничтожается, "говоря последнее слово перед смертью"
// _ вызов деструктора
{
A a;
}
// объекта "а" не существует
// при уничтожении объекта вызывается его деструктор
A a1;
cout << k + sizeof(A) << "-nd ";
// при возвращении из функции и "a1" уничтожен
//(как и все статические) объекты соответственно
//вызваны деструкторы, и память освобождена
return 0;
}
- Также бывают случаи, что деструктору не нужно освобождать память, так
как объект не владеет никаким участком памяти, но деструктор может
выполнить не менее важные функции, такие, как:
закрытие файла, возвращение начального состояния(флагов), установка старой функции завершения программы(std::terminate_handler) и т.д. и т.п.
Поэтому, с точки зрения стандарта,
" Как только для объекта вызывается деструктор, этот объект больше не существует"
. А значит, это есть общее допущение, не зависимое от состояния объекта, что если вызван деструктор, приложение может считать, что этого объекта нет _ игнорировать его, даже если он есть на самом деле. И опять же по стандарту(и по логике языка):
"поведение не определено, если деструктор вызывается для объекта, время жизни которого закончилось "
.А значит, в любом случаи, повторный вызов деструктора может закончится одним словом _ "Бум!"... Не делайте так, если хотите избегать проблем.
Деструктор не освобождает память, которую занимает объект. Ваш деструктор не делает вообще ничего, в том смысле что он компилируется в 0 инструкций.
Однако, стандарт говорит, что деструктор "заканчивает время жизни объекта" (
[basic.life]/1.4). Это формальность, в том смысле, что глядя на байты в памяти нельзя определить, жив объект или мертв. Но вызов деструктора (или любого метода) второй раз на нем же вызывает UB. Причем даже если написатьobject.~Alpha();один раз (а не два), UB все равно будет, потому что при уничтожении переменной деструктор вызывается автоматически (т.е. еще один раз).Память останется выделенной. И ваш деструктор ничего не делает. Единственный эффект - формальное окончание времени жизни объекта.
получается, что после выполнения деструктора может освобождаться память только для полей-членов, которые были выделены на стеке
Вот это вообще не понял. Память под поля освобождается, когда освобождается память под весь объект. Деструктор этого не делает.
См. (1).