Повторные вызовы деструктора и отслеживание маркеров в C ++ / CLI

Я играю с C ++ / CLI, используя документацию MSDN иСтандарт ECMAи Visual C ++ Express 2010. Меня поразил следующий отход от C ++:

For ref classes, both the finalizer and destructor must be written so they can be executed multiple times and on objects that have not been fully constructed.

Я придумал маленький пример:

#include <iostream>

ref struct Foo
{
    Foo()  { std::wcout << L"Foo()\n"; }
    ~Foo() { std::wcout << L"~Foo()\n"; this->!Foo(); }
    !Foo() { std::wcout << L"!Foo()\n"; }
};

int main()
{
    Foo ^ r;

    {
        Foo x;
        r = %x;
    }              // #1

    delete r;      // #2
}

В конце блока в#1, автоматическая переменнаяxумирает, и вызывается деструктор (который, в свою очередь, вызывает финализатор явно, как это принято в обычной идиоме). Это все хорошо и хорошо. Но потом я снова удаляю объект по ссылкеr! Вывод такой:

Foo()
~Foo()
!Foo()
~Foo()
!Foo()

Questions:

Is it undefined behavior, or is it entirely acceptable, to call delete r on line #2?

If we remove line #2, does it matter that r is still a tracking handle for an object that (in the sense of C++) no longer exists? Is it a "dangling handle"? Does its reference counting entail that there will be an attempted double deletion?

I know that there isn't an actual double deletion, as the output becomes this:

Foo()
~Foo()
!Foo()

However, I'm not sure whether that's a happy accident or guaranteed to be well-defined behaviour.

Under which other circumstances can the destructor of a managed object be called more than once?

Would it be OK to insert x.~Foo(); immediately before or after r = %x;?

Другими словами, управляемые объекты «живут вечно» и могут ли их деструкторы и их финализаторы вызываться снова и снова?

В ответ на требование @ Hans к нетривиальному классу вы также можете рассмотреть эту версию (с деструктором и финализатором, выполненными в соответствии с требованием множественного вызова):

ref struct Foo
{
    Foo()
    : p(new int[10])
    , a(gcnew cli::array<int>(10))
    {
        std::wcout << L"Foo()\n";
    }

    ~Foo()
    {
        delete a;
        a = nullptr;

        std::wcout << L"~Foo()\n";
        this->!Foo();
    }

    !Foo()
    {
        delete [] p;
        p = nullptr;

        std::wcout << L"!Foo()\n";
    }

private:
    int             * p;
    cli::array<int> ^ a;
};

Ответы на вопрос(2)

Ваш ответ на вопрос