Procedura: Definire e usare classi e struct (C++/CLI)How to: Define and Consume Classes and Structs (C++/CLI)

Questo articolo illustra come definire e usare i tipi riferimento definiti dall'utente e tipi di valore in C++/CLI.This article shows how to define and consume user-defined reference types and value types in C++/CLI.

ContenutoContents

Istanza di oggettoObject instantiation

Classi implicitamente astratteImplicitly abstract classes

Visibilità dei tipiType visibility

Visibilità dei membriMember visibility

Classi native pubbliche e privatePublic and private native classes

Costruttori staticiStatic constructors

La semantica del puntatoreSemantics of the this pointer

Funzioni Hide-by-signatureHide-by-signature functions

Costruttori di copiaCopy constructors

Distruttori e finalizzatoriDestructors and finalizers

Istanza di oggettoObject instantiation

I tipi di riferimento (ref) possono solo essere creata un'istanza nell'heap gestito, non nello stack o nell'heap nativo.Reference (ref) types can only be instantiated on the managed heap, not on the stack or on the native heap. È possibile creare istanze di tipi di valori sullo stack o nell'heap gestito.Value types can be instantiated on the stack or the managed heap.

// 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 implicitamente astratteImplicitly abstract classes

Un' classe implicitamente astratta non è possibile creare un'istanza.An implicitly abstract class can't be instantiated. Una classe è implicitamente astratta se il tipo di base della classe è un'interfaccia e la classe non implementa tutte le funzioni membro dell'interfaccia stessa.A class is implicitly abstract if the base type of the class is an interface and the class does not implement all of the interface's member functions.

Se non si riesce a costruire degli oggetti da una classe che deriva da un'interfaccia, il motivo potrebbe essere che la classe è implicitamente astratta.If you are unable to construct objects from a class that's derived from an interface, the reason might be that the class is implicitly abstract. Per altre informazioni sulle classi astratte, vedere astratta.For more information about abstract classes, see abstract.

Nell'esempio di codice seguente viene mostrato che non è possibile creare l'istanza della classe MyClass in quanto la funzione MyClass::func2 non è implementata.The following code example demonstrates that the MyClass class cannot be instantiated because function MyClass::func2 is not implemented. Per consentire la compilazione dell'esempio, rimuovere il commento MyClass::func2.To enable the example to compile, uncomment 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.
}

Visibilità dei tipiType visibility

È possibile controllare la visibilità dei tipi CLR (Common Language Runtime) in modo che, se viene fatto riferimento a un assembly, i tipi nell'assembly possono essere visibili o non visibili all'esterno dell'assembly.You can control the visibility of common language runtime (CLR) types so that, if an assembly is referenced, types in the assembly can be visible or not visible outside the assembly.

public indica che un tipo è visibile ai file di origine che contiene un #using direttiva dell'assembly che contiene il tipo.public indicates that a type is visible to any source file that contains a #using directive for the assembly that contains the type. private indica che un tipo non è visibile ai file di origine che contengono un #using direttiva dell'assembly che contiene il tipo.private indicates that a type is not visible to source files that contain a #using directive for the assembly that contains the type. Tuttavia, i tipi privati sono visibili all'interno dello stesso assembly.However, private types are visible within the same assembly. Per impostazione predefinita, la visibilità di una classe è private.By default, the visibility for a class is private.

Per impostazione predefinita prima di Visual Studio 2005, i tipi nativi avevano accessibilità pubblica all'esterno dell'assembly.By default prior to Visual Studio 2005, native types had public accessibility outside the assembly. Abilitare avviso del compilatore (livello 1) C4692 che consentono di vedere in cui i tipi nativi privati vengono usati in modo non corretto.Enable Compiler Warning (level 1) C4692 to help you see where private native types are used incorrectly. Usare la make_public pragma per consentire a un tipo nativo in un file di codice sorgente che non è possibile modificare l'accessibilità pubblica.Use the make_public pragma to give public accessibility to a native type in a source code file that you can't modify.

Per altre informazioni, vedere Direttiva #using.For more information, see #using Directive.

Nell'esempio seguente viene illustrato come dichiarare i tipi, specificarne l'accessibilità e quindi accedere a questi tipi all'interno dell'assembly.The following sample shows how to declare types and specify their accessibility, and then access those types inside the assembly. Naturalmente, se viene fatto riferimento a un assembly contenente tipi privati tramite #using, sono visibili solo i tipi pubblici nell'assembly.Of course, if an assembly that has private types is referenced by using #using, only public types in the assembly are visible.

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

OutputOutput

in Public_Class
in Private_Class
in Private_Class_2

A questo punto, è possibile riscrivere l'esempio precedente in modo da incorporarlo come una DLL.Now, let's rewrite the previous sample so that it is built as a 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");}
};

Nell'esempio successivo viene illustrato come accedere ai tipi all'esterno dell'assembly.The next sample shows how to access types outside the assembly. In questo esempio, il client utilizza il componente incorporato nell'esempio precedente.In this sample, the client consumes the component that's built in the previous sample.

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

OutputOutput

in Public_Class

Visibilità dei membriMember visibility

È possibile rendere l'accesso a un membro di una classe pubblica dall'interno dello stesso assembly diverso dall'accesso dall'esterno dell'assembly utilizzando coppie di identificatori di accesso public, protected e privateYou can make access to a member of a public class from within the same assembly different than access to it from outside the assembly by using pairs of the access specifiers public, protected, and private

In questa tabella sono riepilogati gli effetti dei vari identificatori di accesso:This table summarizes the effect of the various access specifiers:

IdentificatoreSpecifier EffettoEffect
publicpublic Il membro è accessibile all'interno e all'esterno dell'assembly.Member is accessible inside and outside the assembly. Visualizzare pubblica per altre informazioni.See public for more information.
privateprivate Non è possibile accedere al membro, né all'interno né all'esterno dell'assembly.Member is not accessible, neither inside nor outside the assembly. Visualizzare privato per altre informazioni.See private for more information.
protectedprotected È possibile accedere al membro all'esterno e all'interno dell'assembly, ma solo per i tipi derivati.Member is accessible inside and outside the assembly, but only to derived types. Visualizzare protetti per altre informazioni.See protected for more information.
internalinternal Membro è pubblico all'interno dell'assembly, ma privato all'esterno dell'assembly.Member is public inside the assembly but private outside the assembly. internal è una parola chiave sensibile al contesto.internal is a context-sensitive keyword. Per altre informazioni, vedere parole chiave sensibili al contesto.For more information, see Context-Sensitive Keywords.
pubblico protetto - o - protetto pubblicopublic protected -or- protected public Il membro è pubblico nell'assembly, ma protetto all'esterno dell'assembly.Member is public inside the assembly but protected outside the assembly.
privato protetto - o - protetto privatoprivate protected -or- protected private Il membro è protetto all'interno dell'assembly, ma privato all'esterno dell'assembly.Member is protected inside the assembly but private outside the assembly.

Nell'esempio seguente viene illustrato un tipo pubblico che dispone di membri che vengono dichiarati con le diverse accessibilità e successivamente viene mostrata la modalità di accesso a questi membri dall'interno dell'assembly.The following sample shows a public type that has members that are declared with the different accessibilities, and then shows the accessing of those members from inside the assembly.

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

OutputOutput

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

A questo punto è possibile compilare l'esempio precedente come una DLL.Now let's build the previous sample as a DLL.

// compile with: /clr /LD
using namespace System;
// public type, visible inside and outside the assembly
public ref class Public_Class {
public:
   void Public_Function(){System::Console::WriteLine("in Public_Function");}

private:
   void Private_Function(){System::Console::WriteLine("in Private_Function");}

protected:
   void Protected_Function(){System::Console::WriteLine("in Protected_Function");}

internal:
   void Internal_Function(){System::Console::WriteLine("in Internal_Function");}

protected public:
   void Protected_Public_Function(){System::Console::WriteLine("in Protected_Public_Function");}

public protected:
   void Public_Protected_Function(){System::Console::WriteLine("in Public_Protected_Function");}

private protected:
   void Private_Protected_Function(){System::Console::WriteLine("in Private_Protected_Function");}

protected private:
   void Protected_Private_Function(){System::Console::WriteLine("in Protected_Private_Function");}
};

// a derived type, calls protected functions
ref struct MyClass : public Public_Class {
   void Test() {
      Console::WriteLine("=======================");
      Console::WriteLine("in function of derived class");
      Protected_Function();
      Protected_Private_Function();
      Private_Protected_Function();
      Console::WriteLine("exiting function of derived class");
      Console::WriteLine("=======================");
   }
};

Nell'esempio seguente viene utilizzato il componente creato nell'esempio precedente e quindi viene illustrato come accedere ai membri dall'esterno dell'assembly.The following sample consumes the component that's created in the previous sample, and thereby shows how to access the members from outside the assembly.

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

OutputOutput

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

Classi native pubbliche e privatePublic and private native classes

È possibile fare riferimento a un tipo nativo da un tipo gestito.A native type can be referenced from a managed type. Ad esempio, una funzione in un tipo gestito può accettare un parametro il cui tipo è uno struct nativo.For example, a function in a managed type can take a parameter whose type is a native struct. Se il tipo e la funzione gestiti sono pubblici in un assembly, anche il tipo nativo deve essere pubblico.If the managed type and function are public in an assembly, then the native type must also be public.

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

Successivamente, creare il file di codice sorgente che utilizza il tipo nativo:Next, create the source code file that consumes the native type:

// 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:Now, compile a client:

// 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 staticiStatic constructors

Un tipo CLR, ad esempio una classe o uno struct, può avere un costruttore statico che può essere utilizzato per inizializzare i membri dati statici.A CLR type—for example, a class or struct—can have a static constructor that can be used to initialize static data members. Un costruttore statico viene chiamato al massimo una volta e viene chiamato prima del primo accesso a un qualsiasi membro statico del tipo.A static constructor is called at most once, and is called before any static member of the type is accessed the first time.

Un costruttore di istanza viene eseguito sempre dopo un costruttore statico.An instance constructor always runs after a static constructor.

Il compilatore non può rendere inline una chiamata a un costruttore se la classe dispone di un costruttore statico.The compiler cannot inline a call to a constructor if the class has a static constructor. Il compilatore non può rendere inline una chiamata a qualsiasi funzione membro se la classe è un tipo di valore, dispone di un costruttore statico e non ha un costruttore di istanza.The compiler cannot inline a call to any member function if the class is a value type, has a static constructor, and does not have an instance constructor. La chiamata può essere resa inline da CLR, ma non dal compilatore.The CLR may inline the call, but the compiler cannot.

Definire un costruttore statico come funzione membro privata, poiché ha lo scopo di essere chiamato solo da CLR.Define a static constructor as a private member function, because it is meant to be called only by the CLR.

Per altre informazioni sui costruttori statici, vedere come: Definire un costruttore statico di interfaccia (C++/CLI) .For more information about static constructors, see How to: Define an Interface Static Constructor (C++/CLI) .

// compile with: /clr
using namespace System;

ref class MyClass {
private:
   static int i = 0;

   static MyClass() {
      Console::WriteLine("in static constructor");
      i = 9;
   }

public:
   static void Test() {
      i++;
      Console::WriteLine(i);
   }
};

int main() {
   MyClass::Test();
   MyClass::Test();
}

OutputOutput

in static constructor
10
11

La semantica del puntatoreSemantics of the this pointer

Quando si utilizza Visual C++ per definire i tipi, il puntatore this in un tipo di riferimento è di tipo "handle".When you are using Visual C++ to define types, the this pointer in a reference type is of type "handle". Il puntatore this in un tipo di valore è di tipo "puntatore interno".The this pointer in a value type is of type "interior pointer".

Queste diverse semantiche del puntatore this possono generare un comportamento imprevisto quando viene chiamato un indicizzatore predefinito.These different semantics of the this pointer can cause unexpected behavior when a default indexer is called. Nell'esempio seguente viene illustrato il modo corretto per accedere a un indicizzatore predefinito sia in un tipo di riferimento che in un tipo di valore.The next example shows the correct way to access a default indexer in both a ref type and a value type.

Per altre informazioni, vedereFor more information, see

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

OutputOutput

10.89
10.89

Funzioni Hide-by-signatureHide-by-signature functions

Nel linguaggio C++ standard, una funzione in una classe base viene 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.In standard C++, a function in a base class is hidden by a function that has the same name in a derived class, even if the derived-class function does not have the same number or kind of parameters. Ciò è detto hide-by-name semantica.This is referred to as hide-by-name semantics. In un tipo riferimento, una funzione in una classe base può essere nascosta solo da una funzione in una classe derivata se il nome e l'elenco dei parametri sono gli stessi.In a reference type, a function in a base class can only be hidden by a function in a derived class if both the name and the parameter list are the same. Questo è noto come hide-by-signature semantica.This is known as hide-by-signature semantics.

La classe è considerata una classe hide-by-signature quando tutte le relative funzioni sono contrassegnate nei metadati come hidebysig.A class is considered a hide-by-signature class when all of its functions are marked in the metadata as hidebysig. Per impostazione predefinita, tutte le classi che vengono creati nello /clr hanno hidebysig funzioni.By default, all classes that are created under /clr have hidebysig functions. Quando la classe dispone di funzioni hidebysig, il compilatore non nasconde le funzioni per nome in tutte le classi base dirette, ma se rileva una classe hide-by-name in una catena di ereditarietà, continua il comportamento hide-by-name.When a class has hidebysig functions, the compiler doesn't hide functions by name in any direct base classes, but if the compiler encounters a hide-by-name class in an inheritance chain, it continues that hide-by-name behavior.

Nella semantica hide-by-signature, quando una funzione viene chiamata su un oggetto, il compilatore identifica la classe più derivata che contiene una funzione la quale può soddisfare la chiamata di funzione.Under hide-by-signature semantics, when a function is called on an object, the compiler identifies the most derived class that contains a function that could satisfy the function call. Se è presente solo una funzione nella classe che può soddisfare la chiamata, il compilatore chiama questa funzione.If there is only one function in the class that could satisfy the call, the compiler calls that function. Se è disponibile più di una funzione nella classe che può soddisfare la chiamata, il compilatore utilizza le regole di risoluzione di overload per determinare quale funzione chiamare.If there is more than one function in the class that could satisfy the call, the compiler uses overload resolution rules to determine which function to call. Per altre informazioni sulle regole di overload, vedere overload di funzioni.For more information about overload rules, see Function Overloading.

Per una chiamata di funzione specificata, una funzione in una classe base può avere una firma che rende la corrispondenza leggermente migliore rispetto a una funzione in una classe derivata.For a given function call, a function in a base class might have a signature that makes it a slightly better match than a function in a derived class. Tuttavia, se la funzione è stata chiamata esplicitamente su un oggetto della classe derivata, viene chiamata la funzione nella classe derivata.However, if the function was explicitly called on an object of the derived class, the function in the derived class is called.

Poiché il valore restituito non viene considerato parte della firma di una funzione, una funzione di classe base viene nascosta se ha lo stesso nome e prende lo stesso numero e tipo di argomenti di una funzione di classe derivata, anche se differisce nel tipo del valore restituito.Because the return value is not considered part of a function's signature, a base-class function is hidden if it has the same name and takes the same number and kind of arguments as a derived-class function, even if it differs in the type of the return value.

Nell'esempio seguente viene mostrato che una funzione in una classe base non viene nascosta da una funzione in una classe derivata.The following sample shows that a function in a base class is not hidden by a function in a derived class.

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

OutputOutput

Base::Test

Nell'esempio successivo mostra che Microsoft C++ compilatore chiama una funzione nella classe più derivata, anche se è necessaria una conversione in modo che corrisponda a uno o più parametri e non chiama una funzione in una classe base che è una corrispondenza migliore per la chiamata di funzione.The next sample shows that the Microsoft C++ compiler calls a function in the most derived class—even if a conversion is required to match one or more of the parameters—and not call a function in a base class that is a better match for the function call.

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

OutputOutput

Derived::Test2

Nell'esempio seguente viene illustrato che è possibile nascondere una funzione anche se la classe base ha la stessa firma della classe derivata.The following sample shows that it's possible to hide a function even if the base class has the same signature as the derived class.

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

OutputOutput

Derived::Test4
97

Costruttori di copiaCopy constructors

Nel linguaggio C++ standard, un costruttore di copia viene chiamato quando un oggetto viene spostato. In questo modo un oggetto viene creato ed eliminato definitivamente allo stesso indirizzo.The C++ standard says that a copy constructor is called when an object is moved, such that an object is created and destroyed at the same address.

Tuttavia, quando /clr viene usata per compilazione e una funzione che viene compilata in MSIL chiama una funzione nativa in cui una classe nativa, o più di uno, viene passato per valore e in cui la classe nativa ha un costruttore di copia e/o un distruttore, alcuna copia costruttore viene chiamato e l'oggetto viene eliminato in un indirizzo diverso da quello in cui è stato creato.However, when /clr is used to compile and a function that's compiled to MSIL calls a native function where a native class—or more than one—is passed by value and where the native class has a copy constructor and/or destructor, no copy constructor is called and the object is destroyed at a different address than where it was created. Ciò potrebbe causare problemi se la classe dispone di un puntatore in se stessa o se il codice tiene traccia degli oggetti in base all'indirizzo.This could cause problems if the class has a pointer into itself, or if the code is tracking objects by address.

Per altre informazioni, vedere /clr (Compilazione Common Language Runtime).For more information, see /clr (Common Language Runtime Compilation).

Nell'esempio seguente viene illustrato quando un costruttore di copia non viene generato.The following sample demonstrates when a copy constructor is not generated.

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

OutputOutput

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

Distruttori e finalizzatoriDestructors and finalizers

I distruttori in un tipo di riferimento eseguono una pulitura deterministica delle risorse.Destructors in a reference type perform a deterministic clean-up of resources. I finalizzatori puliscono le risorse non gestite e possono essere chiamati in modo deterministico dal distruttore o da Garbage Collector in modo non deterministico.Finalizers clean up unmanaged resources and can be called deterministically by the destructor or nondeterministically by the garbage collector. Per informazioni sui distruttori in C++ standard, vedere distruttori.For information about destructors in standard C++, see Destructors.

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

Il comportamento dei distruttori in una classe gestita di Visual C++ è diverso rispetto alle estensioni gestite per C++.The behavior of destructors in a managed Visual C++ class differs from Managed Extensions for C++. Per altre informazioni su questa modifica, vedere le modifiche nella semantica del distruttore.For more information about this change, see Changes in Destructor Semantics.

Garbage Collector di CLR elimina gli oggetti gestiti inutilizzati e ne libera la memoria quando non sono più necessari.The CLR garbage collector deletes unused managed objects and releases their memory when they are no longer required. Tuttavia, è possibile che un tipo utilizzi risorse che Garbage Collector non sa come rilasciare.However, a type may use resources that the garbage collector does not know how to release. Queste risorse sono note come risorse non gestite (ad esempio, gli handle di file nativi).These resources are known as unmanaged resources (native file handles, for example). Si consiglia di rilasciare tutte le risorse non gestite nel finalizzatore.We recommend that you release all unmanaged resources in the finalizer. Poiché le risorse gestite vengono rilasciate in maniera non deterministica da Garbage Collector, non è sicuro fare riferimento alle risorse gestite in un finalizzatore poiché è possibile che Garbage Collector le abbia già pulite.Because managed resources are released nondeterministically by the garbage collector, it's not safe to refer to managed resources in a finalizer because it's possible that the garbage collector has already cleaned up that managed resource.

Un finalizzatore di Visual C++ non corrisponde al metodo Finalize.A Visual C++ finalizer is not the same as the Finalize method. La documentazione di CLR utilizza il finalizzatore e il metodo Finalize come sinonimi.(CLR documentation uses finalizer and the Finalize method synonymously). Il metodo Finalize viene chiamato da Garbage Collector, che richiama ciascun finalizzatore nella catena di ereditarietà della classe.The Finalize method is called by the garbage collector, which invokes each finalizer in a class inheritance chain. A differenza dei distruttori Visual C++, una chiamata del finalizzatore della classe derivata non indica al compilatore di richiamare il finalizzatore in tutte le classi base.Unlike Visual C++ destructors, a derived-class finalizer call does not cause the compiler to invoke the finalizer in all base classes.

Poiché Microsoft C++ compilatore supporta il rilascio deterministico delle risorse, non tentare di implementare la Dispose oppure Finalize metodi.Because the Microsoft C++ compiler supports deterministic release of resources, don't try to implement the Dispose or Finalize methods. Tuttavia, se si ha dimestichezza con questi metodi, di seguito è mostrato come un finalizzatore di Visual C++ e un distruttore che chiama il finalizzatore eseguono il mapping al modello Dispose:However, if you're familiar with these methods, here's how a Visual C++ finalizer and a destructor that calls the finalizer map to the Dispose pattern:

// 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 utilizzare le risorse gestite che si preferirebbe rilasciare in modo deterministico e non consente a Garbage Collector di liberarle in modo non deterministico ad un certo punto dopo che l'oggetto non è più necessario.A managed type may also use managed resources that you would prefer to release deterministically, and not leave to the garbage collector to release nondeterministically at some point after the object is no longer required. Il rilascio deterministico delle risorse può migliorare significativamente le prestazioni.The deterministic release of resources can significantly improve performance.

Microsoft C++ compilatore consente la definizione di un distruttore in modo deterministico pulire gli oggetti.The Microsoft C++ compiler enables the definition of a destructor to deterministically clean up objects. Utilizzare il distruttore per rilasciare tutte le risorse che si desidera liberare in modo deterministico.Use the destructor to release all resources that you want to deterministically release. Se è presente un finalizzatore, chiamarlo dal distruttore, per evitare la duplicazione di codice.If a finalizer is present, call it from the destructor, to avoid code duplication.

// 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, Garbage Collector finisce per liberare tutte le risorse gestite.If the code that consumes your type does not call the destructor, the garbage collector eventually releases all managed resources.

La presenza di un distruttore non implica la presenza di un finalizzatore.The presence of a destructor does not imply the presence of a finalizer. Tuttavia, la presenza di quest'ultimo implica la necessità di definire un distruttore e di chiamare il finalizzatore da tale distruttore.However, the presence of a finalizer implies that you must define a destructor and call the finalizer from that destructor. In questo modo viene effettuato il rilascio deterministico delle risorse non gestite.This provides for the deterministic release of unmanaged resources.

La chiamata del distruttore annulla, tramite SuppressFinalize, la finalizzazione dell'oggetto.Calling the destructor suppresses—by using SuppressFinalize—finalization of the object. Se il distruttore non viene chiamato, il finalizzatore del tipo verrà chiamato da Garbage Collector.If the destructor is not called, your type's finalizer will eventually be called by the garbage collector.

La pulizia delle risorse dell'oggetto in modo deterministico tramite la chiamata del distruttore può migliorare le prestazioni rispetto alla finalizzazione dell'oggetto da parte di CLR in modo non deterministico.Deterministically cleaning up your object's resources by calling the destructor can improve performance compared with letting the CLR nondeterministically finalize the object.

Codice scritto in Visual C++ e compilato utilizzando /clr esegue distruttore di un tipo se:Code that's written in Visual C++ and compiled by using /clr runs a type's destructor if:

Se il tipo viene utilizzato da un client scritto in un altro linguaggio, il distruttore viene chiamato come segue:If your type is being consumed by a client that's written in another language, the destructor is called as follows:

  • In una chiamata a Dispose.On a call to Dispose.

  • In una chiamata a Dispose(void) sul tipo.On a call to Dispose(void) on the type.

  • Se il tipo esce dall'ambito in un'istruzione using di C#.If the type goes out of scope in a C# using statement.

Se si crea un oggetto di un tipo di riferimento sull'heap gestito (non mediante la semantica dello stack per i tipi riferimento), usare try-finally sintassi per garantire che un'eccezione non impedisce il distruttore di esecuzione.If you create an object of a reference type on the managed heap (not using stack semantics for reference types), use try-finally syntax to ensure that an exception doesn't prevent the destructor from running.

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

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

Se il tipo dispone di un distruttore, il compilatore genera un metodo Dispose che implementa IDisposable.If your type has a destructor, the compiler generates a Dispose method that implements IDisposable. Se un tipo è scritto in Visual C++ e ha un distruttore utilizzato da un altro linguaggio, la chiamata di IDisposable::Dispose su tale tipo determina la chiamata del distruttore del tipo.If a type that's written in Visual C++ and has a destructor that's consumed from another language, calling IDisposable::Dispose on that type causes the type's destructor to be called. Quando il tipo viene utilizzato da un client Visual C++, non è possibile chiamare direttamente Dispose; invece, chiamare il distruttore utilizzando l'operatore delete.When the type is consumed from a Visual C++ client, you can't directly call Dispose; instead, call the destructor by using the delete operator.

Se il tipo presenta un finalizzatore, il compilatore genera un metodo Finalize(void) che esegue l'override di Finalize.If your type has a finalizer, the compiler generates a Finalize(void) method that overrides Finalize.

Se un tipo ha un finalizzatore o un distruttore, il compilatore genera un metodo Dispose(bool), a seconda del modello di progettazione.If a type has either a finalizer or a destructor, the compiler generates a Dispose(bool) method, according to the design pattern. (Per informazioni, vedere modello Dispose).(For information, see Dispose Pattern). Non è possibile creare o chiamare esplicitamente Dispose(bool) in Visual C++.You cannot explicitly author or call Dispose(bool) in Visual C++.

Se un tipo ha una classe base conforme al modello di progettazione, vengono chiamati i distruttori di tutte le classi base quando viene chiamato il distruttore della classe derivata.If a type has a base class that conforms to the design pattern, the destructors for all base classes are called when the destructor for the derived class is called. Se il tipo è scritto in Visual C++, il compilatore assicura che i tipi implementino questo modello. In altre parole, il distruttore di una classe di riferimento si concatena ai relativi membri e basi come specificato dal linguaggio C++ standard. Innanzitutto viene eseguito il distruttore della classe, quindi vengono eseguiti i distruttori per i relativi membri nell'ordine inverso in cui sono stati creati e infine vengono eseguiti i distruttori per le relative classi base nell'ordine inverso in cui sono stati creati.(If your type is written in Visual C++, the compiler ensures that your types implement this pattern.) In other words, the destructor of a reference class chains to its bases and members as specified by the C++ standard—first the class’s destructor is run, then the destructors for its members in the reverse of the order in which they were constructed, and finally the destructors for its base classes in the reverse of the order in which they were constructed.

I distruttori e i finalizzatori non sono ammessi all'interno di tipi di valore o interfacce.Destructors and finalizers are not allowed inside value types or interfaces.

Un finalizzatore può essere definito o dichiarato solo in un tipo di riferimento.A finalizer can only be defined or declared in a reference type. Analogamente a un costruttore e a un distruttore, un finalizzatore non ha alcun tipo restituito.Like a constructor and destructor, a finalizer has no return type.

Una volta eseguito il finalizzatore di un oggetto, vengono chiamati anche i finalizzatori in ogni classe base a partire dal tipo meno derivato.After an object's finalizer runs, finalizers in any base classes are also called, beginning with the least derived type. I finalizzatori per i membri dati non vengono automaticamente concatenati dal finalizzatore di classe.Finalizers for data members are not automatically chained to by a class’s finalizer.

Se un finalizzatore elimina un puntatore nativo in un tipo gestito, è necessario assicurarsi che i riferimenti al puntatore nativo o attraverso questo non vengano raccolti in anticipo; chiamare il distruttore sul tipo gestito anziché utilizzare KeepAlive.If a finalizer deletes a native pointer in a managed type, you must ensure that references to or through the native pointer are not prematurely collected; call the destructor on the managed type instead of using KeepAlive.

In fase di compilazione, è possibile individuare se un tipo ha un finalizzatore o un distruttore.At compile time, you can detect whether a type has a finalizer or a destructor. Per altre informazioni, vedere supporto del compilatore per tratti di tipo.For more information, see Compiler Support for Type Traits.

Nell'esempio seguente vengono illustrati due tipi, uno che dispone di risorse non gestite e uno che dispone di risorse gestite rilasciate in modo deterministico.The next sample shows two types, one that has unmanaged resources and one that has managed resources that are deterministically released.

// 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 ancheSee also

Classi e structClasses and Structs
Classi e structClasses and Structs