Wiederholte Destruktor-Aufrufe und Tracking-Handles in C ++ / CLI

Ich spiele mit C ++ / CLI herum und benutze die MSDN-Dokumentation und dieECMA-Standardund Visual C ++ Express 2010. Was mir aufgefallen ist, war die folgende Abweichung von C ++:

Für ref-Klassen müssen sowohl der Finalizer als auch der Destruktor geschrieben werden, damit sie mehrmals und für Objekte ausgeführt werden können, die nicht vollständig konstruiert wurden.

Ich habe ein kleines Beispiel erfunden:

#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
}

Am Ende des Blocks um#1, die automatische Variablexstirbt, und der Destruktor wird aufgerufen (der wiederum den Finalizer explizit aufruft, wie es die übliche Redewendung ist). Das ist alles in Ordnung und gut. Aber dann lösche ich das Objekt nochmal über die Referenzr! Die Ausgabe lautet wie folgt:

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

Fragen:

Ist es undefiniertes Verhalten oder ist es völlig akzeptabel, anzurufendelete r online#2?

Wenn wir die Leitung entfernen#2Ist es wichtig, dassr ist noch ein Tracking-Handle für ein Objekt, das (im Sinne von C ++) nicht mehr existiert? Ist es ein "baumelnder Griff"? Bedeutet die Referenzzählung, dass versucht wird, zweimal zu löschen?

Ich weiß, dass es keine gibttatsächlich Doppelte Löschung, da die Ausgabe wie folgt lautet:

Foo()
~Foo()
!Foo()

Ich bin mir jedoch nicht sicher, ob das ein glücklicher Zufall ist oder ein genau definiertes Verhalten garantiert.

Unter welchen anderen Umständen kann dasZerstörer von einem verwalteten Objekt mehrmals aufgerufen werden?

Wäre es in Ordnung einzufügenx.~Foo(); unmittelbar davor oder danachr = %x;?

Mit anderen Worten, leben verwaltete Objekte "für immer" und können sowohl ihre Destruktoren als auch ihre Finalizer immer wieder aufgerufen werden?

Als Reaktion auf die Forderung von @ Hans nach einer nicht-trivialen Klasse können Sie auch diese Version in Betracht ziehen (mit Destruktor und Finalizer, die entsprechend der Mehrfachaufruf-Anforderung erstellt wurden):

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;
};

Antworten auf die Frage(2)

Ihre Antwort auf die Frage