Когда вызывается деструктор C++?


основной вопрос: когда программа вызывает метод деструктора класса В C++? Мне сказали, что он вызывается, когда объект выходит из области видимости или подвергается delete

более конкретные вопросы:

1) Если объект создается с помощью указателя, и этот указатель позже удаляется или получает новый адрес, на который он указывает, вызывает ли объект, на который он указывал, свой деструктор (предполагая, что ничто другое не указывает на него)?

2) После на вопрос 1, что определяет, когда объект выходит из области видимости (не относительно того, когда объект покидает данный {блок}). Другими словами, когда деструктор вызывается для объекта в связанном списке?

3) Вы когда-нибудь хотели бы вызвать деструктор вручную?

9 84

9 ответов:

1) Если объект создается с помощью указателя, и этот указатель позже удаляется или получает новый адрес, на который он указывает, вызывает ли объект, на который он указывал, свой деструктор (предполагая, что ничто другое не указывает на него)?

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

2) После ответа на вопрос 1, что определяет, когда объект выходит из области видимости (не относительно того, когда объект покидает данный {блок}). Итак, другими словами, когда деструктор вызывается для объекта в связанном список?

Это зависит от реализации связанного списка. Типичные коллекции уничтожают все содержащиеся в них объекты при их уничтожении.

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

3) Вы когда-нибудь хотели бы вызвать деструктор вручную?

конечно. Например, если вы хотите заменить объект другим объектом того же типа, но не хотите освобождать память, чтобы снова выделить ее. Вы можете уничтожить старый объект на месте и построить новый на месте. (Однако, как правило, это плохая идея.)

// pointer is destroyed because it goes out of scope,
// but not the object it pointed to. memory leak
if (1) {
 Foo *myfoo = new Foo("foo");
}


// pointer is destroyed because it goes out of scope,
// object it points to is deleted. no memory leak
if(1) {
 Foo *myfoo = new Foo("foo");
 delete myfoo;
}

// no memory leak, object goes out of scope
if(1) {
 Foo myfoo("foo");
}

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

ответ-да. @DavidSchwartz привел один пример, но это довольно необычный. Я приведу пример, который находится под капотом того, что многие программисты C++ используют все время:std::vectorstd::deque, хотя он не используется так много).

как известно большинству людей,std::vector выделяем большой блок память, когда / если вы добавляете больше элементов, чем может вместить его текущее выделение. Однако, когда он это делает, у него есть блок памяти, который способен удерживать больше объекты, чем в настоящее время в векторе.

чтобы справиться с этим, что vector делает под крышками выделяет raw памяти через

  1. при создании объекта с new, вы несете ответственность за вызов delete. При создании объекта с помощью make_shared, в результате shared_ptr отвечает за ведение счета и вызов delete когда счетчик использования идет к нулю.
  2. выход из области действия означает выход из блока. Это когда деструктор вызывается, предполагая, что объект был не выделено new (т. е. это объект стека).
  3. о единственном времени, когда вам нужно вызвать деструктор явно, когда вы выделяете объект с размещение new.

1) объекты не создаются с помощью указателей. Существует указатель, который присваивается любому объекту, который вы "новый". Предполагая, что это то, что вы имеете в виду, если вы вызываете "delete" на указателе, он фактически удалит (и вызовет деструктор) объект разыменования указателя. Если вы назначите указатель на другой объект, произойдет утечка памяти; ничто в C++ не будет собирать ваш мусор для вас.

2) это два отдельных вопроса. Переменная выходит из области видимости, когда стек кадр, в котором он объявлен, выскочил из стека. Обычно это происходит, когда вы выходите из блока. Объекты в куче никогда не выходят из области видимости, хотя их указатели в стеке могут. Ничто в частности не гарантирует, что деструктор объекта в связанном списке будет вызван.

3) не реально. Там может быть глубокая магия, которая предполагает иное, но обычно вы хотите сопоставить свои "новые" ключевые слова с ключевыми словами "удалить" и поместить все в свой деструктор, чтобы убедиться он правильно очищает себя. Если вы этого не сделаете, обязательно прокомментируйте деструктор с конкретными инструкциями для всех, кто использует класс о том, как они должны очистить ресурсы этого объекта вручную.

чтобы дать подробный ответ на вопрос 3: Да, есть (редкие) случаи, когда вы можете вызвать деструктор явно, в частности, как аналог нового размещения, как замечает dasblinkenlight.

дать конкретный пример:

#include <iostream>
#include <new>

struct Foo
{
    Foo(int i_) : i(i_) {}
    int i;
};

int main()
{
    // Allocate a chunk of memory large enough to hold 5 Foo objects.
    int n = 5;
    char *chunk = static_cast<char*>(::operator new(sizeof(Foo) * n));

    // Use placement new to construct Foo instances at the right places in the chunk.
    for(int i=0; i<n; ++i)
    {
        new (chunk + i*sizeof(Foo)) Foo(i);
    }

    // Output the contents of each Foo instance and use an explicit destructor call to destroy it.
    for(int i=0; i<n; ++i)
    {
        Foo *foo = reinterpret_cast<Foo*>(chunk + i*sizeof(Foo));
        std::cout << foo->i << '\n';
        foo->~Foo();
    }

    // Deallocate the original chunk of memory.
    ::operator delete(chunk);

    return 0;
}

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

  1. указатели -- обычные указатели не поддерживают RAII. Без явного delete, там будет мусор. К счастью, C++ имеет авто указатели что справиться с этим для вас!

  2. Scope -- подумайте о том, когда переменная становится невидимка в вашей программе. Обычно это в конце {block}, Как вы указали.

  3. механика разрушения -- никогда попробуй это. Просто позвольте scope и RAII сделать магию для вас.

всякий раз, когда вы используете "новый", то есть прикрепляете адрес к указателю, или, скажем, вы претендуете на место в куче, вам нужно "удалить" его.
1.да, когда вы удаляете что-то, деструктор вызывается.
2.Когда вызывается деструктор связанного списка, вызывается деструктор объектов. Но если это указатели, их нужно удалить вручную. 3.когда на пространство претендует "новое".

Да, деструктор (он же dtor) вызывается, когда объект выходит из области видимости, если он находится в стеке или при вызове delete на указатель на объект.

  1. если указатель удаляется через delete тогда dtor будет вызван. Если вы переназначаете указатель без вызова delete во-первых, вы получите утечку памяти, потому что объект все еще существует в памяти где-то. В последнем случае dtor не называется.

  2. хорошая связь реализация списка вызовет dtor всех объектов в списке, когда список будет уничтожен (потому что вы либо вызвали какой-то метод, чтобы уничтожить его, либо он вышел из области действия). Это зависит от реализации.

  3. Я сомневаюсь в этом, но я не удивлюсь, если там есть какие-то странные обстоятельства.

Если объект создается не с помощью указателя (например,a1 = a ();), деструктор вызывается при разрушении объекта, всегда при завершении функции, в которой находится объект.например:

void func()
{
...
A a1 = A();
...
}//finish


деструктор вызывается, когда код выполняется в строке "finish".

если объект создается с помощью указателя(например,A * a2 = new A();),деструктор вызывается при удалении указателя(delete a2;).Если точка не удалена пользователь явно или задал новый адрес перед его удалением, произошла утечка памяти. Это ошибка.

в связанном списке, если мы используем std::list, нам не нужно заботиться о декструкторе или утечке памяти, потому что std:: list закончил все это для нас. В связанном списке, написанном нами самими, мы должны написать описатель и удалить указатель явно.В противном случае это приведет к утечке памяти.

мы редко вызываем деструктор вручную. Это функция, обеспечивающая система.

извините за мой плохой английский!