Gewusst wie: Definieren und Verarbeiten von Klassen und Strukturen (C++/CLI)

In diesem Artikel, wie benutzerdefinierte Werttypen und Verweistypen in C++/CLI definiert und nutzt.

Objektinstanziierung

Typen und Werttypen des Bezugs (ref) können auf dem verwalteten Heap, nicht auf dem Stapel oder auf dem systemeigenen Heap nur instanziiert werden.

// mcppv2_ref_class2.cpp
// compile with: /clr
ref class MyClass {
public:
   int i;

   // nested class
   ref class MyClass2 {
   public:
      int i;
   };

   // nested interface
   interface struct MyInterface {
      void f();
   };
};

ref class MyClass2 : public MyClass::MyInterface {
public:
   virtual void f() {
      System::Console::WriteLine("test");
   }
};

public value struct MyStruct {
   void f() {
      System::Console::WriteLine("test");
   }   
};

int main() {
   // instantiate ref type on garbage-collected heap
   MyClass ^ p_MyClass = gcnew MyClass;
   p_MyClass -> i = 4;

   // instantiate value type on garbage-collected heap
   MyStruct ^ p_MyStruct = gcnew MyStruct;
   p_MyStruct -> f();

   // instantiate value type on the stack
   MyStruct p_MyStruct2;
   p_MyStruct2.f();

   // instantiate nested ref type on garbage-collected heap
   MyClass::MyClass2 ^ p_MyClass2 = gcnew MyClass::MyClass2;
   p_MyClass2 -> i = 5;
}

Implizit abstrakte Klassen

Eine implizit abstrakte Klasse kann nicht instanziiert werden.Eine Klasse ist abstrakt implizit, wenn der Basistyp der Klasse eine Schnittstelle ist und die Klasse nicht die Memberfunktionen aller - Schnittstelle implementiert.

Wenn es nicht möglich ist, Objekte von einer Klasse erstellen, die von einer Schnittstelle abgeleitet wird, könnte der Grund dafür, dass die Klasse implizit abstrakt ist.Weitere Informationen zu abstrakten Klassen, finden Sie unter Zusammenfassung.

Im folgenden Codebeispiel wird veranschaulicht, dass die MyClass-Klasse nicht instanziiert werden kann, da Funktion MyClass::func2 nicht implementiert wird.Um das Beispiel zu ermöglichen zu kompilieren, heben Sie MyClass::func2 aus.

// mcppv2_ref_class5.cpp
// compile with: /clr
interface struct MyInterface {
   void func1();
   void func2();
};

ref class MyClass : public MyInterface {
public:
   void func1(){}
   // void func2(){}
};

int main() {
   MyClass ^ h_MyClass = gcnew MyClass;   // C2259 
                                          // To resolve, uncomment MyClass::func2.
}

Geben Sie ein Sichtbarkeit

Sie können die Sichtbarkeit von CLR-Typen (Common Language Runtime) steuern, damit, wenn auf eine Assembly verwiesen wird, in die Assembly kann außerhalb der Assembly sichtbar oder nicht sichtbar sein können.

public gibt an, dass ein Typ zu jeder Quelldatei sichtbar ist, die #using-Direktiven für die Assembly enthält, die den Typ enthält.private gibt an, dass ein Typ nicht in den Quelldateien sichtbar ist, die #using-Direktiven für die Assembly enthalten, die den Typ enthält.Allerdings sind private Typen innerhalb der gleichen Assembly sichtbar.Standardmäßig ist die Sichtbarkeit für eine Klasse private.

Standardmäßig vor Visual C++ 2005, gibt systemeigene hatte öffentliche Zugriffsmöglichkeiten außerhalb der Assembly ein.Aktivieren Sie Compilerwarnung (Stufe 1) C4692, die Ihnen helfen, zu finden, wo private systemeigene Typen falsch verwendet werden.Verwenden Sie das - Pragma, um make_public öffentliche Zugriffe auf einen systemeigenen zu geben in einer Quellcodedatei, die Sie nicht ändern können.

Weitere Informationen finden Sie unter #using Direktiven (C++).

Das folgende Beispiel zeigt, wie Typen deklariert und die zugehörigen Barrierefreiheit angibt und dann auf diese Typen in der Assembly zugreift.Natürlich wenn eine Assembly, die private Typen verfügt, verwiesen wird, indem #using verwendet, nur öffentliche Typen in die Assembly sichtbar sind.

// type_visibility.cpp
// compile with: /clr
using namespace System;
// public type, visible inside and outside assembly
public ref struct Public_Class {
   void Test(){Console::WriteLine("in Public_Class");}
};

// private type, visible inside but not outside assembly
private ref struct Private_Class {
   void Test(){Console::WriteLine("in Private_Class");}
};

// default accessibility is private
ref class Private_Class_2 {
public:
   void Test(){Console::WriteLine("in Private_Class_2");}
};

int main() {
   Public_Class ^ a = gcnew Public_Class;
   a->Test();

   Private_Class ^ b = gcnew Private_Class;
   b->Test();

   Private_Class_2 ^ c = gcnew Private_Class_2;
   c->Test();
}

Ausgabe

  

Jetzt müssen wir das vorherige Beispiel neu schreiben, damit es als DLL erstellt wird.

// type_visibility_2.cpp
// compile with: /clr /LD
using namespace System;
// public type, visible inside and outside the assembly
public ref struct Public_Class {
   void Test(){Console::WriteLine("in Public_Class");}
};

// private type, visible inside but not outside the assembly
private ref struct Private_Class {
   void Test(){Console::WriteLine("in Private_Class");}
};

// by default, accessibility is private
ref class Private_Class_2 {
public:
   void Test(){Console::WriteLine("in Private_Class_2");}
};

Das folgende Beispiel zeigt wie zu den Zugriffstypen außerhalb der Assembly.In diesem Beispiel wird der Client die Komponente, die das vorherige Beispiel integriert ist.

// type_visibility_3.cpp
// compile with: /clr
#using "type_visibility_2.dll"
int main() {
   Public_Class ^ a = gcnew Public_Class;
   a->Test();

   // private types not accessible outside the assembly
   // Private_Class ^ b = gcnew Private_Class;
   // Private_Class_2 ^ c = gcnew Private_Class_2;
}

Ausgabe

  

Membersichtbarkeit

Sie können den Zugriff auf einen Member einer öffentlichen Klasse aus derselben Assembly eindeutig werden als Zugriff darauf von außerhalb der Assembly, indem Sie die Paare der Zugriffsspezifizierer public, protected und private verwenden

In dieser Tabelle wird der Auswirkungen der verschiedenen Zugriffsspezifizierer zusammengefasst:

Bezeichner

Effect

public

Member ist leicht innerhalb und außerhalb der Assembly.Weitere Informationen finden Sie unter öffentliche (C++).

private

Member ist nicht, weder noch innerhalb außerhalb der Assembly zugegriffen.Weitere Informationen finden Sie unter privat (C++).

protected

Member ist leicht innerhalb und außerhalb der Assembly, aber nur an abgeleitete Typen.Weitere Informationen finden Sie unter geschützt (C++).

internal

Member ist öffentlich innerhalb der Assembly jedoch außerhalb der Assembly privat.internal ist ein kontextbezogenes Schlüsselwort.Weitere Informationen finden Sie unter Kontextbezogene Schlüsselwörter.

public protected
-or-
protected public

Member ist, öffentlich innerhalb der Assembly jedoch geschützt außerhalb der Assembly.

private protected
-or-
protected private

Member wird innerhalb der Assembly jedoch privat außerhalb der Assembly geschützt.

Das folgende Beispiel zeigt einen öffentlichen Typ an, der Member verfügt, die mit den verschiedenen Barrierefreiheiten deklariert und dann das Zugreifen auf dieser Member aus der Assembly heraus anzeigt.

// type_member_visibility.cpp
// compile with: /clr
using namespace System;
// public type, visible inside and outside the assembly
public ref class Public_Class {
public:
   void Public_Function(){System::Console::WriteLine("in Public_Function");}

private:
   void Private_Function(){System::Console::WriteLine("in Private_Function");}

protected:
   void Protected_Function(){System::Console::WriteLine("in Protected_Function");}

internal:
   void Internal_Function(){System::Console::WriteLine("in Internal_Function");}

protected public:
   void Protected_Public_Function(){System::Console::WriteLine("in Protected_Public_Function");}

public protected:
   void Public_Protected_Function(){System::Console::WriteLine("in Public_Protected_Function");}

private protected:
   void Private_Protected_Function(){System::Console::WriteLine("in Private_Protected_Function");}

protected private:
   void Protected_Private_Function(){System::Console::WriteLine("in Protected_Private_Function");}
};

// a derived type, calls protected functions
ref struct MyClass : public Public_Class {
   void Test() {
      Console::WriteLine("=======================");
      Console::WriteLine("in function of derived class");
      Protected_Function();
      Protected_Private_Function();
      Private_Protected_Function();
      Console::WriteLine("exiting function of derived class");
      Console::WriteLine("=======================");
   }
};

int main() {
   Public_Class ^ a = gcnew Public_Class;
   MyClass ^ b = gcnew MyClass;
   a->Public_Function();
   a->Protected_Public_Function();
   a->Public_Protected_Function();

   // accessible inside but not outside the assembly
   a->Internal_Function();

   // call protected functions
   b->Test();

   // not accessible inside or outside the assembly
   // a->Private_Function();
}

Ausgabe

  

Fügen wir nun das vorherige Beispiel als DLL erstellen.

// type_member_visibility_2.cpp
// compile with: /clr /LD
using namespace System;
// public type, visible inside and outside the assembly
public ref class Public_Class {
public:
   void Public_Function(){System::Console::WriteLine("in Public_Function");}

private:
   void Private_Function(){System::Console::WriteLine("in Private_Function");}

protected:
   void Protected_Function(){System::Console::WriteLine("in Protected_Function");}

internal:
   void Internal_Function(){System::Console::WriteLine("in Internal_Function");}

protected public:
   void Protected_Public_Function(){System::Console::WriteLine("in Protected_Public_Function");}

public protected:
   void Public_Protected_Function(){System::Console::WriteLine("in Public_Protected_Function");}

private protected:
   void Private_Protected_Function(){System::Console::WriteLine("in Private_Protected_Function");}

protected private:
   void Protected_Private_Function(){System::Console::WriteLine("in Protected_Private_Function");}
};

// a derived type, calls protected functions
ref struct MyClass : public Public_Class {
   void Test() {
      Console::WriteLine("=======================");
      Console::WriteLine("in function of derived class");
      Protected_Function();
      Protected_Private_Function();
      Private_Protected_Function();
      Console::WriteLine("exiting function of derived class");
      Console::WriteLine("=======================");
   }
};

Im folgenden Beispiel wird die Komponente, die im vorherigen Beispiel erstellt wurde, und so die zeigt, wie die Member von außerhalb der Assembly verweist.

// type_member_visibility_3.cpp
// compile with: /clr
#using "type_member_visibility_2.dll"
using namespace System;
// a derived type, calls protected functions
ref struct MyClass : public Public_Class {
   void Test() {
      Console::WriteLine("=======================");
      Console::WriteLine("in function of derived class");
      Protected_Function();
      Protected_Public_Function();
      Public_Protected_Function();
      Console::WriteLine("exiting function of derived class");
      Console::WriteLine("=======================");
   }
};

int main() {
   Public_Class ^ a = gcnew Public_Class;
   MyClass ^ b = gcnew MyClass;
   a->Public_Function();

   // call protected functions
   b->Test();

   // can't be called outside the assembly
   // a->Private_Function();
   // a->Internal_Function();   
   // a->Protected_Private_Function();
   // a->Private_Protected_Function();
}

Ausgabe

  

Öffentliche und private systemeigene Klassen

Ein systemeigener Typ kann von einem verwalteten Typ verwiesen werden.Beispielsweise kann eine Funktion in einem verwalteten Typ einen - Parameter akzeptieren, dessen Typ eine systemeigene Struktur ist.Wenn der verwaltete Typ und Funktion in einer Assembly öffentlich sind, muss der systemeigene Typ öffentlich auch sein.

// mcppv2_ref_class3.h
// native type
public struct N {
   N(){}
   int i;
};

Danach erstellen Sie die Quellcodedatei, die den systemeigenen Typ verwendet:

// mcppv2_ref_class3.cpp
// compile with: /clr /LD
#include "mcppv2_ref_class3.h"
// public managed type
public ref struct R {
   // public function that takes a native type
   void f(N nn) {}
};

Jetzt kompilieren Sie einen Client:

// mcppv2_ref_class4.cpp
// compile with: /clr
#using "mcppv2_ref_class3.dll"

#include "mcppv2_ref_class3.h"

int main() {
   R ^r = gcnew R;
   N n;
   r->f(n);
}

Statische Konstruktoren

Ein CLR Typ – z. B. eine Klasse oder Struktur-kann einen statischen Konstruktor verfügen, der verwendet werden kann, um statischer Datenmember zu initialisieren.Ein statischer Konstruktor wird höchstens einmal aufgerufen und wird aufgerufen, bevor auf einen statischen Member des Typs das erste Mal zugegriffen wird.

Ein Instanzkonstruktor wird immer einen statischen Konstruktor nach.

Der Compiler kann nicht inline ein Aufruf von einem Konstruktor, wenn die Klasse einen statischen Konstruktor verfügt.Der Compiler kann nicht inline ein Aufruf der Memberfunktionen, wenn die Klasse ein Werttyp ist, enthält einen statischen Konstruktor und verfügt über keinen Instanzkonstruktor.Die CLR kann inline der Aufruf, der Compiler kann jedoch nicht.

Definieren Sie einen statischen Konstruktor als private Memberfunktion, da es soll, nur von der CLR aufgerufen werden.

Weitere Informationen zu statischen Konstruktoren, finden Sie unter Gewusst wie: Definieren eines statischen Schnittstellenkonstruktors (C++/CLI).

// mcppv2_ref_class6.cpp
// compile with: /clr
using namespace System;

ref class MyClass {
private:
   static int i = 0;

   static MyClass() {
      Console::WriteLine("in static constructor");
      i = 9;
   }

public:
   static void Test() {
      i++;
      Console::WriteLine(i);
   }
};

int main() {
   MyClass::Test();
   MyClass::Test();
}

Ausgabe

  

Semantik des Zeigers

Wenn Sie Visual C++ verwenden, um Typen zu definieren, wird der this Zeiger in einem Referenztyp vom Typ "Handle".Der this Zeiger in einen Werttyp ist vom Typ "innerer Zeiger".

Dies kann andere Semantik des Zeigers this unerwartetes Verhalten verursachen, wenn ein Standardindexer aufgerufen wird.Im folgenden Beispiel wird die richtige Methode an, auf einen Standardindexer in einem Referenz-Typ und in einem Werttyp zuzugreifen.

Weitere Informationen finden Sie unter

// semantics_of_this_pointer.cpp
// compile with: /clr
using namespace System;

ref struct A {
   property Double default[Double] {
      Double get(Double data) {
         return data*data;
      }
   }

   A() {
      // accessing default indexer
      Console::WriteLine("{0}", this[3.3]);
   }
};

value struct B {
   property Double default[Double] {
      Double get(Double data) {
         return data*data;
      }
   }
   void Test() {
      // accessing default indexer
      Console::WriteLine("{0}", this->default[3.3]);
   }
};

int main() {
   A ^ mya = gcnew A();
   B ^ myb = gcnew B();
   myb->Test();
}

Ausgabe

  

HIDE-durch-Signaturfunktionen

In Standard-C++ wird eine Funktion in einer Basisklasse durch eine Funktion ausgeblendet, die den gleichen Namen in einer abgeleiteten Klasse verfügt, auch wenn die abgeleitete Klasse nicht dieselbe Anzahl oder Art von Parametern verfügt.Dies wird als HIDE-durchNamesemantik.In einem Referenztyp kann eine Funktion in einer Basisklasse durch eine Funktion in einer abgeleiteten Klasse nur ausgeblendet werden, wenn der Name und die Parameterliste identisch sind.Dies wird als HIDE-durchSignatursemantik.

Eine Klasse wird als eine HIDE-durchSignaturklasse, wenn alle zugehörigen Funktionen in den Metadaten als hidebysig gekennzeichnet werden.Standardmäßig haben alle Klassen, die unter /clr erstellt werden, hidebysig-Funktionen.Allerdings hat eine Klasse, die kompiliert wird, indem /clr:oldSyntax verwendet, nicht hidebysig-Funktionen; stattdessen sind sie HIDE-durchNamefunktionen.Wenn eine Klasse hidebysig-Funktionen verfügt, blendet der Compiler keine Funktionen Namen in einem direkte Basisklassen aus, aber, wenn der Compiler eine HIDE-durchNameklasse in einer Vererbungskette trifft, wird diesem HIDE-durchNameverhalten fort.

Die HIDE-durchSignatursemantik wenn eine Funktion für ein Objekt aufgerufen wird, identifiziert der Compiler die meisten abgeleitete Klasse, die eine Funktion enthält, die den Funktionsaufruf erfüllen kann.Wenn nur eine Funktion in der - Klasse gibt, die den Aufruf erfüllen kann, die Compileraufrufe, die arbeiten.Wenn mehr als eine Funktion in der - Klasse gibt, die den Aufruf erfüllen kann, verwendet der Compiler Überladungsauflösungsregeln zu bestimmen, die arbeiten, um aufzurufen.Weitere Informationen zu Überladungsregeln, finden Sie unter Funktionsüberladung.

Für einen angegebenen Funktionsaufruf kann eine Funktion möglicherweise in einer Basisklasse eine Signatur, die es eine etwas bessere Übereinstimmung als eine Funktion in einer abgeleiteten Klasse macht.Wenn die Funktion explizit für ein Objekt der abgeleiteten Klasse aufgerufen wurde, wird die Funktion in der abgeleiteten Klasse aufgerufen.

Da den Rückgabewert nicht als Teil der Signatur einer Funktion gilt, wird eine Basisklassenfunktion ausgeblendet, wenn sie denselben Namen aufweist und dieselbe Anzahl und Art von Argumente wie eine Funktion der abgeleiteten Klasse akzeptiert, wenn sie im Typ des Rückgabewerts unterscheidet.

Das folgende Beispiel zeigt, dass eine Funktion in einer Basisklasse nicht durch eine Funktion in einer abgeleiteten Klasse verdeckt wird.

// hide_by_signature_1.cpp
// compile with: /clr
using namespace System;
ref struct Base {
   void Test() { 
      Console::WriteLine("Base::Test"); 
   }
};

ref struct Derived : public Base {
   void Test(int i) { 
      Console::WriteLine("Derived::Test"); 
   }
};

int main() {
   Derived ^ t = gcnew Derived;
   // Test() in the base class will not be hidden
   t->Test();
}

Ausgabe

  

Das folgende Beispiel zeigt, dass der Visual C++-Compiler eine Funktion im abgeleiteten Klasse-gleichmäßigen aufruft, wenn eine Konvertierung erforderlich ist, um eine oder mehrere von entspricht Parameter- und eine Funktion in einer Basisklasse nicht aufzurufen, die eine bessere Übereinstimmung für den Funktionsaufruf ist.

// hide_by_signature_2.cpp
// compile with: /clr
using namespace System;
ref struct Base {
   void Test2(Single d) { 
      Console::WriteLine("Base::Test2"); 
   }
};

ref struct Derived : public Base {
   void Test2(Double f) { 
      Console::WriteLine("Derived::Test2"); 
   }
};

int main() {
   Derived ^ t = gcnew Derived;
   // Base::Test2 is a better match, but the compiler
   // calls a function in the derived class if possible
   t->Test2(3.14f);
}

Ausgabe

  

Das folgende Beispiel zeigt, dass es möglich ist, eine Funktion auszublenden, selbst wenn die Basisklasse die gleiche Signatur wie die abgeleitete Klasse.

// hide_by_signature_3.cpp
// compile with: /clr
using namespace System;
ref struct Base {
   int Test4() { 
      Console::WriteLine("Base::Test4"); 
      return 9; 
   }
};

ref struct Derived : public Base {
   char Test4() { 
      Console::WriteLine("Derived::Test4"); 
      return 'a'; 
   }
};

int main() {
   Derived ^ t = gcnew Derived;

   // Base::Test4 is hidden
   int i = t->Test4();
   Console::WriteLine(i);
}

Ausgabe

  

Im folgenden Beispiel wird eine Komponente, die kompiliert wird, indem /clr:oldSyntax verwendet.Klassen, die definiert werden, indem Managed Extensions for C++ verwendet wird, sind HIDE-durchNamememberfunktionen.

// hide_by_signature_4.cpp
// compile with: /clr:oldSyntax /LD
using namespace System;
public __gc struct Base0 {
   void Test() { 
      Console::WriteLine("in Base0::Test");
   }
};

public __gc struct Base1 : public Base0 {
   void Test(int i) { 
      Console::WriteLine("in Base1::Test");
   }
};

Im folgenden Beispiel wird die Komponente, die das vorherige Beispiel integriert ist.Beachten Sie, wie HIDE-durchSignaturfunktionalität nicht den Basisklassen von Typen angewendet wird, die kompiliert werden, indem /clr:oldSyntax verwendet.

// hide_by_signature_5.cpp
// compile with: /clr:oldSyntax /LD
// compile with: /clr
using namespace System;
#using "hide_by_signature_4.dll"

ref struct Derived : public Base1 {
   void Test(int i, int j) { 
      Console::WriteLine("Derived::Test");
   }
};

int main() {
   Derived ^ t = gcnew Derived;
   t->Test(8, 8);   // OK
   t->Test(8);   // OK
   t->Test();   // C2661
}

Kopierkonstruktoren

Im C++-Standard besagt, dass ein Kopierkonstruktor, wenn ein Objekt verschoben wird, so aufgerufen wird, dass ein Objekt erstellt und an der gleichen Adresse zerstört wird.

Wenn /clr verwendet wird, um zu kompilieren und eine Funktion, die zu MSIL-Aufrufen eine systemeigene Funktion in der ein systemeigenes Klasse- oder mehr kompiliert wird, als Wert übergeben ein-wird und sich die systemeigene Klasse einen Kopierkonstruktor und/oder einen Destruktor verfügt, wird kein Kopierkonstruktor aufgerufen und das Objekt wird an einer anderen Adresse als zerstört, wo es erstellt wurde.Dies kann Probleme verursachen, wenn die Klasse einen Zeiger in sich hat oder wenn der Code Objekte durch Adresse nachverfolgt.

Weitere Informationen finden Sie unter /clr (Common Language Runtime-Kompilierung).

Im folgenden Beispiel wird veranschaulicht, wann ein Kopierkonstruktor nicht generiert wird.

// breaking_change_no_copy_ctor.cpp
// compile with: /clr
#include<stdio.h>

struct S {
   int i;
   static int n;

   S() : i(n++) { 
      printf_s("S object %d being constructed, this=%p\n", i, this); 
   }

   S(S const& rhs) : i(n++) { 
      printf_s("S object %d being copy constructed from S object "
               "%d, this=%p\n", i, rhs.i, this); 
   }

   ~S() {
      printf_s("S object %d being destroyed, this=%p\n", i, this); 
   }
};

int S::n = 0;

#pragma managed(push,off)
void f(S s1, S s2) {
   printf_s("in function f\n");
}
#pragma managed(pop)

int main() {
   S s;
   S t;
   f(s,t);
}

Ausgabe

  

Destruktoren und Finalizer

Destruktoren in einem Referenztyp führen eine deterministische Bereinigung von Ressourcen aus.Finalizer räumen nicht verwaltete Ressourcen auf und können vom Destruktor oder vom Garbage Collector Nicht deterministisch aufgerufen werden.Informationen zu Destruktoren in Standard-C++, finden Sie unter Destruktoren (C++).

class classname {
   ~classname() {}   // destructor
   ! classname() {}   // finalizer
};

Das Verhalten von Destruktoren in einer verwalteten Visual C++-Klasse unterscheidet sich von Managed Extensions for C++.Weitere Informationen über diese Änderung finden Sie unter Änderungen in der Destruktorsemantik.

Der CLR-GarbageCollector löscht nicht verwendete verwaltete Objekte und gibt den Speicher frei, wenn sie nicht mehr benötigt werden.verwendet jedoch möglicherweise ein Typ Ressourcen, die der Garbage Collector keine freigeben kann.Diese Ressourcen werden als nicht verwaltete Ressourcen (systemeigene Dateihandles, beispielsweise).Es wird empfohlen, alle nicht verwalteten Ressourcen im Finalizer freigeben.Da verwaltete Ressourcen Nicht vom Garbage Collector freigegeben werden, ist es nicht sicher, verwaltete Ressourcen in einem Finalizer zuzugreifen, da es möglich ist, dass der Garbage Collector bereits diese verwaltete Ressource bereinigt verfügt.

Ein Visual C++-Finalizer ist nicht um die Finalize-Methode.(CLR-Dokumentation verwendet Finalizer und die Finalize-Methode synonym).Die Finalize-Methode wird vom Garbage Collector aufgerufen, der jeden Finalizer in einer Klassenvererbungskette aufruft.Anders als Visual C++-Destruktoren wird ein Finalizeraufruf der abgeleiteten Klasse nicht den Compiler, den Finalizer in allen Basisklassen aufzurufen.

Da der Visual C++-Compiler deterministische Freigabe von Ressourcen unterstützt, versuchen Sie, die nicht Dispose oder Finalize-Methoden zu implementieren.Wenn Sie jedoch mit diesen Methoden vertraut sind, zeigt, wie ein Visual C++-Finalizer und ein Destruktor, der die Finalizerzuordnung zum Dispose Muster aufruft:

// Visual C++ code
ref class T {
   ~T() { this->!T(); }   // destructor calls finalizer
   !T() {}   // finalizer
};

// equivalent to the Dispose pattern
void Dispose(bool disposing) {
   if (disposing) {
      ~T();
   } else {
      !T();
   }
}

Ein verwalteter Typ verwendet möglicherweise auch verwaltete Ressourcen, die Sie lieber, deterministisch freizugeben, und lassen Sie nicht zum Nicht zu einem bestimmten Zeitpunkt nach dem Objekt freizugeben Garbage Collector, wird nicht mehr benötigt.Die deterministische Freigabe von Ressourcen kann die Leistung erheblich verbessern.

Der Visual C++-Compiler ermöglicht die Definition eines Destruktors, um deterministisch bereinigt werden.Verwenden Sie den Destruktor, um alle Ressourcen freizugeben, die Sie deterministisch freigeben möchten.Wenn ein Finalizer vorhanden ist, rufen Sie ihn im Destruktor auf, um von Codeduplikaten.

// destructors_finalizers_1.cpp
// compile with: /clr /c
ref struct A {
   // destructor cleans up all resources
   ~A() {
      // clean up code to release managed resource
      // ...
      // to avoid code duplication, 
      // call finalizer to release unmanaged resources
      this->!A();
   }

   // finalizer cleans up unmanaged resources
   // destructor or garbage collector will
   // clean up managed resources
   !A() {
      // clean up code to release unmanaged resources
      // ...
   }
};

Wenn der Code, der den Typ nutzt, nicht den Destruktor aufgerufen wird, gibt der Garbage Collector schließlich alle verwalteten Ressourcen frei.

Das Vorhandensein eines Destruktors bedeutet nicht das Vorhandensein eines Finalizers.Allerdings bedeutet das Vorhandensein eines Finalizers, dass Sie einen Destruktor definieren und den Finalizer von diesem Destruktor aufrufen müssen.Dadurch wird für die deterministische Version von nicht verwalteten Ressourcen bereit.

Der Destruktor unterdrücken-durch die Anwendung von SuppressFinalize aufrufen - Abschluss des Objekts.Wenn der Destruktor nicht aufgerufen wird, wird der Finalizer des Typs schließlich vom Garbage Collector aufgerufen.

Die Ressourcen des Objekts, indem Sie den Destruktor deterministisch bereinigen aufruft, kann die Leistung verbessern, die mit dem Übernehmen der CLR abschließen verglichen wird, Nicht das Objekt.

Code, der in Visual C++ geschrieben und kompiliert, mithilfe von /clr, den Destruktor eines Typs ausführt, wenn:

Wenn der Typ von einem Client verwendet wird, der in einer anderen Sprache geschrieben wurde, wird der Destruktor aufgerufen, wie folgt:

  • Auf einem Aufruf Dispose.

  • Auf einem Aufruf Dispose(void) für den Typ.

  • Wenn der Typ vom Bereich in einer Anweisung C# using verlässt.

Wenn Sie ein Objekt eines Referenztyps auf dem verwalteten Heap (nicht mit der Stapelsemantik für Referenztypen) erstellen, verwenden Sie die try-finally-Syntax, um sicherzustellen, dass eine Ausnahme den Destruktor nicht am Ausführen hindert.

// clr_destructors.cpp
// compile with: /clr
ref struct A {
   ~A() {}
};

int main() {
   A ^ MyA = gcnew A;
   try {
      // use MyA
   }
   finally {
      delete MyA;
   }
}

Wenn der Typ einen Destruktor enthält, generiert der Compiler eine Dispose-Methode, die IDisposable implementiert.Wenn ein Typ, der in Visual C++ geschrieben und einen Destruktor enthält, der in einer anderen Sprache verwendet wird, IDisposable::Dispose auf diesem Aufrufen der Typursachen aufgerufen werden, Destruktor des Typs.Wenn der Typ von einem Visual C++-Client verwendet wird, können Sie Dispose nicht direkt aufrufen, Rufen Sie stattdessen den Destruktor auf, indem Sie den delete-Operator verwenden.

Wenn der Typ einen Finalizer hat, generiert der Compiler eine Finalize(void)-Methode, die Finalize überschreibt.

Wenn ein Typ entweder einen Finalizer oder einen Destruktor enthält, generiert der Compiler eine Dispose(bool)-Methode, entsprechend dem Entwurfsmuster.(Weitere Informationen finden Sie unter, Implementing Finalize and Dispose to Clean Up Unmanaged Resources).Sie können Dispose(bool) in Visual C++ nicht explizit erstellen oder aufrufen.

Wenn ein Typ eine Basisklasse hat, die das Entwurfsmuster entspricht, werden die Destruktoren für alle Basisklassen aufgerufen, wenn der Destruktor für die abgeleitete Klasse aufgerufen wird.(Wenn der Typ in Visual C++ geschrieben ist, stellt der Compiler, dass die Typen dieses Muster implementieren). Das heißt, wird der Destruktor Ketten von einer Verweisklasse den Basen und Member, die durch das angegeben C++-Standard-erste der Destruktor der - Klasse, dann die Destruktoren für Mitglieder den umgekehrten der Reihenfolge, in der sie erstellt wurden, und schließlich die Destruktoren für ihren Basisklassen in den umgekehrten der Reihenfolge ausgeführt, in der sie erstellt wurden.

Destruktoren und Finalizer werden keine inneren Werttypen oder Schnittstellen ermöglicht.

Ein Finalizer kann in einem Referenztyp nur definiert werden oder deklariert werden.Wie ein Konstruktor und ein Destruktor verfügt ein Finalizer keinen Rückgabetyp.

Nachdem der Finalizer eines Objekts ausgeführt wird, werden die Finalizer in allen Basisklassen auch aufgerufen und beginnen mit dem geringsten abgeleiteten Typ.Finalizer für Datenmember werden nicht automatisch in den Finalizer einer Klasse verkettet.

Wenn ein Finalizer einen systemeigenen Zeiger in einem verwalteten Typ löschen, müssen Sie sicherstellen, dass Verweise oder durch in systemeigenen Zeiger nicht vorzeitig erfasst werden; Rufen Sie den Destruktor auf dem verwalteten Typ auf, KeepAlive, anstatt zu verwenden.

Zur Kompilierzeit können Sie feststellen, ob ein Typ einen Finalizer oder einen Destruktor enthält.Weitere Informationen finden Sie unter Compiler-Unterstützung für Typ-Merkmale.

Im folgenden Beispiel werden zwei Typen, einer, der nicht verwaltete Ressourcen verfügt und einen an, der Ressourcen verwaltet verfügt, die deterministisch freigegeben werden.

// destructors_finalizers_2.cpp
// compile with: /clr
#include <vcclr.h>
#include <stdio.h>
using namespace System;
using namespace System::IO;

ref class SystemFileWriter {
   FileStream ^ file;
   array<Byte> ^ arr;
   int bufLen;

public:
   SystemFileWriter(String ^ name) : file(File::Open(name, FileMode::Append)), 
                                     arr(gcnew array<Byte>(1024)) {}

   void Flush() {
      file->Write(arr, 0, bufLen);
      bufLen = 0;
   }

   ~SystemFileWriter() {
      Flush();
      delete file;
   }
};

ref class CRTFileWriter {
   FILE * file;
   array<Byte> ^ arr;
   int bufLen;

   static FILE * getFile(String ^ n) {
      pin_ptr<const wchar_t> name = PtrToStringChars(n);
      FILE * ret = 0;
      _wfopen_s(&ret, name, L"ab");
      return ret;
   }

public:
   CRTFileWriter(String ^ name) : file(getFile(name)), arr(gcnew array<Byte>(1024) ) {}

   void Flush() {
      pin_ptr<Byte> buf = &arr[0];
      fwrite(buf, 1, bufLen, file);
      bufLen = 0;
   }

   ~CRTFileWriter() {
      this->!CRTFileWriter();
   }

   !CRTFileWriter() {
      Flush();
      fclose(file);
   }
};

int main() {
   SystemFileWriter w("systest.txt");
   CRTFileWriter ^ w2 = gcnew CRTFileWriter("crttest.txt");
}

Siehe auch

Referenz

Classes and Structs (Managed)

Classes and Structs (Managed)