Dela via


Anvisningar: Definiera och använda klasser och structs (C++/CLI)

Den här artikeln visar hur du definierar och använder användardefinierade referenstyper och värdetyper i C++/CLI.

Instansiering av objekt

Referenstyper (ref) kan bara instansieras på den hanterade heapen, inte på stacken eller på den interna heapen. Värdetyper kan instansieras i stacken eller den hanterade heapen.

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

Implicit abstrakta klasser

Det går inte att instansiera en implicit abstrakt klass . En klass är implicit abstrakt när:

  • bastypen för klassen är ett gränssnitt, och
  • -klassen implementerar inte alla gränssnitts medlemsfunktioner.

Du kanske inte kan konstruera objekt från en klass som härleds från ett gränssnitt. Anledningen kan vara att klassen är implicit abstrakt. Mer information om abstrakta klasser finns i abstrakt.

Följande kodexempel visar att MyClass klassen inte kan instansieras eftersom funktionen MyClass::func2 inte har implementerats. Om du vill aktivera exemplet för kompilering avkommenterar du MyClass::func2.

// 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.
}

Typens synlighet

Du kan styra synligheten för clr-typer (common language runtime). När sammansättningen refereras styr du om typerna i sammansättningen är synliga eller inte visas utanför sammansättningen.

public anger att en typ är synlig för alla källfiler som innehåller ett #using direktiv för den sammansättning som innehåller typen. private anger att en typ inte är synlig för källfiler som innehåller ett #using direktiv för sammansättningen som innehåller typen. Privata typer visas dock i samma sammansättning. Som standard är synligheten för en klass private.

Som standard före Visual Studio 2005 hade interna typer offentlig tillgänglighet utanför sammansättningen. Aktivera kompilatorvarning (nivå 1) C4692 för att hjälpa dig att se var privata interna typer används felaktigt. Använd make_public pragma för att ge offentlig tillgänglighet till en intern typ i en källkodsfil som du inte kan ändra.

Mer information finns i #using-direktivet.

Följande exempel visar hur du deklarerar typer och anger deras tillgänglighet och sedan får åtkomst till dessa typer i sammansättningen. Om en sammansättning som har privata typer refereras med hjälp #usingav visas endast offentliga typer i sammansättningen.

// 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();
}

Resultat

in Public_Class
in Private_Class
in Private_Class_2

Nu ska vi skriva om det tidigare exemplet så att det skapas som en DLL.

// 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");}
};

Nästa exempel visar hur du kommer åt typer utanför sammansättningen. I det här exemplet använder klienten komponenten som skapades i föregående exempel.

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

Resultat

in Public_Class

Synlighet för medlemmar

Du kan göra åtkomsten till en medlem i en offentlig klass från samma sammansättning annorlunda än åtkomsten till den utanför sammansättningen med hjälp av par av åtkomstspecificerarna public, protectedoch private

Den här tabellen sammanfattar effekten av de olika åtkomstspecificerarna:

Specificerare Effekt
public Medlem är tillgänglig inom och utanför sammansättningen. Mer information finns i public.
private Medlemmen är otillgänglig, både inom och utanför sammansättningen. Mer information finns i private.
protected Medlemmen är tillgänglig både inom och utanför församlingen, men endast för härledda typer. Mer information finns i protected.
internal Medlemmen är offentlig inne i församlingen men privat utanför församlingen. internal är ett kontextkänsligt nyckelord. Mer information finns iContext-Sensitive Nyckelord.
public protected protected public Medlemmen är offentlig i sammansättningen men skyddad utanför sammansättningen.
private protected protected private Medlemmen är skyddad i sammansättningen men privat utanför sammansättningen.

Följande exempel visar en offentlig typ som har medlemmar som deklareras med hjälp av de olika åtkomstspecificerarna. Sedan visas åtkomst till dessa medlemmar inifrån sammansättningen.

// 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();
}

Resultat

in Public_Function
in Protected_Public_Function
in Public_Protected_Function
in Internal_Function
=======================
in function of derived class
in Protected_Function
in Protected_Private_Function
in Private_Protected_Function
exiting function of derived class
=======================

Nu ska vi skapa det tidigare exemplet som en DLL.

// 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("=======================");
   }
};

Följande exempel använder komponenten som skapades i föregående exempel. Den visar hur du kommer åt medlemmarna utanför sammansättningen.

// 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();
}

Resultat

in Public_Function
=======================
in function of derived class
in Protected_Function
in Protected_Public_Function
in Public_Protected_Function
exiting function of derived class
=======================

Offentliga och privata interna klasser

En inbyggd typ kan refereras från en typ som hanteras. En funktion i en hanterad typ kan till exempel ta en parameter vars typ är en intern struct. Om den hanterade typen och funktionen är offentliga i en sammansättning måste den interna typen också vara offentlig.

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

Skapa sedan den källkodsfil som använder den interna typen:

// 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) {}
};

Kompilera nu en klient:

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

#include "mcppv2_ref_class3.h"

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

Statiska konstruktorer

En CLR-typ, till exempel en klass eller struct, kan ha en statisk konstruktor som kan användas för att initiera statiska datamedlemmar. En statisk konstruktor anropas högst en gång och anropas innan någon statisk medlem av typen används första gången.

En instanskonstruktor körs alltid efter en statisk konstruktor.

Kompilatorn kan inte infoga ett anrop till en konstruktor om klassen har en statisk konstruktor. Kompilatorn kan inte infoga ett anrop till någon medlemsfunktion om klassen är en värdetyp, har en statisk konstruktor och inte har någon instanskonstruktor. CLR kan infoga anropet, men kompilatorn kan inte göra det.

Definiera en statisk konstruktor som en privat medlemsfunktion, eftersom den endast är avsedd att anropas av CLR.

Mer information om statiska konstruktorer finns i How to: Define an Interface Static Constructor (C++/CLI) .

// 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();
}

Resultat

in static constructor
10
11

Semantik för pekaren this

När du använder C++/CLI för att definiera typer är pekaren this i en referenstyp av typen handtag. Pekaren this i en värdetyp är av typen inre pekare.

Dessa olika semantik för pekaren this kan orsaka oväntat beteende när en standardindexerare anropas. I nästa exempel visas rätt sätt att komma åt en standardindexerare i både en referenstyp och en värdetyp.

Mer information finns i Hantera till objektoperator (^) och interior_ptr (C++/CLI)

// 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();
}

Resultat

10.89
10.89

Dölj-funktioner baserat på signatur

I standard-C++ döljs en funktion i en basklass av en funktion som har samma namn i en härledd klass, även om funktionen derived-class inte har samma typ eller antal parametrar. Det kallas hide-by-name-semantik . I en referenstyp döljs en funktion i en basklass endast av en funktion i en härledd klass om både namnet och parameterlistan är desamma. Det kallas hide-by-signature-semantik .

En klass anses vara en hide-by-signature-klass när alla dess funktioner markeras i metadata som hidebysig. Som standardinställning har alla klasser som skapas under /clr funktioner för hidebysig. När en klass har hidebysig funktioner döljer kompilatorn inte funktioner efter namn i några direkta basklasser, men om kompilatorn stöter på en hide-by-name-klass i en arvskedja fortsätter beteendet att dölja efter namn.

Under hide-by-signature-semantik identifierar kompilatorn den mest härledda klassen som innehåller en funktion som kan uppfylla funktionsanropet när en funktion anropas för ett objekt. Om det bara finns en funktion i klassen som uppfyller anropet anropar kompilatorn den funktionen. Om det finns fler än en funktion i klassen som kan uppfylla anropet använder kompilatorn regler för överbelastningsmatchning för att avgöra vilken funktion som ska anropas. Mer information om överlagringsregler finns i Funktionsöverlagring.

För ett visst funktionsanrop kan en funktion i en basklass ha en signatur som gör den till en något bättre matchning än en funktion i en härledd klass. Men om funktionen uttryckligen anropades för ett objekt i den härledda klassen anropas funktionen i den härledda klassen.

Eftersom returvärdet inte betraktas som en del av en funktions signatur döljs en basklassfunktion om den har samma namn och har samma typ och antal argument som en härledd klassfunktion, även om den skiljer sig åt i typen av returvärde.

Följande exempel visar att en funktion i en basklass inte döljs av en funktion i en härledd klass.

// 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();
}

Resultat

Base::Test

Nästa exempel visar att Microsoft C++-kompilatorn anropar en funktion i den mest härledda klassen – även om en konvertering krävs för att matcha en eller flera parametrar – och inte anropar en funktion i en basklass som är en bättre matchning för funktionsanropet.

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

Resultat

Derived::Test2

Följande exempel visar att det är möjligt att dölja en funktion även om basklassen har samma signatur som den härledda klassen.

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

Resultat

Derived::Test4
97

Kopiera konstruktorer

C++-standarden säger att en kopieringskonstruktor anropas när ett objekt flyttas, så att ett objekt skapas och förstörs på samma adress.

Men när en funktion som kompileras till MSIL anropar en intern funktion där en intern klass , eller mer än en, skickas av ett värde och där den interna klassen har en kopieringskonstruktor eller en destruktor, anropas ingen kopieringskonstruktor och objektet förstörs på en annan adress än där det skapades. Det här beteendet kan orsaka problem om klassen har en pekare i sig själv, eller om koden spårar objekt efter adress.

Mer information finns i /clr (Common Language Runtime Compilation).

Följande exempel visar när en kopieringskonstruktor inte genereras.

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

Resultat

S object 0 being constructed, this=0018F378
S object 1 being constructed, this=0018F37C
S object 2 being copy constructed from S object 1, this=0018F380
S object 3 being copy constructed from S object 0, this=0018F384
S object 4 being copy constructed from S object 2, this=0018F2E4
S object 2 being destroyed, this=0018F380
S object 5 being copy constructed from S object 3, this=0018F2E0
S object 3 being destroyed, this=0018F384
in function f
S object 5 being destroyed, this=0018F2E0
S object 4 being destroyed, this=0018F2E4
S object 1 being destroyed, this=0018F37C
S object 0 being destroyed, this=0018F378

Destruktorer och finalizers

Destruktorer i en referenstyp utför en deterministisk städning av resurser. Slutförarna rensar ohanterade resurser och kan anropas antingen deterministiskt av destruatorn eller nondeterministiskt av skräpinsamlaren. Information om destructors i standard C++finns i Destructors.

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

CLR-skräpinsamlaren tar bort oanvända hanterade objekt och frigör deras minne när de inte längre behövs. En typ kan dock använda resurser som skräpinsamlaren inte vet hur de ska släppas. Dessa resurser kallas ohanterade resurser (interna filreferenser, till exempel). Vi rekommenderar att du släpper alla ohanterade resurser i slutversionen. Skräpinsamlaren släpper hanterade resurser obestämt, så det är inte säkert att referera till hanterade resurser i en slutversion. Det beror på att det är möjligt att skräpinsamlaren redan har rensat upp dem.

En Visual C++-finalator är inte samma sak som Finalize metoden. (CLR-dokumentationen använder finalizer och Finalize metoden synonymt). Metoden Finalize anropas av skräpinsamlaren, som anropar varje slutförare i en klassarvkedja. Till skillnad från Visual C++ destructors orsakar inte ett finalatoranrop av härledd klass att kompilatorn anropar finaliseraren i alla basklasser.

Eftersom Microsoft C++-kompilatorn stöder deterministisk frisläppning av resurser ska du inte försöka implementera Dispose metoderna eller Finalize . Men om du är bekant med dessa metoder, så här fungerar en Visual C++-finalisator och en destruktor som anropar finalisatorn och mappar till Dispose mönstret:

// 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();
   }
}

En hanterad typ kan också använda hanterade resurser som du föredrar att släppa på ett bestämt sätt. Du kanske inte vill att skräpinsamlaren ska släppa ett objekt obestämt någon gång efter att objektet inte längre krävs. Den deterministiska frigöringen av resurser kan avsevärt förbättra prestandan.

Microsoft C++-kompilatorn gör det möjligt för definitionen av en destructor att deterministiskt rensa objekt. Använd destruktorn för att frigöra alla resurser som du vill släppa deterministiskt. Om det finns en finalizer anropar du den från destruktorn för att undvika kodduplicering.

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

Om koden som använder din typ inte anropar destruktör, frigör avfallssamlaren så småningom alla hanterade resurser.

Förekomsten av en destruktor innebär inte förekomsten av en finalisator. Förekomsten av en finalizer innebär dock att du måste definiera en destruktor och anropa finalizern från den. Det här anropet möjliggör en deterministisk frigöring av icke-hanterade resurser.

Om du anropar destruktor undertrycks finaliseringen av objektet med hjälp av SuppressFinalize. Om destruatorn inte anropas anropas din typs finalator så småningom av skräpinsamlaren.

Du kan förbättra prestandan genom att anropa destruktören för att deterministiskt rensa objektets resurser i stället för att låta CLR slutföra objektet icke-terministiskt.

Kod som skrivs i Visual C++ och kompileras med hjälp av /clr kör en typs destruktor om:

Om en klient som är skriven på ett annat språk använder din typ, anropas destruktoren enligt följande:

  • När ett samtal görs till Dispose.

  • När ett anrop görs till Dispose(void) på typen.

  • Om typen hamnar utanför omfånget i en C#- using instruktion.

Om du inte använder stacksemantik för referenstyper och skapar ett objekt av en referenstyp på den hanterade heapen, använd try-finally-syntax för att säkerställa att ett undantag inte hindrar att destruktorn körs.

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

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

Om din typ har en destructor genererar kompilatorn en Dispose metod som implementerar IDisposable. Om en typ är skriven i Visual C++ och har en destruktor som används från ett annat språk, orsakar ett anrop av IDisposable::Dispose på den typen att typens destruktor anropas. När typen används från en Visual C++-klient kan du inte anropa Disposedirekt. Anropa i stället destruatorn med hjälp av operatorn delete .

Om din typ har en slutversion genererar kompilatorn en Finalize(void) metod som åsidosätter Finalize.

Om en typ antingen har en finalator eller en destructor genererar kompilatorn en Dispose(bool) metod enligt designmönstret. (Mer information finns i Dispose Pattern). Du kan inte uttryckligen skapa eller anropa Dispose(bool) i Visual C++.

Om en typ har en basklass som överensstämmer med designmönstret, anropas destruktorer för alla basklasser när destruktorn för den härledda klassen anropas. (Om din typ är skriven i Visual C++, ser kompilatorn till att dina typer implementerar det här mönstret.) Med andra ord kedjar destrutorn av en referensklass till sina baser och medlemmar enligt C++-standarden. Först körs klassens destruktor. Sedan körs destruktörerna för dess medlemmar i omvänd ordning i den ordning som de konstruerades. Slutligen körs destruktörerna för dess basklasser i omvänt mot den ordning de konstruerades i.

Destruktorer och finaliserare tillåts inte i värdetyper eller gränssnitt.

En finalizer kan bara definieras eller deklareras i en referenstyp. Precis som en konstruktor och destruktor har en finalizer ingen returtyp.

Efter att ett objekts finaliserare har körts anropas även finalizers i alla basklasser, som börjar med den minst härledda typen. Finalizers för datamedlemmar är inte automatiskt länkade av klassens finalizer.

Om en finalizer tar bort en inbyggd pekare i en hanterad typ måste du se till att referenser till eller via den interna pekaren inte samlas in i förtid. Anropa destruktor för den hanterade typen i stället för att använda KeepAlive.

Vid kompileringstillfället kan du identifiera om en typ har en finalizer eller en destruktör. Mer information finns i Kompilatorstöd för typegenskaper.

Nästa exempel visar två typer: en som har ohanterade resurser och en som har hanterade resurser som släpps deterministiskt.

// 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");
}

Se även

Klasser och strukturer