Condividi tramite


Procedura: definire e utilizzare classi e struct (C++/CLI)

In questo articolo viene illustrato come definire e utilizzare riferimento a tipi definiti dall'utente e tipi di valore in C++/CLI.

Creazione di istanze di oggetti

I tipi di riferimento e tipi di valore di (ref) possono essere inizializzate solo nell'heap gestito, non nello stack o nell'heap nativo.

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

Classi in modo implicito astratte

Una classe in modo implicito astratta non è possibile creare un'istanza.La classe è implicitamente astratta se il tipo di base della classe è un'interfaccia e la classe non implementa le funzioni membro di un'interfaccia.

Se non è in grado di costruire oggetti da una classe che deriva da un'interfaccia, il motivo è possibile che la classe è implicitamente astratta.Per ulteriori informazioni sulle classi astratte, vedere estrarre.

Nell'esempio di codice viene illustrato che la classe di MyClass non è possibile creare un'istanza in quanto la funzione MyClass::func2 non è implementata.Per consentire all'esempio per compilare, rimuovere il commento 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.
}

Digitare la visibilità

È possibile controllare la visibilità dei tipi di (CLR) di Common Language Runtime in modo che, se un assembly viene fatto riferimento, digitare l'assembly possa essere visibile o non visibile all'esterno dell'assembly.

public indica che un tipo è visibile a qualsiasi file di origine contenente una direttiva di #using per l'assembly contenente il tipo.private indica che un tipo non è visibile ai file di origine contenenti una direttiva di #using per l'assembly contenente il tipo.Tuttavia, i tipi privati sono visibili all'interno dello stesso assembly.Per impostazione predefinita, la visibilità di una classe viene private.

Per impostazione predefinita prima di Visual C++ 2005, il tali tipi serviva accessibilità pubblica l'assembly.Consentire a Avviso del compilatore (livello 1) C4692 per visualizzare dove i tipi nativi privati vengono utilizzati in modo errato.Utilizzare il pragma di make_public per consentire all'accessibilità pubblica a un tipo nativo in un file di codice sorgente che non è possibile modificare.

Per ulteriori informazioni, vedere direttiva #using (C++).

Il seguente codice di esempio mostra come dichiarare i tipi e specificare la l'accessibilità quindi accedere ai tipi nell'assembly.Naturalmente, se un assembly contenente tipi privati viene fatto riferimento tramite #using, solo i tipi pubblici dell'assembly sono visibili.

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

Output

  

Ora, riscriviamo l'esempio precedente in modo da consentirne la 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");}
};

Mostrato nell'esempio riportato ai tipi di accesso all'esterno dell'assembly.In questo esempio, il client viene utilizzato il componente che è incorporata nell'esempio precedente.

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

Output

  

Visibilità del membro

È possibile semplificare l'accesso a un membro di una classe pubblica dallo stesso assembly diverso che l'accesso all'esterno dell'assembly tramite le coppie gli identificatori di accesso public, protectede private

Questa tabella sono illustrati gli effetti dei vari ID di accesso:

Identificatore

Effetto

public

Il membro è interno e esterno all'assembly.Per ulteriori informazioni, vedere pubblico (C++).

private

Il membro non è accessibile, né né nell'assembly.Per ulteriori informazioni, vedere privato (C++).

protected

Il membro è interno e esterno all'assembly, ma solo tipi derivati.Per ulteriori informazioni, vedere protetto (C++).

internal

Il membro è pubblico nell'assembly privato ma fuori dell'assembly.internal è una parola chiave sensibile al contesto.Per ulteriori informazioni, vedere Parole chiave sensibili al contesto (Estensioni del componente C++).

public protected
-or-
protected public

Il membro è pubblico nell'assembly ma è esterno all'assembly.

private protected
-or-
protected private

Il membro è protetto nell'assembly privato ma fuori dell'assembly.

L'esempio seguente mostra un tipo pubblico che dispone di membri che vengono dichiarati con le accessibilità diversi e verrà visualizzato accedere ai membri dell'assembly.

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

Output

  

Ora compiliamo l'esempio precedente come 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("=======================");
   }
};

Nell'esempio seguente viene utilizzato il componente creato nell'esempio precedente e pertanto viene illustrato come accedere ai membri dall'.

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

Output

  

Classi native pubbliche e private

Un tipo nativo possono fare riferimento a un tipo gestito.Ad esempio, una funzione in un tipo gestito può accettare un parametro il cui tipo è una struttura nativa.Se il tipo gestito e funzione è pubblico in un assembly, il tipo nativo deve essere pubblico.

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

Successivamente, creare il file di codice sorgente che utilizza il tipo nativo:

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

A questo punto, compilare un client:

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

#include "mcppv2_ref_class3.h"

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

Costruttori statici

Un tipo CLR, ad esempio una classe o struct- può avere un costruttore statico che può essere utilizzato per inizializzare i membri dati statici.Un costruttore statico viene chiamato al massimo una volta e viene chiamato prima che qualsiasi membro statico del tipo situato la prima volta.

Un costruttore di istanza viene sempre eseguito dopo un costruttore statico.

Il compilatore non rendere inline una chiamata a un costruttore se la classe dispone di un costruttore statico.Il compilatore non rendere inline una chiamata a una funzione membro se la classe è un tipo di valore, ha un costruttore statico e non ha un costruttore di istanza.CLR può inline la chiamata, ma non è possibile.

Definire un costruttore statico come funzione membro privata, poiché deve essere chiamato solo da CLR.

Per ulteriori informazioni sui costruttori statici, vedere Procedura: definire un costruttore statico di interfaccia (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();
}

Output

  

Semantica del puntatore

Quando si utilizza Visual C++ per definire i tipi, il puntatore di this in un tipo di riferimento è di tipo "l".Il puntatore di this in un tipo di valore è di tipo "puntatore interno."

Questo semantica diversa del puntatore di this può generare un comportamento imprevisto quando un indicizzatore predefinito viene chiamato.L'esempio seguente illustra il modo corretto per accedere a un indicizzatore predefinito in un tipo di riferimento che in un tipo di valore.

Per ulteriori informazioni, vedere

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

Output

  

Funzioni di Pellame-da- firma

In C++ standard, una funzione in una classe base è nascosta da una funzione con lo stesso nome in una classe derivata, anche se la funzione della classe derivata non dispone dello stesso numero o tipo di parametri.Questo processo viene definito la semantica di pellame-da- nome.In un tipo di riferimento, una funzione in una classe base può essere nascosto solo da una funzione in una classe derivata se sia il nome che l'elenco di parametri sono uguali.Questa operazione è nota come semantica di pellame-da- firma.

La classe è considerata una classe di pellame-da- firma quando tutte le funzioni sono contrassegnate nei metadati come hidebysig.Per impostazione predefinita, tutte le classi create in /clr hanno funzioni di hidebysig.Tuttavia, la classe compilata utilizzando /clr:oldSyntax non dispone di funzioni di hidebysig ; invece, sono funzioni di pellame-da-nome.Quando la classe dispone di funzioni di hidebysig, il compilatore non nasconda le funzioni al nome in qualsiasi classi di base dirette, ma se viene rilevata una classe di pellame-da- nome in una catena di ereditarietà, continua il comportamento di pellame-da- nome.

Nella semantica di pellame-da- firma, quando una funzione viene chiamata a un oggetto, il compilatore identifica la classe derivata che contiene una funzione che può soddisfare la chiamata di funzione.Se è presente solo una funzione in una classe che può soddisfare la chiamata, le chiamate del compilatore in esecuzione.Se c'è più di una funzione in una classe che può soddisfare la chiamata, il compilatore utilizza le regole di risoluzione di l determinare la funzione da chiamare.Per ulteriori informazioni sulle regole di overload, vedere Overload di funzione.

Per una chiamata di funzione specificata, una funzione in una classe base può avere una firma che rende leggermente più una corrispondenza che una funzione in una classe derivata.Tuttavia, se la funzione in modo esplicito è stata chiamata a un oggetto classe derivata, la funzione nella classe derivata è denominata.

Poiché il valore restituito non viene considerato parte della firma di una funzione, una funzione di classe base è nascosta se ha lo stesso nome e ha lo stesso numero e tipo di argomenti di una funzione di classe derivata, anche se differisce nel tipo del valore restituito.

Nell'esempio seguente viene illustrata una funzione in una classe base non sia nascosto da una funzione in una classe derivata.

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

Output

  

L'esempio seguente mostra che il compilatore Visual C++ chiama una funzione in l classe uguale più derivato se una conversione necessaria per ottenere una corrispondenza con uno o più di parametro- e non chiamare una funzione in una classe base che rappresenta una corrispondenza migliore per la chiamata di funzione.

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

Output

  

Nell'esempio seguente viene illustrato che è possibile nascondere una funzione anche se la classe base ha la stessa firma della classe derivata.

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

Output

  

Nell'esempio seguente viene definito un componente compilata utilizzando /clr:oldSyntax.Le classi definite tramite le estensioni gestite di C++ hanno funzioni membro di pellame-da- nome.

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

Nell'esempio seguente viene utilizzato il componente che è incorporata nell'esempio precedente.Notare che la funzionalità di pellame-da- firma non si applica alle classi base di tipi compilati utilizzando /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
}

Costruttori di copia

Lo standard C++ indica che un costruttore di copia viene chiamato quando un oggetto viene spostato, in modo che un oggetto viene creato e distrutte gli stessi l'indirizzo.

Tuttavia, quando /clr viene utilizzato per compilare e una funzione compilata per le chiamate MSIL di una funzione nativa in cui un nativo classe o più di un viene passato per valore e in cui la classe nativa ha un costruttore di copia e/o un distruttore, nessun costruttore di copia viene chiamato e l'oggetto eliminato a un indirizzo diverso da in cui è stato creato.Ciò potrebbe causare problemi se la classe dispone di un puntatore in se stesso, o se il codice viene tenuta traccia degli oggetti dall'indirizzo.

Per ulteriori informazioni, vedere /clr (Compilazione Common Language Runtime).

Nell'esempio seguente viene illustrato quando un costruttore di copia non viene generato.

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

Output

  

Distruttori e finalizzatori

Distruttori in un tipo di riferimento eseguono una pulitura delle risorse.I finalizzatori puliscono risorse non gestite e possono essere chiamati in modo deterministico dal distruttore o nondeterministically dal Garbage Collector.Per informazioni sui distruttori in C++ standard, vedere Distruttori (C++).

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

Il comportamento dei distruttori in una classe gestita di Visual C++ è diverso rispetto alle estensioni gestite di C++.Per ulteriori informazioni su questa modifica, vedere Modifiche nella semantica del distruttore.

Il Garbage Collector di common language runtime elimina gli oggetti gestiti inutilizzati e liberare la memoria quando non sono più necessari.Tuttavia, un tipo può utilizzare le risorse che il Garbage Collector non sa rilascio.Queste risorse sono note come risorse non gestite (handle di file nativi, ad esempio).È consigliabile rilasciare tutte le risorse non gestite nel finalizzatore.Poiché le risorse gestite vengono rilasciate nondeterministically dal Garbage Collector, non è possibile fare riferimento alle risorse gestite in un finalizzatore poiché è possibile che il Garbage Collector sia già disponibile pulito tale risorsa gestita.

Un finalizzatore di Visual C++ non corrisponde al metodo di Finalize.Nella documentazione di CLR utilizza il finalizzatore e il metodo di Finalize un sinonimo.Il metodo di Finalize viene chiamato dal Garbage Collector, che richiama ciascun finalizzatore nella catena di ereditarietà di classe.A differenza dei distruttori in C++, una chiamata del finalizzatore della classe derivata non indica al compilatore di richiamare il finalizzatore in tutte le classi di base.

Poiché il compilatore di Visual C++ supporta la versione delle risorse, non tentare di implementare i metodi di Finalize o di Dispose.Tuttavia, se si ha dimestichezza con questi metodi, è come un finalizzatore di Visual C++ e un distruttore che chiama il mapping del finalizzatore al modello di Dispose :

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

Un tipo gestito può inoltre possibile utilizzare le risorse gestite che preferireste rilasciare in modo deterministico e non consentito al Garbage Collector di liberare nondeterministically dopodiché che l'oggetto non è più necessario.La versione delle risorse può migliorare significativamente le prestazioni.

Il compilatore di Visual C++ consente alla definizione di un distruttore in modo deterministico per pulire oggetti.Utilizzare il distruttore per rilasciare tutte le risorse desiderate in modo deterministico per rilasciare.Se un finalizzatore è presente, chiamarlo dal distruttore, per evitare la duplicazione di codice.

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

Se il codice che utilizza il tipo non chiama il distruttore, il Garbage Collector eventualmente libera tutte le risorse gestite.

La presenza di un distruttore non implica la presenza di un finalizzatore.Tuttavia, la presenza di un finalizzatore implica che non sia necessario definire un distruttore e chiamare il finalizzatore dal distruttore.Ciò consente alla versione delle risorse non gestite.

Chiamare il distruttore eliminati da utilizzando SuppressFinalize— completamento dell'oggetto.Se il distruttore non viene chiamato, il finalizzatore del tipo che verrà chiamato dal Garbage Collector.

In modo deterministico pulire le risorse dell'oggetto chiamando il distruttore può migliorare le prestazioni rispetto a lasciare CLR nondeterministically finisce l'oggetto.

Il codice scritto in Visual C++ e compilato utilizzando /clr esegue il distruttore di un tipo se:

Se il tipo viene utilizzato da un client scritto in un altro linguaggio, il distruttore viene chiamato come segue:

  • In una chiamata a Dispose.

  • In una chiamata a Dispose(void) sul tipo.

  • Se il tipo area di validità in un'istruzione di using c.

Se si crea un oggetto di un tipo di riferimento nell'heap gestito (non mediante la semantica dello stack per i tipi di riferimento, la sintassi di try-finally di utilizzo per garantire che l'eccezione non impedisce il distruttore di esecuzione.

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

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

Se il tipo ha un distruttore, il compilatore genera un metodo di Dispose che implementa IDisposable.Se un tipo che è scritto in Visual C++ e ha un distruttore utilizzato da un altro linguaggio, chiamando IDisposable::Dispose su tale cause del tipo il distruttore del tipo da chiamare.Quando il tipo viene utilizzato da un client di Visual C++, non è possibile chiamare direttamente Dispose; invece, chiamare il distruttore utilizzando l'operatore di delete.

Se il tipo presenta un finalizzatore, il compilatore genera un metodo di Finalize(void) che esegue l'override di Finalize.

Se un tipo ha un distruttore o un distruttore, il compilatore genera un metodo di Dispose(bool), a seconda del modello di progettazione.(Per informazioni, vedere Implementing Finalize and Dispose to Clean Up Unmanaged Resources).Non è possibile creare esplicitamente oppure chiamare Dispose(bool) in Visual C++.

Se un tipo è una classe base conforme al modello di progettazione, distruttori per tutte le classi di base vengono chiamati quando il distruttore della classe derivata viene chiamato.(Se il tipo viene scritto in Visual C++, il compilatore assicura che i tipi implementano questo modello.) Ovvero il distruttore delle catene di riferimento di una classe relativi alle basi e membri come specificato il distruttore da C++ standard- primo della classe viene eseguito, i distruttori per i relativi membri in inverso dell'ordine in cui sono stati creati e infine distruttori per le relative classi base dell'inversione dell'ordine in cui sono stati creati.

I distruttori e i finalizzatori non sono consentiti i tipi di valore o le interfacce interni.

Un finalizzatore può essere definito o dichiarati solo in un tipo di riferimento.Ad esempio un costruttore e il distruttore, un finalizzatore non ha un tipo restituito.

Dopo che il finalizzatore di un oggetto viene eseguita, i finalizzatori nelle classi di base vengono definiti, a partire dal tipo meno derivato.I finalizzatori per i membri dati non vengono automaticamente concatenati dal finalizzatore di classe.

Se un finalizzatore elimina un puntatore nativo in un tipo gestito, è necessario assicurarsi che i riferimenti a o tramite il puntatore nativo in anticipo non vengono raccolti; chiamare il distruttore sul tipo gestito anziché utilizzare KeepAlive.

In fase di compilazione, è possibile verificare se un tipo ha un distruttore o un distruttore.Per ulteriori informazioni, vedere Supporto del compilatore per tratti di tipo (Estensioni del componente C++).

L'esempio seguente illustra due tipi, uno che dispone di risorse non gestiti e uno che dispone di risorse gestite in modo deterministico rilasciate.

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

Vedere anche

Riferimenti

Classi e struct (Estensioni del componente C++)

Classi e struct (Estensioni del componente C++)