Comment : définir et consommer des classes et des structs (C++/CLI)How to: Define and Consume Classes and Structs (C++/CLI)

Cet article explique comment définir et utiliser des types référence et des types valeur définis par l’utilisateur dans C++/CLI.This article shows how to define and consume user-defined reference types and value types in C++/CLI.

MatièresContents

Instanciation d’objetsObject instantiation

Classes implicitement abstraitesImplicitly abstract classes

Visibilité du typeType visibility

Visibilité des membresMember visibility

Classes natives publiques et privéesPublic and private native classes

Constructeurs statiquesStatic constructors

Sémantique du pointeur thisSemantics of the this pointer

Fonctions de masquage par signatureHide-by-signature functions

Constructeurs de copieCopy constructors

Destructeurs et finaliseursDestructors and finalizers

Instanciation d’objetsObject instantiation

Les types référence (Ref) peuvent uniquement être instanciés sur le tas managé, pas sur la pile ou sur le tas natif.Reference (ref) types can only be instantiated on the managed heap, not on the stack or on the native heap. Les types valeur peuvent être instanciés sur la pile ou le tas managé.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;
}

Classes implicitement abstraitesImplicitly abstract classes

Une classe implicitement abstraite ne peut pas être instanciée.An implicitly abstract class can't be instantiated. Une classe est implicitement abstraite si le type de base de la classe est une interface et que la classe n’implémente pas toutes les fonctions membres de l’interface.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.

Si vous ne parvenez pas à construire des objets à partir d’une classe dérivée d’une interface, cela peut être dû au fait que la classe est implicitement abstraite.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. Pour plus d’informations sur les classes abstraites, consultez abstract.For more information about abstract classes, see abstract.

L’exemple de code suivant montre que la MyClass classe ne peut pas être instanciée, car la fonction MyClass::func2 n’est pas implémentée.The following code example demonstrates that the MyClass class cannot be instantiated because function MyClass::func2 is not implemented. Pour permettre à l’exemple de compiler, supprimez les marques de commentaire 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é du typeType visibility

Vous pouvez contrôler la visibilité des types de common language runtime (CLR) afin que, si un assembly est référencé, les types de l’assembly puissent être visibles ou non visibles à l’extérieur de l’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.

publicindique qu’un type est visible pour tout fichier source qui contient une #using directive pour l’assembly qui contient le type.public indicates that a type is visible to any source file that contains a #using directive for the assembly that contains the type. privateindique qu’un type n’est pas visible dans les fichiers sources qui contiennent une #using directive pour l’assembly qui contient le type.private indicates that a type is not visible to source files that contain a #using directive for the assembly that contains the type. Toutefois, les types privés sont visibles dans le même assembly.However, private types are visible within the same assembly. Par défaut, la visibilité d’une classe est private .By default, the visibility for a class is private.

Par défaut, avant Visual Studio 2005, les types natifs avaient une accessibilité publique en dehors de l’assembly.By default prior to Visual Studio 2005, native types had public accessibility outside the assembly. Activez l' Avertissement du compilateur (niveau 1) C4692 pour vous aider à voir où les types natifs privés sont utilisés de manière incorrecte.Enable Compiler Warning (level 1) C4692 to help you see where private native types are used incorrectly. Utilisez le pragma make_public pour fournir une accessibilité publique à un type natif dans un fichier de code source que vous ne pouvez pas modifier.Use the make_public pragma to give public accessibility to a native type in a source code file that you can't modify.

Pour plus d’informations, consultez #using, directive.For more information, see #using Directive.

L’exemple suivant montre comment déclarer des types et spécifier leur accessibilité, puis comment accéder à ces types à l’intérieur de l’assembly.The following sample shows how to declare types and specify their accessibility, and then access those types inside the assembly. Bien entendu, si un assembly qui a des types privés est référencé à l’aide de #using , seuls les types publics de l’assembly sont visibles.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();
}

SortieOutput

in Public_Class
in Private_Class
in Private_Class_2

Nous allons maintenant réécrire l’exemple précédent pour qu’il soit généré en tant que 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");}
};

L’exemple suivant montre comment accéder aux types en dehors de l’assembly.The next sample shows how to access types outside the assembly. Dans cet exemple, le client consomme le composant qui est généré dans l’exemple précédent.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;
}

SortieOutput

in Public_Class

Visibilité des membresMember visibility

Vous pouvez rendre l’accès à un membre d’une classe publique à partir du même assembly que l’accès à celui-ci à partir de l’extérieur de l’assembly à l’aide de paires de spécificateurs d’accès public , protected et**private**You 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

Ce tableau résume l’effet des différents spécificateurs d’accès :This table summarizes the effect of the various access specifiers:

SpécificateurSpecifier EffetEffect
publicpublic Le membre est accessible à l’intérieur et à l’extérieur de l’assembly.Member is accessible inside and outside the assembly. Pour plus d’informations, consultez public .See public for more information.
privateprivate Le membre n’est pas accessible, ni à l’intérieur ni à l’extérieur de l’assembly.Member is not accessible, neither inside nor outside the assembly. Pour plus d’informations, consultez Private .See private for more information.
protectedprotected Le membre est accessible à l’intérieur et à l’extérieur de l’assembly, mais uniquement aux types dérivés.Member is accessible inside and outside the assembly, but only to derived types. Pour plus d’informations, consultez protégé .See protected for more information.
interneinternal Le membre est public à l’intérieur de l’assembly mais est privé en dehors de l’assembly.Member is public inside the assembly but private outside the assembly. internal est un mot clé contextuel.internal is a context-sensitive keyword. Pour plus d’informations, consultez Mots clés contextuels.For more information, see Context-Sensitive Keywords.
public protégé ou protégépublic protected -or- protected public Le membre est public à l’intérieur de l’assembly, mais protégé à l’extérieur de l’assembly.Member is public inside the assembly but protected outside the assembly.
privée privée protégée ou protégéeprivate protected -or- protected private Le membre est protégé à l’intérieur de l’assembly mais privé à l’extérieur de l’assembly.Member is protected inside the assembly but private outside the assembly.

L’exemple suivant montre un type public qui a des membres déclarés avec les différentes accessibilités, puis qui affiche l’accès de ces membres à partir de l’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();
}

SortieOutput

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

Créons maintenant l’exemple précédent sous la forme d’une 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("=======================");
   }
};

L’exemple suivant utilise le composant qui est créé dans l’exemple précédent et montre donc comment accéder aux membres à partir de l’extérieur de l’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();
}

SortieOutput

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

Classes natives publiques et privéesPublic and private native classes

Un type natif peut être référencé à partir d’un type managé.A native type can be referenced from a managed type. Par exemple, une fonction dans un type managé peut accepter un paramètre dont le type est un struct natif.For example, a function in a managed type can take a parameter whose type is a native struct. Si le type et la fonction managés sont publics dans un assembly, le type natif doit également être public.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;
};

Ensuite, créez le fichier de code source qui utilise le type natif :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) {}
};

À présent, compilez 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);
}

Constructeurs statiquesStatic constructors

Un type CLR (par exemple, une classe ou un struct) peut avoir un constructeur statique qui peut être utilisé pour initialiser des données membres statiques.A CLR type—for example, a class or struct—can have a static constructor that can be used to initialize static data members. Un constructeur statique est appelé au plus une fois, et est appelé avant qu’un membre statique du type ne soit accédé la première fois.A static constructor is called at most once, and is called before any static member of the type is accessed the first time.

Un constructeur d’instance s’exécute toujours après un constructeur statique.An instance constructor always runs after a static constructor.

Le compilateur ne peut pas incorporer un appel à un constructeur si la classe a un constructeur statique.The compiler cannot inline a call to a constructor if the class has a static constructor. Le compilateur ne peut pas incorporer un appel à une fonction membre si la classe est un type valeur, a un constructeur statique et n’a pas de constructeur d’instance.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. Le CLR peut incorporer l’appel, mais le compilateur ne peut pas le faire.The CLR may inline the call, but the compiler cannot.

Définissez un constructeur statique comme une fonction membre privée, car il est destiné à être appelé uniquement par le CLR.Define a static constructor as a private member function, because it is meant to be called only by the CLR.

Pour plus d’informations sur les constructeurs statiques, consultez Comment : définir un constructeur d’interface statique (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();
}

SortieOutput

in static constructor
10
11

Sémantique du pointeur thisSemantics of the this pointer

Lorsque vous utilisez Visual C++ pour définir des types, le this pointeur dans un type référence est de type « handle ».When you are using Visual C++ to define types, the this pointer in a reference type is of type "handle". Le this pointeur dans un type valeur est de type "Interior pointer".The this pointer in a value type is of type "interior pointer".

Ces différentes sémantiques du this pointeur peuvent entraîner un comportement inattendu lorsqu’un indexeur par défaut est appelé.These different semantics of the this pointer can cause unexpected behavior when a default indexer is called. L’exemple suivant illustre la méthode correcte pour accéder à un indexeur par défaut dans un type REF et un type valeur.The next example shows the correct way to access a default indexer in both a ref type and a value type.

Pour plus d'informations, consultez la rubriqueFor 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();
}

SortieOutput

10.89
10.89

Fonctions de masquage par signatureHide-by-signature functions

Dans le langage C++ standard, une fonction d’une classe de base est masquée par une fonction qui porte le même nom dans une classe dérivée, même si la fonction de classe dérivée n’a pas le même nombre ou le même type de paramètres.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. C’est ce que l’on appelle la sémantique de masquage par nom .This is referred to as hide-by-name semantics. Dans un type référence, une fonction d’une classe de base peut uniquement être masquée par une fonction dans une classe dérivée si le nom et la liste des paramètres sont identiques.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. C’est ce que l’on appelle la sémantique de masquage par signature .This is known as hide-by-signature semantics.

Une classe est considérée comme une classe masquer par signature lorsque toutes ses fonctions sont marquées dans les métadonnées sous la forme hidebysig .A class is considered a hide-by-signature class when all of its functions are marked in the metadata as hidebysig. Par défaut, toutes les classes créées sous /CLR ont des hidebysig fonctions.By default, all classes that are created under /clr have hidebysig functions. Quand une classe a hidebysig des fonctions, le compilateur ne masque pas les fonctions par nom dans les classes de base directes, mais si le compilateur rencontre une classe Hide-by-Name dans une chaîne d’héritage, il continue ce comportement de masquage par nom.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.

Sous la sémantique de masquage par signature, lorsqu’une fonction est appelée sur un objet, le compilateur identifie la classe la plus dérivée qui contient une fonction qui peut satisfaire l’appel de fonction.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. S’il n’existe qu’une seule fonction dans la classe qui peut satisfaire l’appel, le compilateur appelle cette fonction.If there is only one function in the class that could satisfy the call, the compiler calls that function. Si plusieurs fonctions de la classe peuvent satisfaire l’appel, le compilateur utilise les règles de résolution de surcharge pour déterminer la fonction à appeler.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. Pour plus d’informations sur les règles de surcharge, consultez surcharge de fonction.For more information about overload rules, see Function Overloading.

Pour un appel de fonction donné, une fonction d’une classe de base peut avoir une signature qui en fait une correspondance légèrement meilleure qu’une fonction dans une classe dérivée.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. Toutefois, si la fonction a été appelée explicitement sur un objet de la classe dérivée, la fonction dans la classe dérivée est appelée.However, if the function was explicitly called on an object of the derived class, the function in the derived class is called.

Étant donné que la valeur de retour n’est pas considérée comme faisant partie de la signature d’une fonction, une fonction de classe de base est masquée si elle porte le même nom et qu’elle accepte le même nombre et le même type d’arguments qu’une fonction de classe dérivée, même si elle diffère dans le type de la valeur de retour.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.

L’exemple suivant montre qu’une fonction d’une classe de base n’est pas masquée par une fonction dans une classe dérivée.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();
}

SortieOutput

Base::Test

L’exemple suivant montre que le compilateur Microsoft C++ appelle une fonction dans la classe la plus dérivée, même si une conversion est requise pour mettre en correspondance un ou plusieurs des paramètres, et n’appelle pas une fonction dans une classe de base qui est une meilleure correspondance pour l’appel de fonction.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);
}

SortieOutput

Derived::Test2

L’exemple suivant montre qu’il est possible de masquer une fonction même si la classe de base a la même signature que la classe dérivée.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);
}

SortieOutput

Derived::Test4
97

Constructeurs de copieCopy constructors

La norme C++ indique qu’un constructeur de copie est appelé lorsqu’un objet est déplacé, de telle sorte qu’un objet est créé et détruit à la même adresse.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.

Toutefois, lorsque /CLR est utilisé pour compiler et qu’une fonction compilée en langage MSIL appelle une fonction native dans laquelle une classe native (ou plusieurs) est passée par valeur et où la classe native a un constructeur de copie et/ou un destructeur, aucun constructeur de copie n’est appelé et l’objet est détruit à une adresse différente de celle où il a été créé.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. Cela peut entraîner des problèmes si la classe a un pointeur vers lui-même, ou si le code effectue le suivi des objets par adresse.This could cause problems if the class has a pointer into itself, or if the code is tracking objects by address.

Pour plus d’informations, consultez /clr (compilation pour le Common Language Runtime).For more information, see /clr (Common Language Runtime Compilation).

L’exemple suivant montre qu’un constructeur de copie n’est pas généré.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);
}

SortieOutput

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

Destructeurs et finaliseursDestructors and finalizers

Les destructeurs dans un type référence effectuent un nettoyage déterministe des ressources.Destructors in a reference type perform a deterministic clean-up of resources. Les finaliseurs nettoient les ressources non managées et peuvent être appelées de manière déterministe par le destructeur ou non déterministe par le garbage collector.Finalizers clean up unmanaged resources and can be called deterministically by the destructor or nondeterministically by the garbage collector. Pour plus d’informations sur les destructeurs en C++ standard, consultez destructeurs.For information about destructors in standard C++, see Destructors.

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

Le comportement des destructeurs dans une classe de Visual C++ managée diffère de Extensions managées pour C++.The behavior of destructors in a managed Visual C++ class differs from Managed Extensions for C++. Pour plus d’informations sur cette modification, consultez modifications de la sémantique du destructeur.For more information about this change, see Changes in Destructor Semantics.

Le garbage collector CLR supprime les objets managés inutilisés et libère leur mémoire lorsqu’ils ne sont plus nécessaires.The CLR garbage collector deletes unused managed objects and releases their memory when they are no longer required. Toutefois, un type peut utiliser des ressources que le garbage collector ne sait pas comment libérer.However, a type may use resources that the garbage collector does not know how to release. Ces ressources sont appelées ressources non managées (Handles de fichiers natifs, par exemple).These resources are known as unmanaged resources (native file handles, for example). Nous vous recommandons de libérer toutes les ressources non managées dans le finaliseur.We recommend that you release all unmanaged resources in the finalizer. Étant donné que les ressources managées sont libérées de manière non déterministe par le garbage collector, il n’est pas possible de faire référence à des ressources managées dans un finaliseur, car il est possible que le garbage collector ait déjà nettoyé cette ressource managée.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 finaliseur Visual C++ n’est pas le même que la Finalize méthode.A Visual C++ finalizer is not the same as the Finalize method. (La documentation CLR utilise finaliseur et la Finalize méthode synonymes).(CLR documentation uses finalizer and the Finalize method synonymously). La Finalize méthode est appelée par le garbage collector, qui appelle chaque finaliseur dans une chaîne d’héritage de classe.The Finalize method is called by the garbage collector, which invokes each finalizer in a class inheritance chain. Contrairement à Visual C++ destructeurs, un appel de finaliseur de classe dérivée ne fait pas en sorte que le compilateur appelle le finaliseur dans toutes les classes de base.Unlike Visual C++ destructors, a derived-class finalizer call does not cause the compiler to invoke the finalizer in all base classes.

Étant donné que le compilateur Microsoft C++ prend en charge la version déterministe des ressources, n’essayez pas d’implémenter les Dispose Finalize méthodes ou.Because the Microsoft C++ compiler supports deterministic release of resources, don't try to implement the Dispose or Finalize methods. Toutefois, si vous êtes familiarisé avec ces méthodes, voici comment un finaliseur Visual C++ et un destructeur qui appelle le finaliseur mappent au Dispose modèle :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 type managé peut également utiliser des ressources managées que vous préférez pouvoir libérer de manière déterministe et ne pas laisser le garbage collector se libérer à un moment donné après que l’objet n’est plus nécessaire.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. La version déterministe des ressources peut améliorer considérablement les performances.The deterministic release of resources can significantly improve performance.

Le compilateur Microsoft C++ permet à la définition d’un destructeur de nettoyer les objets de manière déterministe.The Microsoft C++ compiler enables the definition of a destructor to deterministically clean up objects. Utilisez le destructeur pour libérer toutes les ressources que vous souhaitez libérer de manière déterministe.Use the destructor to release all resources that you want to deterministically release. Si un finaliseur est présent, appelez-le à partir du destructeur pour éviter la duplication de code.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
      // ...
   }
};

Si le code qui utilise votre type n’appelle pas le destructeur, le garbage collector libère finalement toutes les ressources managées.If the code that consumes your type does not call the destructor, the garbage collector eventually releases all managed resources.

La présence d’un destructeur n’implique pas la présence d’un finaliseur.The presence of a destructor does not imply the presence of a finalizer. Toutefois, la présence d’un finaliseur implique que vous devez définir un destructeur et appeler le finaliseur à partir de ce destructeur.However, the presence of a finalizer implies that you must define a destructor and call the finalizer from that destructor. Cela fournit la version déterministe des ressources non managées.This provides for the deterministic release of unmanaged resources.

L’appel du destructeur supprime (à l’aide SuppressFinalize de) la finalisation de l’objet.Calling the destructor suppresses—by using SuppressFinalize—finalization of the object. Si le destructeur n’est pas appelé, le finaliseur de votre type finira par être appelé par le garbage collector.If the destructor is not called, your type's finalizer will eventually be called by the garbage collector.

Le nettoyage déterministe des ressources de votre objet par l’appel du destructeur peut améliorer les performances par rapport à la finalisation non déterministe de l’objet par le CLR.Deterministically cleaning up your object's resources by calling the destructor can improve performance compared with letting the CLR nondeterministically finalize the object.

Le code écrit en Visual C++ et compilé à l’aide de /CLR exécute le destructeur d’un type si :Code that's written in Visual C++ and compiled by using /clr runs a type's destructor if:

Si votre type est consommé par un client écrit dans un autre langage, le destructeur est appelé comme suit :If your type is being consumed by a client that's written in another language, the destructor is called as follows:

  • Sur un appel à Dispose .On a call to Dispose.

  • Sur un appel à Dispose(void) sur le type.On a call to Dispose(void) on the type.

  • Si le type est hors de portée dans une using instruction C#.If the type goes out of scope in a C# using statement.

Si vous créez un objet d’un type référence sur le tas managé (sans utiliser la sémantique de pile pour les types référence), utilisez la syntaxe try-finally pour vous assurer qu’une exception n’empêche pas l’exécution du destructeur.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;
   }
}

Si votre type a un destructeur, le compilateur génère une Dispose méthode qui implémente IDisposable .If your type has a destructor, the compiler generates a Dispose method that implements IDisposable. Si un type écrit en Visual C++ et a un destructeur qui est consommé à partir d’un autre langage, IDisposable::Dispose l’appel de sur ce type entraîne l’appel du destructeur du type.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. Quand le type est consommé à partir d’un client Visual C++, vous ne pouvez pas appeler directement Dispose ; à la place, appelez le destructeur à l’aide de l' delete opérateur.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.

Si votre type a un finaliseur, le compilateur génère une Finalize(void) méthode qui remplace Finalize .If your type has a finalizer, the compiler generates a Finalize(void) method that overrides Finalize.

Si un type a un finaliseur ou un destructeur, le compilateur génère une Dispose(bool) méthode, selon le modèle de conception.If a type has either a finalizer or a destructor, the compiler generates a Dispose(bool) method, according to the design pattern. (Pour plus d’informations, consultez modèle de suppression).(For information, see Dispose Pattern). Vous ne pouvez pas créer ou appeler explicitement Dispose(bool) dans Visual C++.You cannot explicitly author or call Dispose(bool) in Visual C++.

Si un type a une classe de base conforme au modèle de conception, les destructeurs pour toutes les classes de base sont appelés lorsque le destructeur de la classe dérivée est appelé.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. (Si votre type est écrit en Visual C++, le compilateur s’assure que vos types implémentent ce modèle.) En d’autres termes, le destructeur d’une classe de référence est lié à ses bases et à ses membres, comme spécifié par la norme C++. d’abord, le destructeur de la classe est exécuté, puis les destructeurs de ses membres à l’inverse de l’ordre dans lequel ils ont été construits, et enfin les destructeurs de ses classes de base dans l’ordre inverse de leur construction.(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.

Les destructeurs et les finaliseurs ne sont pas autorisés dans les types valeur ou les interfaces.Destructors and finalizers are not allowed inside value types or interfaces.

Un finaliseur peut uniquement être défini ou déclaré dans un type référence.A finalizer can only be defined or declared in a reference type. À l’instar d’un constructeur et d’un destructeur, un finaliseur n’a aucun type de retour.Like a constructor and destructor, a finalizer has no return type.

Après l’exécution du finaliseur d’un objet, les finaliseurs dans toutes les classes de base sont également appelées, en commençant par le type le moins dérivé.After an object's finalizer runs, finalizers in any base classes are also called, beginning with the least derived type. Les finaliseurs pour les membres de données ne sont pas automatiquement chaînés par le finaliseur d’une classe.Finalizers for data members are not automatically chained to by a class’s finalizer.

Si un finaliseur supprime un pointeur natif dans un type managé, vous devez vous assurer que les références vers ou via le pointeur natif ne sont pas collectées prématurément ; appelez le destructeur sur le type managé au lieu d’utiliser 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.

Au moment de la compilation, vous pouvez détecter si un type a un finaliseur ou un destructeur.At compile time, you can detect whether a type has a finalizer or a destructor. Pour plus d’informations, consultez Prise en charge du compilateur pour les caractéristiques de type.For more information, see Compiler Support for Type Traits.

L’exemple suivant montre deux types, un qui a des ressources non managées et un qui contient des ressources managées qui sont libérées de manière déterministe.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");
}

Voir aussiSee also

Classes et structsClasses and Structs
Classes et structsClasses and Structs