Подтвердить что ты не робот

Восстановить объект от деструктора в С++?

Отказ от ответственности: Я знаю, что это плохой дизайн, я просто задаю вопрос из любопытства, чтобы попытаться получить более глубокое знание о том, как деструктор работает на С++.

В С# можно написать: GC.KeepAlive(this) в деструкторе класса (см. ниже), и это будет означать, что объект все еще будет сохранен в памяти даже после завершения вызова деструктора.

Позволяет ли дизайн С++ восстанавливать объект из деструктора, аналогичный описанному выше алгоритму С#?

Изменить. Как указано в ответе ниже, GC.ReRegisterForFinalize() более тесно связан с вопросом, чем GC.KeepAlive(this).

4b9b3361

Ответ 1

Короткий ответ: нет. В С++ не используется сборка мусора, например Java или С#. Когда объект уничтожается, он немедленно уничтожается. Ушел на всегда. Присоединился к хору невидимым. Pining для фьордов и т.д.

И сказать это пару раз разными словами, чтобы не было возможной реинтерпретации с легким...

Деструктор вызывается как часть уничтожения объекта. Уничтожение объектов состоит в вызове деструктора и освобождении памяти, которая была использована для самого объекта. Это один процесс, а не два отдельных процесса. Пока деструктор запущен, объект все еще существует для деструктора, но он существует в заемное время. Это предрешено, что объект будет испаряться, как только деструктор вернется. Когда вызывается деструктор, объект будет уничтожен, и ничто не изменит его судьбу.

Поймите это: причина, по которой вызывается деструктор, заключается в том, что либо: объект был первоначально выделен в куче с "новым", а теперь "удалить" d. "delete" означает "delete", а не "delete maybe". Таким образом, объект удаляется. Или, если объект был выделен в стеке, поток выполнения вышел из области действия, поэтому все объекты, объявленные в области, будут уничтожены. Деструктор технически получает вызов в результате уничтожения объекта. Таким образом, объект уничтожается. Конец.

Сказав это, С++ позволяет реализовать пользовательский распределитель для ваших классов. Если вам это нравится, вы можете написать свои собственные функции распределения памяти и освобождения, которые реализуют любую функциональность, которую вы хотите. Хотя они никогда не используются для выделенных стек объектов (т.е. Локальных переменных).

Ответ 2

На самом деле вы неверно представляете, что делает GC.KeepAlive в .NET. Он не должен использоваться в деструкторе объекта, чтобы этот объект не был разрушен. Фактически, GC.KeepAlive() пуст и не имеет реализации. Смотрите исходный код .NET здесь.

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

Ответ 3

Вот идея:

C* gPhoenix= nullptr;

C::~C ()
{
gPhoenix= new C (*this);  // note: loses any further-derived class ("slice")
}

Теперь, если у объектов (баз или членов) действительно есть деструкторы, которые что-то делают, это сталкивается с проблемой, если вы delete gPhoenix;, поэтому вам понадобятся более сложные механизмы в зависимости от того, что она действительно пытается выполнить. Но у вас нет реальных целей, просто любопытное исследование, поэтому указывать на это должно быть достаточно.

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

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

Ответ 4

Как уже отмечалось , GC.KeepAlive не сделайте это.

Пока .NET идет, можно воскресить из финализатора, используя GC.ReRegisterForFinalize, вы все равно можете получить ссылку на если у вас есть WeakReference или GCHandle отслеживание или просто дать this чему-то вне класса. Это приведет к прекращению разрушения.

Это старый трюк, чтобы обнаружить сборку мусора в .NET 2.0 больше не актуально, но все же работает (вид, сбор мусора теперь можно быть частичным и выполняться параллельно с другими потоками).

Следует подчеркнуть тот факт, что на .NET вы используете финализатор, который работает до уничтожения, и может его предотвратить. Итак, хотя технически правильно, что вы не можете восстановить объект после уничтожения - на любом языке - вы можете приблизиться к поведению, которое вы описываете в .NET, за исключением используя GC.ReRegisterForFinalize.


На С++ вы уже получили правильный ответ.

Ответ 5

Это невозможно на любом языке.

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

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

То, что вы хотите, скорее всего, это иметь std::shared_ptr, который отслеживает копии и ссылки и только уничтожает объект, когда он больше не нужен.

Ответ 6

В случае, если это помогает, функция деструктора и распределение памяти различаются.

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

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

Вы можете взять на себя ответственность за распределение памяти, предоставив дополнительные реализации нового оператора или используя существующие, такие как размещение new, но общее поведение по умолчанию заключается в том, что компилятор вызовет ваш деструктор и что у вас есть возможность привести в порядок. Тот факт, что некоторая память впоследствии будет очищена, находится вне контроля деструктора.

#include <iostream>
#include <iomanip>

namespace test
{
  class GotNormalDestructor
  {
    public:
      ~GotNormalDestructor() { std::wcout << L"~GotNormalDestructor(). this=0x" << std::hex << this << L"\n"; }
  };

  class GotVirtualDestructor
  {
    public:
      virtual ~GotVirtualDestructor() { std::wcout << L"~GotVirtualDestructor(). this=0x" << std::hex << this << L"\n"; }
  };

  template <typename T>
  static void create_destruct_delete(wchar_t const name[])
  {
    std::wcout << L"create_destruct_delete<" << name << L">()\n";
    {
      T t;
      std::wcout << L"Destructing auto " << name << L" explicitly.\n";
      t.~T();
      std::wcout << L"Finished destructing " << name << L" explicitly.\n";
      std::wcout << name << L" going out of scope.\n";
    }
    std::wcout << L"Finished " << name << L" going out of scope.\n";
    std::wcout << L"\n";
  }

  template <typename T>
  static void new_destruct_delete(wchar_t const name[])
  {
    std::wcout << L"new_destruct_delete<" << name << L">()\n";
    T *t = new T;
    std::wcout << L"Destructing new " << name << L" explicitly.\n";
    t->~T();
    std::wcout << L"Finished destructing new " << name << L" explicitly.\n";
    std::wcout << L"Deleting " << name << L".\n";
    delete t;
    std::wcout << L"Finished deleting " << name << L".\n";
    std::wcout << L"\n";
  }

  static void test_destructor()
  {
    {
      std::wcout << L"\n===auto normal destructor variable===\n";
      GotNormalDestructor got_normal;
    }

    {
      std::wcout << L"\n===auto virtual destructor variable===\n";
      GotVirtualDestructor got_virtual;
    }

    {
      std::wcout << L"\n===new variables===\n";
      new_destruct_delete<GotNormalDestructor>(L"GotNormalDestructor");
      new_destruct_delete<GotVirtualDestructor>(L"GotVirtualDestructor"); 
    }

    {
      std::wcout << L"\n===auto variables===\n";
      create_destruct_delete<GotNormalDestructor>(L"GotNormalDestructor");
      create_destruct_delete<GotVirtualDestructor>(L"GotVirtualDestructor");
    }

    std::wcout << std::endl;
  }
}

int main(int argc, char *argv[])
{
  test::test_destructor();

  return 0;
}

Пример вывода

===auto normal destructor variable===
~GotNormalDestructor(). this=0x0x23fe1f

===auto virtual destructor variable===
~GotVirtualDestructor(). this=0x0x23fe10

===new variables===
new_destruct_delete<GotNormalDestructor>()
Destructing new GotNormalDestructor explicitly.
~GotNormalDestructor(). this=0x0x526700
Finished destructing new GotNormalDestructor explicitly.
Deleting GotNormalDestructor.
~GotNormalDestructor(). this=0x0x526700
Finished deleting GotNormalDestructor.

new_destruct_delete<GotVirtualDestructor>()
Destructing new GotVirtualDestructor explicitly.
~GotVirtualDestructor(). this=0x0x526700
Finished destructing new GotVirtualDestructor explicitly.
Deleting GotVirtualDestructor.
~GotVirtualDestructor(). this=0x0x526700
Finished deleting GotVirtualDestructor.


===auto variables===
create_destruct_delete<GotNormalDestructor>()
Destructing auto GotNormalDestructor explicitly.
~GotNormalDestructor(). this=0x0x23fdcf
Finished destructing GotNormalDestructor explicitly.
GotNormalDestructor going out of scope.
~GotNormalDestructor(). this=0x0x23fdcf
Finished GotNormalDestructor going out of scope.

create_destruct_delete<GotVirtualDestructor>()
Destructing auto GotVirtualDestructor explicitly.
~GotVirtualDestructor(). this=0x0x23fdc0
Finished destructing GotVirtualDestructor explicitly.
GotVirtualDestructor going out of scope.
~GotVirtualDestructor(). this=0x0x23fdc0
Finished GotVirtualDestructor going out of scope.