Udostępnij za pośrednictwem


Porady: definiowanie oraz stosowanie klas i struktur (C++/CLI)

W tym artykule przedstawiono sposób definiowania i zużyje typy zdefiniowane przez użytkownika odwołań i typy wartości w C++/CLI.

Podczas tworzenia wystąpienia obiektu

Typy odwołań (ref) i typy wartości można tylko można utworzyć wystąpienia w zarządzanych sterty, nie na stosie lub macierzystą stertę.

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

Klasy abstrakcyjne niejawnie

Niejawnie abstrakcyjne klasy nie można utworzyć wystąpienia.Klasa jest niejawnie abstrakcyjna, jeśli typ bazowy klasy jest interfejsem, a klasa nie implementuje wszystkie funkcje składowe interfejsu.

Jeśli nie jest możliwe do konstruowania obiektów z klasy, która wywodzi się z interfejsu, powód może być, że klasa jest abstrakcyjna niejawnie.Aby uzyskać więcej informacji na temat klasy abstrakcyjne, zobacz streszczenie.

Poniższy przykład kodu pokazuje, że MyClass klasy nie można utworzyć wystąpienia, ponieważ funkcja MyClass::func2 nie jest zaimplementowana.Aby włączyć w przykładzie skompilować, Usuń oznaczenie jako 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.
}

Widoczność typu

Można sterować widocznością language runtime (CLR) typowych, tak, że jeśli zespół jest wywoływany, typów w zestawie mogą być widoczne lub niewidoczne znajdującego się poza zestawem.

publicWskazuje, że typ jest widoczny dla dowolnego pliku źródłowego, który zawiera #using w dyrektywie dla zestawu, który zawiera tekst.privateWskazuje, że typ nie jest widoczny dla plików źródłowych, które zawierają #using w dyrektywie dla zestawu, który zawiera tekst.Typy prywatne będą jednak widoczne, w tym samym zespole.Domyślnie jest widoczność dla klasy private.

Domyślnie przed Visual C++ 2005 macierzyste typy miał powszechnej dostępności znajdującego się poza zestawem.Włącz C4692 (poziom 1) ostrzeżenia kompilatora Aby zobaczyć, gdzie prywatne macierzyste typy zostaną użyte błędnie.Użycie make_public pragma zapewnienie dostępności opinii publicznej do typu macierzystego w pliku kodu źródłowego, który nie można modyfikować.

Aby uzyskać więcej informacji, zobacz # za pomocą dyrektywy (C++).

Poniższy przykład ilustruje występować deklaracje typów i określ ich dostępność, a następnie uzyskać dostęp do tych rodzajów wewnątrz montażu.Oczywiście, jeśli to zespół, który ma typy prywatnych jest wywoływany przy użyciu #using, tylko publiczne typów w zestawie są widoczne.

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

Dane wyjściowe

  

Teraz Przepiszmy poprzedni przykład, tak aby jest tworzona jako biblioteki 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");}
};

Następny przykład pokazuje jak dostęp do typów znajdującego się poza zestawem.W tej próbce klient używa składnika, który jest zbudowany w poprzednim przykładzie.

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

Dane wyjściowe

  

Widoczność Członkowskie

Można wprowadzać dostępu członkiem publicznych klasy z wnętrza tego samego zespołu różni się od dostępu do niego z znajdującego się poza zestawem za pomocą pary specyfikatory dostępu public, protected, iprivate

W poniższej tabeli podsumowano wyniki zastosowania różnych specyfikatory dostępu:

Specyfikator

Efekt

public

Element członkowski jest dostępny wewnątrz i na zewnątrz zgromadzenie.Aby uzyskać więcej informacji, zobacz publicznego (C++).

private

Członek nie jest dostępne, ani wewnątrz, ani znajdującego się poza zestawem.Aby uzyskać więcej informacji, zobacz prywatne (C++).

protected

Element członkowski jest dostępny wewnątrz i znajdującego się poza zestawem, ale tylko do pochodnych typów.Aby uzyskać więcej informacji, zobacz chronione (C++).

internal

Element członkowski jest publiczny wewnątrz montażu, ale prywatnych znajdującego się poza zestawem.internaljest słowem kluczowym uzależnioną od kontekstu.Aby uzyskać więcej informacji, zobacz Kontekstowe słowa kluczowe (C++ Component Extensions).

public protected
-or-
protected public

Element członkowski jest publiczny wewnątrz montażu, ale chroniona znajdującego się poza zestawem.

private protected
-or-
protected private

Element członkowski jest chronione wewnątrz montażu, ale prywatnych znajdującego się poza zestawem.

Poniższy przykładowy pokazuje typ publiczny, który ma elementy zadeklarowane za pomocą różnych kosztorysu, a następnie pokazuje dostęp do tych członków z wewnątrz montażu.

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

Dane wyjściowe

  

Teraz spróbujmy skompilować poprzedni przykład jako biblioteki DLL.

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

Poniższy przykład używa składnika, który jest tworzony w poprzednim przykładzie, a tym samym pokazuje, jak uzyskać dostęp do członków z znajdującego się poza zestawem.

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

Dane wyjściowe

  

Publiczne i prywatne klasy natywne

Typ macierzysty można odwoływać się od typu zarządzanego.Na przykład to funkcja w typu zarządzanego może zająć parametru, którego typem jest struct macierzystego.Jeśli typu zarządzanego i funkcji są publiczne w zestawie, następnie typu macierzystego należy również publiczne.

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

Następnie należy utworzyć plik kodu źródłowego, który zużywa typ macierzysty:

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

Teraz skompilować klienta:

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

Konstruktory statyczne

Typ CLR — na przykład, klasie lub strukturze — może mieć w konstruktorze statycznym, który może służyć do zainicjowania elementy danych statycznych.Konstruktorze statycznym nazywa się co najwyżej raz, a jest wywoływane przed dowolnym statyczny element członkowski tego typu jest dostępny po raz pierwszy.

Konstruktor wystąpień jest uruchamiany zawsze po konstruktorze statycznym.

Kompilator nie może inline wywołanie konstruktora, jeśli klasa ma konstruktorze statycznym.Kompilator nie może inline wywołanie funkcji dowolnego członka, jeśli klasa jest typ wartości, posiada konstruktorze statycznym i nie ma być konstruktorem wystąpień.Środowisko CLR może inline wywołanie, ale nie kompilator.

Zdefiniuj konstruktorze statycznym w funkcji prywatne Państwa, ponieważ ma być wywoływana tylko przez środowisko CLR.

Aby uzyskać więcej informacji na temat konstruktorów statycznych, zobacz Porady: definiowanie statycznego konstruktora interfejsu (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();
}

Dane wyjściowe

  

Semantyka tej wskaźnika

Podczas korzystania z języka Visual C++ do definiowania typów, this wskaźnik w polu Typ odwołania jest typu "obsługiwać".this Wskaźnik w polu Typ wartości jest typu "wskaźnik wnętrza".

Taką semantykę różnych z this wskaźnika może powodować nieoczekiwane zachowanie, gdy wywoływana jest indeksatora domyślne.W następnym przykładzie pokazano prawidłowy sposób, aby uzyskać dostęp indeksatora domyślne w typ odwołania i typ wartości.

Aby uzyskać więcej informacji zobacz

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

Dane wyjściowe

  

Ukryj podpisu funkcji

W C++ funkcji w klasie podstawowej jest ukryty przez funkcję, która ma taką samą nazwę w klasie pochodnej, nawet jeśli funkcja klasa nie ma ten sam numer lub rodzaju parametrów.Jest to określane jako Ukryj według nazwy semantyka.W polu Typ odwołania funkcji w klasie podstawowej można tylko je ukryć przez funkcję w klasie pochodnej nazwy użytkownika i listy parametrów są takie same.Jest to znane jako Ukryj przez podpis semantyka.

Klasa jest traktowana jako klasa Ukryj podpisu, gdy wszystkie jego funkcje są oznaczone w metadanych jako hidebysig.Domyślnie, wszystkie klasy, które zostały utworzone w /clr mają hidebysig funkcji.Jednak klasa, która jest kompilowana przy użyciu /clr:oldSyntax nie ma hidebysig funkcje; są to funkcje Ukryj według nazwy.Kiedy klasa posiada hidebysig funkcji, kompilator nie powoduje ukrycia funkcje według nazwy w wszelkie bezpośrednie podstawowych klas, ale jeśli kompilator napotka klasy Ukryj według nazwy w łańcuchu dziedziczenia, nadal to zachowanie Ukryj według nazwy.

W obszarze semantykę Ukryj podpisu przy wywołaniu funkcji na obiekcie, kompilator identyfikuje najbardziej odziedziczonej klasy, która zawiera funkcję, która mogłaby zaspokoić wywołanie funkcji.Jeśli istnieje tylko jedna funkcja klasy, która mogłaby zaspokoić wywołanie, kompilator wywołuje tę funkcję.Jeśli istnieje więcej niż jednej funkcji w klasie, która mogłaby zaspokoić wywołanie, do zastosowań kompilator przeciążać rozdzielczość zasady określania, które funkcji do wywołania.Aby uzyskać więcej informacji o regułach przeciążenie, zobacz Działać na przeciążenie.

Dla wywołania danej funkcji funkcja w klasie podstawowej ma podpis, który sprawia, że dopasowane nieco lepiej niż funkcję w klasie pochodnej.Jednakże jeśli funkcja została wywołana jawnie na obiekt klasy pochodnej, funkcja w klasie pochodnej jest wywoływana.

Ponieważ wartości zwracanej nie jest traktowana jako część podpisu funkcji, funkcja klasa bazowa jest ukryte, jeśli ma taką samą nazwę i ma taką samą ilość i rodzaj argumenty w funkcji klasa, nawet, jeśli różni się ono w polu Typ wartości zwracanej.

Poniższy przykład pokazuje, że funkcja w klasie podstawowej nie jest ukryty przez funkcję w klasie pochodnej.

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

Dane wyjściowe

  

Następny przykład pokazuje, że kompilator języka Visual C++ wywołuje funkcję w klasie pochodnej najbardziej — nawet jeśli konwersja jest wymagane do jednego lub więcej parametrów — i wywołał funkcję w klasie bazowej, która jest lepiej pasuje do wywołania funkcji.

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

Dane wyjściowe

  

Poniższy przykład pokazuje, że istnieje możliwość ukryć funkcję, nawet jeśli klasy podstawowej ma taką samą sygnaturę jak klasie pochodnej.

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

Dane wyjściowe

  

Poniższy przykładowy określa składnik, który jest kompilowana przy użyciu /clr:oldSyntax.Klas, które są zdefiniowane przy użyciu rozszerzenia zarządzane dla języka C++ ma funkcje składowe Ukryj według nazwy.

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

Następny przykład zużywa składnika, który jest zbudowany w poprzednim przykładzie.Obwieszczenie jak Ukryj przez podpisu funkcjonalność nie jest stosowane do klas bazowych typów, które są zestawiane za pomocą /clr:oldSyntax.

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

Konstruktory kopii

C++ standard mówi, że Konstruktor kopiujący jest wywoływane, gdy obiekt zostanie przeniesiony, takie, że obiekt jest tworzone i niszczone pod tym samym adresem.

Jednak, gdy /clr jest używana do kompilacji i funkcji, która jest kompilowana w celu wywołania MSIL pochodzący działać w przypadku, gdy macierzysta klasa — lub więcej niż jedną — jest przekazywany przez wartość i gdzie macierzysta klasa ma Konstruktor kopiujący i/lub destruktor, nazywa się nie Konstruktor kopiujący i obiekt jest niszczony pod adres inny niż gdzie została utworzona.Może to powodować problemy, jeśli klasa ma wskaźnik do siebie samego, czy kod jest śledzenie obiektów według adresów.

Aby uzyskać więcej informacji, zobacz / CLR (kompilacja wspólnej Language Runtime).

Poniższy przykład demonstruje, nie wygenerowano Konstruktor kopiujący.

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

Dane wyjściowe

  

Destruktory i finalizatorów

Destruktory w typu odwołania wykonywać deterministyczne oczyszczania zasobów.Finalizatorów oczyścić niezarządzanych zasoby i może być wywoływana deterministically przez destruktor, lub nondeterministically przez moduł garbage collector.Aby uzyskać informacje o destruktory w C++, zobacz Destruktory (C++).

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

Zachowanie destruktory w zarządzanej klasy Visual C++ różni się od rozszerzenia zarządzane dla języka C++.Aby uzyskać więcej informacji o tej zmianie, zobacz Zmiany w semantyce destruktor.

Moduł garbage collector CLR powoduje usunięcie nieużywanych obiektów zarządzanych i zwalnia ich pamięci, gdy nie są już wymagane.Typ może posługiwać się zasoby, które moduł garbage collector nie potrafi prawa do wydania.Zasoby te są znane jako niezarządzanych zasoby (macierzysty dojść do pliku, na przykład).Zaleca się, że zwolnić wszystkie zasoby niezarządzanego w finalizatorów.Ponieważ zarządzanych zasobów są uwalniane nondeterministically przez moduł garbage collector, nie jest bezpieczne odwołać się do zarządzanych zasobów w finalizatorów ponieważ istnieje możliwość, że moduł garbage collector oczyszczonego już się tym zarządzanego zasobu.

Finalizatorów Visual C++ nie jest taka sama, jak Finalize metoda.(CLR w dokumentacji zastosowano finalizatorów i Finalize metoda zamiennie).Finalize Metoda jest wywoływana przez moduł garbage collector, który wywołuje każdego finalizatorów w łańcuchu dziedziczenia klas.W odróżnieniu od destruktory Visual C++ wywołanie klasy pochodzące finalizatorów nie powoduje kompilatora wywołać finalizatorów we wszystkich klasach podstawowej.

Ponieważ kompilator języka Visual C++ obsługuje deterministyczne uwolnienie zasobów, nie staramy się wdrażać Dispose lub Finalize metody.Jednakże, jeśli masz wystarczającą wiedzę o tych metod, w tym miejscu jest jak finalizatorów Visual C++ i destruktor, który wywołuje finalizatorów mapować Dispose wzór:

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

Typu zarządzanego mogą również używać zarządzanych zasobów, które chcesz zwolnić deterministically, a nie opuszczają do garbage collector zwolnienie w pewnym momencie nondeterministically, po obiekt nie jest już wymagany.Deterministyczne uwolnienie zasobów może znacznie poprawić wydajność.

Kompilator Visual C++ umożliwia definicji destruktora deterministically oczyszczenie obiektów.Destruktor służy do zwalniania wszystkich zasobów, które mają zostać wydane deterministically.Jeśli występuje finalizatorów wywołać ją z destruktora, w celu uniknięcia powielania kod.

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

Jeśli kod, który zużywa danego typu nie wymaga destruktor, moduł garbage collector ostatecznie zwalnia wszystkie zasoby zarządzane.

Obecność destruktora nie oznacza obecność finalizatorów.Jednak obecności finalizatorów oznacza, że należy zdefiniować destruktora a wywoływać finalizatorów z tym destruktora.Zapewnia to deterministyczne zwolnienia niezarządzanych zasobów.

Wywołanie destruktora pomija — za pomocą SuppressFinalize— końcowego przetwarzania obiektu.Jeżeli destruktor nie jest wywoływana, finalizatorów typ swojego po pewnym czasie zostanie zwołane przez moduł garbage collector.

Oczyszczanie deterministically zasoby nazwę obiektu poprzez wywołanie destruktora może zwiększyć wydajność w porównaniu z najmu CLR nondeterministically sfinalizowania obiekt.

Kod, który został napisany w języku Visual C++ i skompilowane przy użyciu /clr typu destructor jest uruchamiany, jeśli:

Jeśli typ swojego zużywanych przez klienta, który jest napisany w innym języku, destruktor nazywa się następujące zmiany:

  • Na wywołanie Dispose.

  • Na wywołanie Dispose(void) w typie.

  • Jeśli typ wykracza poza zakres w C# using instrukcji.

Jeśli tworzysz obiekt typu odwołanie na stercie zarządzanych (bez użycia semantykę stosu dla typu odwołania), użyj try-finally składni w celu zapewnienia, że wyjątek nie pozbawia destruktor z systemem.

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

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

Jeśli Twój typ ma destruktora, kompilator generuje Dispose metoda, która implementuje IDisposable.Jeśli typ, który jest napisany w języku Visual C++ i ma destruktor, który jest zużywany w innym języku, wzywając IDisposable::Dispose na tego typu powoduje, że na wywołanie destruktora typu.Gdy typ jest zużywany przez klienta Visual C++, nie można bezpośrednio wywołać Dispose; Zamiast tego wywołania destruktora za pomocą delete operatora.

Jeśli Twój typ ma finalizatorów, kompilator generuje Finalize(void) metoda, która zastępuje Finalize.

Jeśli typ ma finalizatorów lub destruktora, kompilator generuje Dispose(bool) sposób, w zależności od wzorca projektowego.(Aby uzyskać informacje, zobacz Implementing Finalize and Dispose to Clean Up Unmanaged Resources).Nie można jawnie autor lub wywołać Dispose(bool) w programie Visual C++.

Jeśli typ ma klasy podstawowej, która jest zgodna z wzorca projektowego, destruktory dla wszystkich klas podstawowych są nazywane po wywołaniu destruktora klasy pochodnej.(Jeśli danego typu został napisany w języku Visual C++, kompilator zapewnia że swój typ wprowadzać w życie tego wzorca.) Innymi słowy, destruktora klasy reference sieci do jej podstawy i członków, jak określono w normie C++ — pierwszy destruktora klasy jest Uruchom, a następnie destruktory dla jej członków w odwrotnej kolejności, w którym zostały skonstruowane, a na końcu destruktory dla jej klas podstawowych w odwrotnej kolejności, w którym zostały wyprodukowane.

Destruktory i finalizatorów nie są dozwolone wewnątrz typów wartości lub interfejsów.

Tylko finalizatorów może być zdefiniowane lub zadeklarowane w typu odwołania.Podobnie jak konstruktora i destruktora finalizatorów nie ma zwrotu typu.

Po uruchomieniu obiektu finalizatorów finalizatorów w dowolnym bazowe klasy są również nazywane, począwszy od typu najmniej pochodnego.Finalizatorów dla elementów członkowskich danych są nie automatycznie powiązane do przez finalizatorów obiektu prototypowego klasy.

Jeśli finalizatorów usuwa wskaźniku macierzystym w typu zarządzanego, musi zapewnić, że odniesienia do lub za pośrednictwem wskaźniku macierzystym nie są zbierane przedwcześnie; wywołanie destruktora na typu zarządzanego, zamiast korzystać z KeepAlive.

W czasie kompilacji mogą wykryć, czy typ ma finalizatorów lub destruktora.Aby uzyskać więcej informacji, zobacz Obsługa cech typu w kompilatorze (C++ Component Extensions).

Następny przykład pokazuje dwa typy, który ma niezarządzanych zasoby i taką, która ma zarządzanych zasobów, które są dostępne w deterministically.

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

Zobacz też

Informacje

Klasy i struktury (C++ Component Extensions)

Klasy i struktury (C++ Component Extensions)