Вопросы по ООП в 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 шт):

Автор решения: Qwertiy
  1. Деструктор не освобождает память.
  2. Деструктор вызывается автоматически в конце области видимости локальной переменной с объектом. А так же, при использовании delete.
  3. Явно вызывать деструктор не надо. Кроме деструктора базового класса в деструкторе дочернего.
  4. В твоём коде деструктор ничего не делает, так что его можно безвредно вызывать сколько угодно, как и любой обычный метод. По крайней мере, я так думаю, но могу и ошибаться.
→ Ссылка
Автор решения: AR Hovsepyan
  • Память не освобождается, потому что вызван деструктор, а деструктор вызывается при уничтожении объекта. Раз объекта нет, значит память свободна.
  • Если объект владеет некоторым участком памяти, тогда его деструктор должен освобождать эту память или это нужно сделать в ручную, но перед уничтожением объекта:
  • Но бывают случаи, когда деструктор ничего не делает или не делает ничего, связанное с некоторым участком памяти.

Можно написать маленький пример для демонстрации:

    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) и т.д. и т.п.

Поэтому, с точки зрения стандарта,

" Как только для объекта вызывается деструктор, этот объект больше не существует"

. А значит, это есть общее допущение, не зависимое от состояния объекта, что если вызван деструктор, приложение может считать, что этого объекта нет _ игнорировать его, даже если он есть на самом деле. И опять же по стандарту(и по логике языка):

"поведение не определено, если деструктор вызывается для объекта, время жизни которого закончилось "

.А значит, в любом случаи, повторный вызов деструктора может закончится одним словом _ "Бум!"... Не делайте так, если хотите избегать проблем.

→ Ссылка
Автор решения: HolyBlackCat
  1. Деструктор не освобождает память, которую занимает объект. Ваш деструктор не делает вообще ничего, в том смысле что он компилируется в 0 инструкций.

    Однако, стандарт говорит, что деструктор "заканчивает время жизни объекта" ([basic.life]/1.4). Это формальность, в том смысле, что глядя на байты в памяти нельзя определить, жив объект или мертв. Но вызов деструктора (или любого метода) второй раз на нем же вызывает UB. Причем даже если написать object.~Alpha(); один раз (а не два), UB все равно будет, потому что при уничтожении переменной деструктор вызывается автоматически (т.е. еще один раз).

  2. Память останется выделенной. И ваш деструктор ничего не делает. Единственный эффект - формальное окончание времени жизни объекта.

    получается, что после выполнения деструктора может освобождаться память только для полей-членов, которые были выделены на стеке

    Вот это вообще не понял. Память под поля освобождается, когда освобождается память под весь объект. Деструктор этого не делает.

  3. См. (1).

→ Ссылка