Udostępnij za pośrednictwem


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

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

Zawartość

Podczas tworzenia wystąpienia obiektu

Klasy abstrakcyjne niejawnie

Typ widoczności

Członek widoczności

Publiczne i prywatne macierzystych klas

Konstruktory statyczne

Semantyka tej wskaźnika

Funkcje Ukryj przez podpisu

Konstruktory kopii

Destruktory i finalizatorów

Podczas tworzenia wystąpienia obiektu

Typy odwołań (ref) i typy wartości tylko mogą być utworzone na zarządzanego stosu, nie na stosu lub sterty macierzystej.

// 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 streszczenie klasy nie można utworzyć wystąpienia.Klasa jest niejawnie streszczenie, jeśli typ bazowy klasy jest interfejsem i klasa nie implementuje wszystkie funkcje składowe interfejsu.

Jeśli nie jest możliwe do konstruowania obiektów z klasy pochodzącej z interfejsem, powodem może być klasa jest niejawnie streszczenie.Aby uzyskać więcej informacji na temat klas abstrakcyjnych, 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 komentarza 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.
}

Typ widoczności

Można sterować widocznością wspólnego języka wspólnego (CLR) typów, tak, że jeśli odwołuje się do zestawu, 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, zawierającą #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 prywatny są jednak widoczne w obrębie tego samego zestawu.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 Ostrzeżenie kompilatora (poziom 1) C4692 aby zobaczyć, gdzie prywatne macierzyste typy zostaną użyte błędnie.Użycie make_public pragmy dać powszechnej dostępności do typu macierzystego w pliku kodu źródłowego, które można modyfikować.

Aby uzyskać dodatkowe informacje, zobacz #using — dyrektywa (C++).

Poniżej pokazano jak deklarować typów i określić ich dostępności i dostępu do tych rodzajów wewnątrz montażu.Oczywiście, jeśli jest to zespół, który ma typy prywatnych jest wywoływany przy użyciu #using, tylko typy publiczne 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 załóżmy przepisać poprzedni przykład, tak, że jest zbudowany 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 typy znajdującego się poza zestawem.W tym przykładzie klienta zużywa składnik, 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

  

Członek widoczności

Można wprowadzać dostępu jest członkiem publicznych klasy z wnętrza tego samego zestawu inny niż dostęp do niej z znajdującego się poza zestawem za pomocą pary specyfikatory dostępu public, protected, iprivate

Ta tabela zawiera zestawienie wpływ 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 publiczne (C++).

private

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

protected

Element członkowski jest dostępny wewnątrz i poza zestaw, ale tylko do typów pochodnych. 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ć dodatkowe informacje, zobacz Słowa kluczowe kontekstowe.

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 chroniony 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 elementów od 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 załóżmy budować 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 macierzystych klas

Typ macierzysty można odwoływać się od typu zarządzanego. Na przykład funkcja w typu zarządzanego może zająć parametr, którego typem jest struct macierzystego. Jeśli typu zarządzanego i funkcji są publiczne w zespole, 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 kod źródłowy, 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 kompilacji 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 klasy lub struktury — może mieć konstruktorze statycznym, który może służyć do zainicjowania dane statyczne członków. Konstruktorze statycznym nazywa się co najwyżej raz i nazywa się przed dowolnego członka statycznego tego typu jest dostępny po raz pierwszy.

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

Jeśli klasa ma konstruktorze statycznym kompilator nie może inline wywołanie konstruktora. Kompilator nie może inline wywołanie funkcji dowolnego członka, jeśli klasa jest typ wartości, posiada konstruktorze statycznym i nie ma konstruktora wystąpienia. Środowisko CLR może wbudowane połączenie, ale nie kompilator.

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

Aby uzyskać więcej informacji dotyczących 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 programu Visual C++ do definiowania typów, this wskaźnik w polu Typ odwołania jest typu "uchwyt".this Wskaźnik w polu Typ wartości jest typu "wskaźnik wnętrza".

Te różne semantykę this wskaźnika może powodować nieoczekiwane zachowanie, gdy nazywa się indeksatora domyślne.W następnym przykładzie poprawnego sposobu dostępu do indeksatora domyślne zarówno 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

  

Funkcje Ukryj przez podpisu

W języku C++ standard funkcji w klasie podstawowej jest ukryty przez funkcję, która ma taką samą nazwę w klasie pochodnej, nawet jeśli funkcja klas pochodnych nie zawiera tego samego numeru lub rodzaju parametrów.Jest to określane jako ukrywać wg nazwy semantyka.W typu odwołania funkcji w klasie podstawowej można tylko ukryte przez funkcję w klasie pochodnej Jeśli nazwy użytkownika i listy parametrów są takie same.Jest to nazywane ukrywać 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 na podstawie /clr mają hidebysig funkcji.Jednak klasa, która jest kompilowana za pomocą /clr:oldSyntax nie ma hidebysig funkcje; są to funkcje Ukryj według nazwy.Jeśli klasa ma hidebysig funkcje, 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 semantyka Ukryj podpisu po wywołaniu funkcji na obiekcie, kompilator identyfikuje najbardziej pochodne klasy, która zawiera funkcję, która mogłaby zaspokoić wywołania funkcji.Jeśli istnieje tylko jedna funkcja w klasie, która mogłaby zaspokoić wywołanie, kompilator wywołania tej funkcji.Jeśli istnieje więcej niż jedną funkcję w klasie, która mogłaby zaspokoić wywołanie, do zastosowań kompilator przeciążenia rozdzielczość zasady określające które funkcji do wywołania.Aby uzyskać więcej informacji o regułach przeciążenie, zobacz Przeładowywanie funkcji.

Dla wywołania danej funkcji funkcja w klasie podstawowej może mieć podpis, który sprawia, że dopasowanie nieco lepsza niż funkcji w klasie pochodnej.Jednak jeśli na obiekt klasy pochodnej jawnie wywołano funkcję, jest nazywana funkcji w klasie pochodnej.

Ponieważ wartości zwracanej nie jest uważany za część podpisu funkcji, funkcja klasa bazowa jest ukryte, jeśli ma taką samą nazwę i ma ten sam numer i rodzaj argumenty w funkcji klas pochodnych, nawet jeżeli 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, aby dopasować jeden lub więcej parametrów — a nie wywołać funkcję w klasy podstawowej, 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

  

Następujący przykładowy pokazuje, że jest możliwe, aby ukryć funkcję, chociażby sam podpis, jak klasa pochodna klasy podstawowej.

// 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 kompilowany przy użyciu /clr:oldSyntax.Klasy, które są definiowane przy użyciu rozszerzenia zarządzane dla języka C++ mają 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ładnik, który jest zbudowany w poprzednim przykładzie.Zawiadomienie jak ukryć przez podpisu funkcji nie jest stosowane do klas bazowych typów, które są kompilowane 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 tworzony i zniszczone pod tym samym adresem.

Jednak, jeśli /clr jest używana do kompilowania i funkcji, która jest kompilowana w celu wywołania MSIL native 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 tego samego, czy kod jest śledzenie obiektów według adresów.

Aby uzyskać dodatkowe informacje, zobacz /clr (Kompilacja środowiska uruchomieniowego języka wspólnego).

Poniższy przykład demonstruje, kiedy Konstruktor kopiujący nie jest generowane.

// 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 typ odwołania wykonywać deterministyczne oczyszczania zasobów.Finalizatorów Oczyszczanie niezarządzanych zasoby i może być wywoływana deterministically przez destruktor, lub nondeterministically przez moduł garbage collector.Informacje destruktorów w C++, zobacz Destruktory (C++).

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

Zachowanie destruktorów w zarządzanej klasie 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 destruktora.

Moduł garbage collector CLR powoduje usunięcie nieużywanych obiektów zarządzanych i zwalnia pamięć ich, gdy nie są już wymagane.Jednakże typu mogą używać zasobów, które moduł garbage collector nie potrafi do wydania.Zasoby te są znane jako niezarządzanych zasoby (macierzysty dojść do pliku, na przykład).Firma Microsoft zaleca zwolnieniem wszystkich niezarządzanych zasobów w finalizatorów.Ponieważ zarządzane zasoby są zwalniane nondeterministically przez moduł garbage collector, nie jest bezpiecznie odnoszą się do zarządzanych zasobów w finalizatorów ponieważ istnieje możliwość, że moduł garbage collector ma już oczyszczone tego zarządzanego zasobu.

Finalizatorów Visual C++ nie jest taki sam, jak Finalize metody. (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 finalizatorów klas pochodnych nie powoduje kompilatora wywołać finalizatorów wszystkich klas podstawowych.

Ponieważ kompilator języka Visual C++ obsługuje deterministyczne uwolnienie zasobów, nie próbuj implementuje Dispose lub Finalize metody.Jednak użytkownicy zaznajomieni z tych metod, Oto jak mapa finalizatorów Visual C++ i destruktor, który wywołuje finalizatorów do 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();
   }
}

Typ zarządzany może również wykorzystywać zarządzanych zasobów, które chcesz zwolnić deterministically i nie pozostawić garbage collector nondeterministically zwolnienie w pewnym momencie po obiekt nie jest już wymagane.Deterministyczne uwolnienie zasobów może znacznie poprawić wydajność.

Kompilator języka Visual C++ umożliwia definicji destruktora deterministically oczyszczenie obiektów.Aby zwolnić wszystkie zasoby, które chcesz zwolnić deterministically, należy użyć destruktora. Jeśli występuje finalizatorów wywołać ją z destruktor, 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 używa typu nie wymaga destruktor, moduł garbage collector ostatecznie zwalnia wszystkie zasoby zarządzane.

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

Wywołanie destruktora pomija — za pomocą SuppressFinalize— finalizacja obiektu.Jeśli zostanie on nie jest wywoływana, finalizatorów typ swojego ostatecznie zostanie wywołany przez moduł garbage collector.

Deterministically czyszczenia zasobów nazwę obiektu przez wywołanie destruktora może zwiększyć wydajność w porównaniu z najmu nondeterministically sfinalizowania obiektu CLR.

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

Jeśli Twój typ zużywanych przez klienta, który jest napisany w innym języku, destruktor Nazywa się w następujący sposób:

  • Na wywołanie Dispose.

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

  • Jeśli typ wykracza poza zakres języka C# using instrukcji.

Jeśli tworzysz obiekt typu odwołania na stercie zarządzanych (bez użycia semantyka stosu dla typu odwołania), użyj try-finally składni, aby zapewnić wyjątek nie uniemożliwia 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 destruktora typu do wywołania.Gdy typ jest zużywany w kliencie programu Visual C++, nie można bezpośrednio wywołać Dispose; Zamiast tego wywołania destruktor 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) metoda, zgodnie z wzorcem projektowania. (Aby uzyskać informacje, zobacz Implementing Finalize and Dispose to Clean Up Unmanaged Resources).Nie można jawnie autor lub wywołania Dispose(bool) w programie Visual C++.

Jeśli typ ma klasy podstawowej, która jest zgodne z wzorem projektu, destruktory dla wszystkich klas podstawowych są nazywane po wywołaniu destruktora w klasie pochodnej. (Jeśli Twój typ został napisany w języku Visual C++, kompilator zapewnia, że Twój rodzaje wdrożenia tego wzorca.) Innymi słowy, destruktor klasy reference łańcuchów do jej podstawy i elementów członkowskich określonych przez C++ standard — pierwszy destruktora tej klasy jest Uruchom, a następnie destruktory swoich członków w odwrotnej kolejności, w którym zostały skonstruowane, a na końcu destruktory jej klas podstawowych w odwrotnej kolejności, w którym zostały skonstruowane.

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

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

Po uruchomieniu obiektu finalizatorów finalizatorów w dowolnym klas podstawowych są również nazywane, począwszy od najmniej pochodnych typu.Finalizatorów danych członków są nie automatycznie powiązane przez finalizatorów klasy.

Jeśli finalizatorów usuwa wskaźniku macierzystym w typu zarządzanego, należy się upewnić, że odwołania lub za pośrednictwem wskaźniku macierzystym nie są zbierane przedwcześnie; wywołanie destruktora na typ zarządzany, zamiast korzystać z KeepAlive.

W czasie kompilacji może wykryć, czy typ ma finalizatorów lub destruktora.Aby uzyskać dodatkowe informacje, zobacz Obsługa kompilatora cech typu.

Następny przykład pokazuje dwa typy, który ma niezarządzanych zasoby i który ma zarządzanych zasobów, wydane 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 strukturach (zarządzanych)

Klasy i strukturach (zarządzanych)