Megosztás a következőn keresztül:


Útmutató: Osztályok és szerkezetek definiálása és felhasználása (C++/CLI)

Ez a cikk bemutatja, hogyan definiálhat és használhat felhasználó által definiált referenciatípusokat és értéktípusokat a C++/CLI-ben.

Objektum példányosítása

A referenciatípusok (ref) csak a kezelt halomon hozhatók létre, nem a veremen vagy a natív halomon. Az értéktípusok példányosíthatók a veremen vagy a felügyelt halomon.

// 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 módon absztrakt osztályok

Egy implicit absztrakt osztály nem hozható létre. Az osztály implicit módon absztrakt, ha:

  • az osztály alaptípusa egy interfész, és
  • az osztály nem implementálja az interfész összes tagfüggvényét.

Előfordulhat, hogy nem tud objektumokat létrehozni egy interfészből származtatott osztályból. Ennek oka lehet, hogy az osztály implicit módon absztrakt. Az absztrakt osztályokról további információt az absztrakt című témakörben talál.

Az alábbi példakód azt mutatja be, hogy az MyClass osztály nem hozható létre, mert a függvény MyClass::func2 nincs implementálva. A példa lefordításának engedélyezéséhez távolítsa el a megjegyzés jelet a(z) MyClass::func2 elől.

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

Típus láthatósága

Szabályozhatja a közös nyelvi futtatókörnyezetek (CLR) típusainak láthatóságát. Amikor hivatkozik az assembly-re, eldöntheti, hogy az assembly-beli típusok láthatóak-e kívülről vagy sem.

public azt jelzi, hogy egy típus látható minden olyan forrásfájlban, amely a típust #using tartalmazó szerelvény irányelvét tartalmazza. private azt jelzi, hogy a típus nem látható a típust tartalmazó szerelvényre vonatkozó irányelveket #using tartalmazó forrásfájlok számára. A privát típusok azonban ugyanabban a szerelvényben láthatók. Az osztály láthatósága alapértelmezés szerint az private.

Alapértelmezés szerint, a Visual Studio 2005 előtt a natív típusok nyilvánosan elérhetők voltak az összeállításon kívül. Engedélyezze a fordító figyelmeztetését (1. szintű) C4692, hogy lássa, hol használják helytelenül a privát natív típusokat. A make_public pragma használatával nyilvános hozzáférést biztosíthat egy natív típushoz egy olyan forráskódfájlban, amelyet nem lehet módosítani.

További információ: #using irányelv.

Az alábbi minta bemutatja, hogyan deklarálhat típusokat, és hogyan adhatja meg azok akadálymentességét, majd hogyan érheti el ezeket a típusokat a szerelvényen belül. Ha #using segítségével egy külső szerelvényre hivatkozik, amely privát típusokat tartalmaz, akkor csak a publikus típusok érhetők el a szerelvényben.

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

Kimenet

in Public_Class
in Private_Class
in Private_Class_2

Most írjuk át az előző mintát, hogy DLL-ként legyen létrehozva.

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

A következő minta bemutatja, hogyan férhet hozzá a szerelvényen kívüli típusokhoz. Ebben a mintában az ügyfél az előző mintában létrehozott összetevőt használja.

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

Kimenet

in Public_Class

Tagok láthatósága

A nyilvános osztály egy tagjának hozzáférését ugyanabból a szerelvényből érheti el, nem pedig a szerelvényen kívülről. Ehhez használja a hozzáférési jelölőpárokat public, protectedés private

Ez a táblázat a különböző hozzáférés-meghatározók hatását foglalja össze:

Kijelölő Hatás
public A tag a közgyűlésen belül és kívül is elérhető. További információért lásd public.
private A tag nem érhető el a közgyűlésen belül és kívül is. További információért lásd private.
protected A tag a szerelvényen belül és kívül is elérhető, de csak származtatott típusok számára. További információért lásd protected.
internal A tag nyilvános a közgyűlésen belül, de privát a közgyűlésen kívül. A internal környezetfüggő kulcsszó. További információ: Context-Sensitive Kulcsszavak.
public protected protected public A tag az összeállításon belül nyilvános, de az összeállításon kívül védett.
private protected protected private A tag védett az összeállításon belül, de privát kívül az összeállításon.

Az alábbi minta egy olyan nyilvános típust mutat be, amelynek tagjai a különböző hozzáférési azonosítók használatával vannak deklarálva. Ezután megjeleníti a tagok hozzáférését a közgyűlésen belülről.

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

Kimenet

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
=======================

Most hozzuk létre az előző mintát DLL-ként.

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

Az alábbi minta az előző mintában létrehozott összetevőt használja fel. Bemutatja, hogyan férhet hozzá a tagokhoz a közgyűlésen kívülről.

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

Kimenet

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

Nyilvános és privát natív osztályok

Egy natív típus felügyelt típusból is hivatkozható. Egy felügyelt típusú függvény például olyan paramétert vehet fel, amelynek típusa natív szerkezet. Ha a felügyelt típus és függvény nyilvános egy szerelvényben, akkor a natív típusnak is nyilvánosnak kell lennie.

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

Ezután hozza létre a natív típust használó forráskódfájlt:

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

Most készítsen össze egy klienst.

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

#include "mcppv2_ref_class3.h"

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

Statikus konstruktorok

A CLR-típusok (például osztály vagy szerkezet) statikus konstruktort is tartalmazhatnak, amely statikus adattagok inicializálására használható. A statikus konstruktorok meghívása legfeljebb egyszer történik, és a típus bármely statikus tagjának első elérése előtt lesz meghívva.

A példánykonstruktorok mindig statikus konstruktor után futnak.

A fordító nem tud meghívni egy konstruktort, ha az osztály statikus konstruktorsal rendelkezik. A fordító nem tud beágyazni egyetlen tagfüggvényhívást sem, ha az osztály értéktípus, statikus konstruktorral rendelkezik, de nincs példánykonstruktora. Előfordulhat, hogy a CLR beágyazhatja a hívást, de a fordító erre nem képes.

A statikus konstruktort privát tagfüggvényként definiálhatja, mert csak a CLR hívja meg.

További információ a statikus konstruktorokról: Útmutató: Interfész statikus konstruktor definiálása (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();
}

Kimenet

in static constructor
10
11

A mutató szemantikája this

Ha Ön C++/CLI használatával típusokat definiál, a this hivatkozástípusú mutató fogantyú típusú. Az this értéktípus mutatója belső mutató típusú.

A this mutató különböző szemantikái váratlan viselkedéshez vezethetnek, amikor egy alapértelmezett indexelőt hívnak meg. A következő példa bemutatja, hogyan lehet elérni egy alapértelmezett indexelőt referenciatípusban és értéktípusban.

További információ: Handle to Object Operator (^) és 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();
}

Kimenet

10.89
10.89

Aláírás alapján elrejtett függvények

A standard C++-ban az alaposztályban lévő függvényeket elrejti egy olyan függvény, amelynek neve azonos egy származtatott osztályban, még akkor is, ha a származtatott osztályfüggvény nem rendelkezik azonos típusú vagy számú paraméterrel. Név szerinti elrejtés szemantikájaként ismert. Referenciatípus esetén az alaposztályban lévő függvényeket csak akkor rejti el egy származtatott osztály egyik függvénye, ha a név és a paraméterlista megegyezik. Ez az úgynevezett rejtett aláírás szemantikája.

Az osztály akkor minősül rejtett aláírás szerinti osztálynak, ha az összes függvénye a metaadatokban hidebysiga következőképpen van megjelölve. Alapértelmezés szerint az összes /clr osztálynak vannak hidebysig függvényei. Ha egy osztály rendelkezik hidebysig függvényekkel, a fordító nem rejti el név alapján a függvényeket egyetlen közvetlen alaposztályban sem, de ha a fordító egy öröklési láncban egy név szerinti elrejtési osztálysal találkozik, az továbbra is a név szerinti elrejtési viselkedést követi.

A rejtett aláírás szemantikája alatt, amikor egy függvényt meghívnak egy objektumra, a fordító azonosítja a legelvezetettebb osztályt, amely olyan függvényt tartalmaz, amely kielégítheti a függvényhívást. Ha az osztályban csak egy függvény felel meg a hívásnak, a fordító meghívja a függvényt. Ha az osztályban több függvény is megfelelhet a hívásnak, a fordító túlterhelésfeloldási szabályokkal határozza meg, hogy melyik függvényt hívja meg. További információ a túlterhelési szabályokról: Függvény túlterhelése.

Egy adott függvényhívás esetében az alaposztályban lévő függvények aláírással rendelkezhetnek, ami kissé jobb egyezést tesz lehetővé, mint egy származtatott osztályban lévő függvény. Ha azonban a függvényt explicit módon meghívták a származtatott osztály egy objektumára, a függvény a származtatott osztályban lesz meghívva.

Mivel a visszatérési érték nem egy függvény aláírásának része, az alaposztályú függvény rejtve lesz, ha ugyanazzal a névvel rendelkezik, és ugyanolyan típusú és számú argumentumot vesz fel, mint egy származtatott osztályfüggvény, még akkor is, ha a visszatérési érték típusában eltér.

Az alábbi minta azt mutatja, hogy az alaposztályban lévő függvényeket nem rejti el egy származtatott osztály egyik függvénye.

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

Kimenet

Base::Test

A következő példa azt mutatja, hogy a Microsoft C++ fordító meghív egy függvényt a legelvezetettebb osztályban – még akkor is, ha egy vagy több paraméternek megfelelő átalakításra van szükség –, és nem hív meg olyan függvényt egy alaposztályban, amely jobb egyezés a függvényhíváshoz.

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

Kimenet

Derived::Test2

Az alábbi minta azt mutatja, hogy akkor is elrejthető egy függvény, ha az alaposztály aláírása megegyezik a származtatott osztályéval.

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

Kimenet

Derived::Test4
97

Konstruktorok másolása

A C++ szabvány azt mondja, hogy egy példánykonstruktort akkor hívunk meg, amikor áthelyezünk egy objektumot, hogy egy objektumot ugyanazon a címen hozzunk létre és semmisítsünk meg.

Ha azonban egy MSIL-nek lefordított függvény olyan natív függvényt hív meg, amelyben egy natív osztályt vagy egynél több osztályt ad át az érték, és ahol a natív osztály rendelkezik másolatkonstruktorsal vagy destruktorsal, a példánykonstruktor nem lesz meghívva, és az objektum a létrehozás helyétől eltérő címen lesz megsemmisítve. Ez a viselkedés problémákat okozhat, ha az osztály egy mutatóval rendelkezik önmagában, vagy ha a kód cím alapján követi nyomon az objektumokat.

További információ: /clr (Common Language Runtime Compilation).

Az alábbi minta bemutatja, hogy mikor nem jön létre másolatkonstruktor.

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

Kimenet

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

Destruktorok és véglegesítők

A referencia típusú destruktorok determinisztikus módon tisztítják meg az erőforrásokat. A véglegesítők megtisztítják a nem felügyelt erőforrásokat, és determinisztikusan a destruktor, vagy nemdeterminisztikusan a szemétgyűjtő által hívhatók. A standard C++ destruktorokkal kapcsolatos információkért lásd: Destruktorok.

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

A CLR szemétgyűjtő törli a nem használt felügyelt objektumokat, és felszabadítja a memóriát, ha már nincs rájuk szükség. Előfordulhat azonban, hogy egy típus olyan erőforrásokat használ, amelyeket a szemétgyűjtő nem tud felszabadítani. Ezeket az erőforrásokat nem felügyelt erőforrásoknak (például natív fájlkezelőknek) nevezzük. Javasoljuk, hogy az összes nem felügyelt erőforrást engedje el a véglegesítőben. A szemétgyűjtő nemdeterminista módon bocsátja ki a felügyelt erőforrásokat, így nem biztonságos a felügyelt erőforrásokra hivatkozni a véglegesítőben. Ez azért van, mert lehetséges, hogy a szemétgyűjtő már megtisztította őket.

A Visual C++ véglegesítő nem ugyanaz, mint a Finalize módszer. (A CLR dokumentációja a finalizert és a Finalize metódus szinonimát használja). A Finalize metódust a szemétgyűjtő hívja meg, amely meghívja az osztályöröklési lánc minden véglegesítőjét. A Visual C++ destruktoroktól eltérően egy származtatott osztály véglegesítő hívása nem eredményezi, hogy a fordító meghívja a véglegesítőt az összes alaposztályban.

Mivel a Microsoft C++ fordító támogatja az erőforrások determinisztikus kiadását, ne próbálja meg implementálni a Dispose metódusokat.Finalize Ha azonban ismeri ezeket a módszereket, itt van, hogyan illeszkedik egy Visual C++ finalizer és egy destruktor, amely a finalizert hívja a Dispose mintához:

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

A felügyelt típus olyan felügyelt erőforrásokat is használhat, amelyeket determinisztikus módon szeretne kiadni. Előfordulhat, hogy nem szeretné, hogy a szemétgyűjtő nem meghatározott módon engedje fel az objektumot, miután az objektumra már nincs szükség. Az erőforrások determinisztikus kiadása jelentősen javíthatja a teljesítményt.

A Microsoft C++ fordító lehetővé teszi a destruktor definícióját az objektumok determinisztikus törléséhez. A destruktor használatával minden olyan erőforrást felszabadíthat, amelyet determinisztikusan szeretne felszabadítani. Ha van véglegesítő, hívja meg a destruktorból, így elkerülheti a kód duplikálását.

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

Ha a típust használó kód nem hívja meg a destruktort, a szemétgyűjtő rendszer végül felszabadít minden felügyelt erőforrást.

A destruktor jelenléte nem jelenti a véglegesítő jelenlétét. A véglegesítő jelenléte azonban azt jelenti, hogy meg kell határoznia egy destruktort, és meg kell hívnia a véglegesítőt az adott destruktorból. Ez a hívás a nem felügyelt erőforrások determinisztikus kiadását biztosítja.

Az objektum véglegesítését a destruktor meghívásával – SuppressFinalize használatával – lehet letiltani. Ha a destruktort nem hívják meg, a típus véglegesítőjét végül a szemétgyűjtő hívja meg.

A teljesítmény javításához hívja meg a destruktort az objektum erőforrásainak determinisztikus megtisztítására ahelyett, hogy a CLR nemdeterminisztikus módon véglegesíti az objektumot.

A Visual C++-ban írt és a /clr által lefordított kód egy típus destruktorát futtatja, ha:

Ha egy másik nyelven írt kliens használja az Ön típusát, a destruktor meghívása a következőképpen történik:

  • Egy hívás során a(z) Dispose-hoz/-hez/-höz.

  • Hívás Dispose(void) típusra.

  • Ha a típus egy C# using -utasítás hatókörén kívül esik.

Ha nem használ veremszemantikát a referenciatípusokhoz, és egy referenciatípusú objektumot hoz létre a felügyelt halomon, használja a végrepróbálkozási szintaxist annak biztosítására, hogy egy kivétel ne akadályozza meg a destruktor futtatását.

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

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

Ha a típusnak van destruktora, a fordító létrehoz egy Dispose metódust, amely megvalósítja IDisposable. Ha egy Visual C++ nyelven írt és egy másik nyelvről származó destruktort tartalmazó típus, az adott típus meghívása IDisposable::Dispose a típus destruktorának meghívását eredményezi. Ha a típust Visual C++ ügyfélből használja, közvetlenül nem hívhatja meg Dispose; ehelyett az delete operátor használatával hívja meg a destruktort.

Ha a típus véglegesítővel rendelkezik, a fordító létrehoz egy metódust Finalize(void) , amely felülbírálja a metódust Finalize.

Ha egy típus véglegesítővel vagy destruktorsal rendelkezik, a fordító a tervezési minta szerint létrehoz egy Dispose(bool) metódust. (További információ: Megsemmisítési minta). A Visual C++-ban nem lehet explicit módon létrehozni vagy meghívni Dispose(bool) .

Ha egy típus olyan alaposztálysal rendelkezik, amely megfelel a tervezési mintának, a rendszer az összes alaposztály destruktorait meghívja a származtatott osztály destruktorának meghívásakor. (Ha a típust a Visual C++-ban írják meg, a fordító biztosítja, hogy a típus implementálja ezt a mintát.) Más szóval, a referenciaosztály destruktora a C++ szabványban meghatározott bázisosztályokra és tagváltozókra láncol. Először is fut az osztály destruktora. Ezután a tagok destruktorai a felépítés sorrendjének fordítottja alatt futnak. Végül az alaposztályok destruktorai a megépítésük sorrendjének fordított sorrendjében futnak le.

A destruktorok és a véglegesítők nem engedélyezettek értéktípusokon vagy interfészeken belül.

A véglegesítők csak referenciatípusban definiálhatók vagy deklarálhatók. A finalizálóra, úgy mint a konstruktorra és a destruktorra, nem vonatkozik visszatérési típus.

Az objektum véglegesítőjének futtatása után a rendszer meghívja az alaposztályok véglegesítőit is, a legkevésbé származtatott típussal kezdve. Az adattagok véglegesítőihez az osztály véglegesítője nincs automatikusan láncolva.

Ha a véglegesítő egy natív mutatót töröl egy felügyelt típusban, győződjön meg arról, hogy a natív mutatóra vagy azon keresztül mutató hivatkozások nem lesznek idő előtt összegyűjtve. Hívja meg a destruktort a felügyelt típuson a KeepAlive használata helyett.

Fordítás idején megállapíthatja, hogy egy típus rendelkezik-e véglegesítővel vagy destruktorral. A Típustulajdonságok fordítói támogatása című dokumentumban további információt talál.

A következő minta két típust mutat be: az egyik nem felügyelt erőforrásokat tartalmaz, és egy másik, amely felügyelt erőforrásokat tartalmaz, amelyek determinisztikusan felszabadulnak.

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

Lásd még

Osztályok és szerkezetek