явный вызов деструктора не разрушает мой объект, почему? - PullRequest
6 голосов
/ 13 сентября 2011

Я вызываю деструктор, чтобы освободить память, но он не удаляет мой объект.В чем причина этого?

мой код выглядит так:

class A
{
public: 
    int a;
    A()
    {
        cout << "a" << endl;
    }
};

class B :public A
{
public: 
    int b;
    B()
    {
        cout << "b" << endl; a = 10; b = 20;
    }
    ~B()
    {
        cout << a << b << endl;
    }
};

и я использую его как:

int main()
{
    {
        B b;
        b.~B();
        b.b=100;  // why this step is executed?
    }
    int x;
    cin>>x;
    return 0;
}

Ответы [ 7 ]

19 голосов
/ 13 сентября 2011

я вызываю деструктор для освобождения памяти

Почему?На уровне языка деструктор не освобождает память, занятую самим объектом.

Нетривиальный деструктор завершает время жизни объекта , но не заканчивает продолжительность хранения объекта .Это означает, что память остается выделенной, она просто становится «сырой» (неинициализированной).Таким образом, в этом смысле это уничтожает ваш объект.

Между тем, тривиальный деструктор вообще не действует.Даже если вы вызываете это явно, время жизни объекта не заканчивается.

В вашем случае деструктор B::~B нетривиален, что формально означает, что, вызвав его, вы закончили жизнь вашего объекта.Вы уничтожили это столько, сколько локальный объект может быть уничтожен. Но память остается .Попытка получить доступ к этой памяти как объекту B просто приводит к неопределенному поведению.

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

5 голосов
/ 13 сентября 2011

Вы не вызываете такой деструктор (ну, вы можете , но обычно это не делается).

Для автоматических переменных, таких как b,деструктор будет вызван в какой-то момент, когда переменная выходит из области видимости.Вам никогда не нужно вызывать деструктор явно.

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

C ++ 03 состояния в 12.4 Destructors:

Деструкторы вызываются неявно:

  • для построенного объекта со статической продолжительностью хранения (3.7.1) при завершении программы;
  • для построенного объекта с автоматической продолжительностью хранения (3.7.2) при выходе из блока, в котором создается объект;
  • для созданного временного объекта, когда заканчивается время жизни временного объекта;
  • для построенного объекта, выделенного новым выражением, с помощью выражения delete;
  • в некоторых ситуациях из-за обработки исключений.

Деструкторы также могут вызываться явно.

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

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

Как только деструктор вызывается для объекта, объект больше не существует;поведение не определено, если деструктор вызывается для объекта, время жизни которого закончилось.Пример: если деструктор для автоматического объекта вызывается явным образом, а затем блок оставляется таким образом, который обычно вызывает неявное уничтожение объекта, поведение не определено.

Этот текст остается без измененийв последнем наброске C ++ 11, который у меня есть (n3225, ноябрь 2010 г.), и вряд ли он по сути изменился бы между этим и утверждением в августе 2011 г.

4 голосов
/ 13 сентября 2011

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

3 голосов
/ 13 сентября 2011

Это выполнено , потому что вы написали код, который сказал, что вы хотите, чтобы это произошло.Компилятор просто делает то, что вы сказали.

То, что вы делаете, вероятно не"освобождает память", как вы и предлагали.Вместо этого он просто вызывает деструктор.Деструкторы не освобождают память, занятую объектами, к которым они обращаются.Они освобождают память, выделенную объектом (например, вызывая деструкторы переменных-членов или вызывая free или delete для других вещей), но память самого объекта освобождается в другом месте, либо внутренними действиями delete или компилятором при очистке автоматических переменных (это то, что представляет ваша декларация B b).Даже закрытие блока области видимости, вероятно, не освобождает память для b;компиляторы обычно выясняют, сколько стекового пространства им потребуется для всей подпрограммы , и выделяют все это при входе.Память, занятая этим объектом B, зарезервирована для b при входе во внутреннюю область и при выходе автоматически вызывается деструктор.

1 голос
/ 08 марта 2019

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

0 голосов
/ 18 февраля 2016

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

class B: public A
{
public:
 int b;
 B() {cout<<"b"<<endl;a=10;b=20;}
 ~B() {Release(); cout<<a<<b<<endl;}
 void Release() { cout<<"Releasing B"; b = 0; }
};

int main()
{
    {
      B b;
      b.Release();
      b.b=100;  // why this step is executed?
    }
    int x;
    cin>>x;
    return 0;
}

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

int main()
{
    {
      B b;
      b.b = 100;  //OK
    }
    b.b = 100; //compile time error
    int x;
    cin>>x;
    return 0;
}
0 голосов
/ 13 сентября 2011

Почему бы и нет? Ваш объект был уничтожен, но его пространство памяти все еще остается, пока не выйдет из области видимости, где, кстати, он снова будет уничтожен. Неопределенное поведение делать то, что ты делаешь.

...