Cómo: Definir y utilizar clases y structs (C++/CLI)

En este artículo se muestra cómo definir y utilizar tipos de referencia definido por el usuario y tipos de valor de C++/CLI.

Creación de instancias de objeto

Los tipos y los tipos de valor de (ref) de referencia pueden crearse instancias sólo en el montón administrado, no en la pila o en el montón 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;
}

Implícitamente clases abstractas

Una clase implícitamente abstracta no se puede crear instancias.Una clase es implícitamente abstracta si el tipo base de la clase es una interfaz y la clase no implementa las funciones miembro de la interfaz.

Si no puede construir objetos de una clase que se derive de una interfaz, el motivo puede ser que la clase es implícitamente abstracta.Para obtener más información sobre las clases abstractas, vea resumen.

El ejemplo de código siguiente se muestra que la clase de MyClass no se pueden crear instancias porque la función MyClass::func2 no se implementa.Para permitir que el ejemplo para compilar, quite 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.
}

Visibilidad de tipo

Puede controlar la visibilidad de los tipos de (CLR) de Common Language Runtime de modo que, si se hace referencia a un ensamblado, los tipos del ensamblado pueden estar visible o invisible fuera del ensamblado.

public indica que un tipo está visible para cualquier archivo de código fuente que contenga una directiva de #using del ensamblado que contiene el tipo.private indica que un tipo no es visible para los archivos de código fuente que contienen una directiva de #using del ensamblado que contiene el tipo.Sin embargo, los tipos privados son visibles dentro del mismo ensamblado.De forma predeterminada, la visibilidad de una clase es private.

De forma predeterminada antes de Visual C++ 2005, los tipos nativos tenían accesibilidad pública fuera del ensamblado.Permiso Advertencia del compilador (nivel 1) C4692 para ayudarle a ver dónde se utilizan los tipos nativos privados incorrectamente.Utilice el pragma de make_public para dar accesibilidad pública un tipo nativo en un archivo de código fuente que no puede modificar.

Para obtener más información, vea directiva #using (C++).

El ejemplo siguiente se muestra cómo declarar tipos y especificar su accesibilidad, y después tiene acceso a estos tipos en el ensamblado.Por supuesto, si se hace referencia a un ensamblado que tiene tipos privados mediante #using, solo los tipos públicos del ensamblado es 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();
}

Output

  

Ahora, reescribamos el ejemplo anterior para generar como 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");}
};

El ejemplo siguiente se muestra cómo obtener acceso a tipos fuera del ensamblado.En este ejemplo, el cliente utiliza el componente que se integra en el ejemplo anterior.

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

  

Visibilidad de miembro

Puede crear el acceso a un miembro de una clase pública dentro del mismo ensamblado diferente que tener acceso al desde fuera del ensamblado mediante pares de los especificadores public, protected, y private de acceso

Esta tabla resume el efecto de los distintos especificadores de acceso:

Especificador

Efecto

public

El miembro está accesible dentro y fuera del ensamblado.Para obtener más información, consulte público (C++).

private

El miembro no es accesible, ni dentro o fuera del ensamblado.Para obtener más información, consulte private (C++).

protected

El miembro está accesible dentro y fuera del ensamblado, pero solo a tipos derivados.Para obtener más información, consulte protected (C++).

internal

El miembro es público dentro del ensamblado pero privado fuera del ensamblado.internal es una palabra clave contextual.Para obtener más información, vea Palabras clave contextuales (Extensiones de componentes de C++).

public protected
-or-
protected public

El miembro es público dentro del ensamblado pero se protege fuera del ensamblado.

private protected
-or-
protected private

Proteger el miembro dentro del ensamblado pero privado fuera del ensamblado.

El ejemplo siguiente se muestra un tipo público que tenga miembros declarados con distintas accesibilidades y, a continuación muestra el acceso de esos miembros desde dentro del ensamblado.

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

  

Ahora compilemos el ejemplo anterior como una 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("=======================");
   }
};

El ejemplo siguiente utiliza el componente creado en el ejemplo anterior, y por tanto muestra cómo tener acceso a los miembros desde fuera del ensamblado.

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

  

Clases nativas públicas y privadas

Un tipo nativo puede hacer referencia a un tipo administrado.Por ejemplo, una función de un tipo administrado puede tomar un parámetro cuyo tipo sea un struct nativo.Si el tipo administrado y función es público en un ensamblado, el tipo nativo también debe ser público.

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

A continuación, cree el archivo de código fuente que utiliza el 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) {}
};

Ahora, compile un cliente:

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

Constructores estáticos

CLR tipo- para el ejemplo, una clase o struct- puede tener un constructor estático que se puede utilizar para inicializar los miembros de datos estáticos.Se llama a lo sumo una vez, y se llama a un constructor estático antes de que un miembro estático de tipo se tiene acceso por primera vez.

Un constructor de instancia siempre se ejecuta después de un constructor estático.

El compilador no puede línea una llamada a un constructor si la clase tiene un constructor estático.El compilador no puede línea una llamada a ningún miembro que la función si la clase es un tipo de valor, tener un constructor estático, y no tiene un constructor de instancia.CLR puede escrito la llamada, pero el compilador no puede.

Defina un constructor estático como función miembro privado, porque está diseñado para ser llamado solo por CLR.

Para obtener más información sobre constructores estáticos, vea Cómo: Definir un constructor estático de interfaz (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

  

Semántica de este puntero

Cuando se usa Visual C++ para definir tipos, el puntero de this en un tipo de referencia es de tipo “identificador”.El puntero de this en un tipo de valor es de tipo “puntero interior”.

Este una semántica diferente al puntero de this puede provocar un comportamiento inesperado cuando se llama a un indizador predeterminado.El ejemplo siguiente se muestra la manera correcta de tener acceso a un indizador predeterminado en un tipo de referencia y un tipo de valor.

Para obtener más información, vea

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

  

Funciones de la Piel-por-firma

En C++ estándar, una función de una clase base es ocultada por una función con el mismo nombre en una clase derivada, incluso si la función de clase derivada no tiene el mismo número o clase de parámetros.Esto se denomina semántica de piel-por- nombre.En un tipo de referencia, una función de una clase base se puede ocultar sólo por una función en una clase derivada si el nombre y la lista de parámetros son iguales.Esto se denomina semántica de la piel-por- firma.

Una clase se considera una clase de la piel-por- firma cuando todas sus funciones se marcan en metadatos como hidebysig.De forma predeterminada, todas las clases que se crean en /clr tienen funciones de hidebysig.Sin embargo, una clase compilado utilizando /clr:oldSyntax no tiene funciones de hidebysig; en su lugar, son funciones de piel-por- nombre.Cuando una clase tiene funciones de hidebysig, el compilador no oculta funciones por nombre en cualquier las clases base directas, pero si el compilador encuentra una clase de piel-por- nombre en una cadena de herencia, continúa ese comportamiento de piel-por- nombre.

Con una semántica de la piel-por- firma, cuando una función se llama un objeto, el compilador identifica la clase derivada que contiene una función que podría satisfacer la llamada de función.Si solo hay una función en la clase que podría satisfacer la llamada, el compilador que funcionan.Si hay más de una función en la clase que podría satisfacer la llamada, el compilador utiliza las reglas de resolución de sobrecarga para determinar que funcionan para llamar a.Para obtener más información sobre las reglas de sobrecarga, vea La sobrecarga de la función.

Para una llamada de función especificada, una función de una clase base podría tener una firma que le crea una coincidencia ligeramente mejor que una función en una clase derivada.Sin embargo, si la función explícitamente se invitada un objeto de una clase derivada, la función en la clase derivada se denomina.

Dado que el valor devuelto no se considera parte de la firma de una función, se oculta una función de clase base si tiene el mismo nombre y tiene el mismo número y tipo argumentos que una función de clase derivada, incluso si difiere del tipo de valor devuelto.

El ejemplo siguiente se muestra una función de una clase base no es ocultada por una función en una clase derivada.

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

  

El ejemplo siguiente se muestra que el compilador de Visual C++ llama a una función en el clase- uniforme más derivado si una conversión se requiere coincidir con uno o más de parámetro- y no llamar a una función de una clase base que es una coincidencia mejor para la llamada de función.

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

  

El ejemplo siguiente se muestra que es posible ocultar una función incluso si la clase base tiene la misma firma que la clase derivada.

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

  

El ejemplo siguiente define un componente compilado mediante /clr:oldSyntax.Las clases que se definen mediante Extensiones administradas para C++ tienen funciones miembro de piel-por- nombre.

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

El ejemplo siguiente utiliza el componente que se integra en el ejemplo anterior.Observe que la funcionalidad de la piel-por- firma no se aplica a las clases base de tipos que se compilan mediante /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
}

Constructores de copias

El estándar de C++ indica que un constructor de copias se llama cuando se mueve un objeto, como que un objeto se crea y se destruye en la misma dirección.

Sin embargo, cuando /clr se utiliza para compilar y una función que se compila a las llamadas de MSIL una función nativa donde un natural clase- o más que uno- se pasa por valor y dónde la clase nativa tiene un constructor o destructor de copia, no se llama a ningún constructor de copias y el objeto se destruye en otra dirección donde se creó.Esto podría producir problemas si la clase tiene un puntero en sí mismo, o si el código está realizando objetos address.

Para obtener más información, vea /clr (Compilación de Common Language Runtime).

El ejemplo siguiente se muestra cuando un constructor de copias no se representa.

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

  

Destructores y finalizadores

Destructores en un tipo de referencia realizan una limpieza determinista de recursos.Finalizadores limpia los recursos no administrados y puede llamarse determinista el destructor o no determinista por el recolector de elementos no utilizados.Para obtener información sobre los destructores de C++ estándar, vea Destructores (C++).

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

El comportamiento de destructores en una clase administrada de Visual C++ difieren de Extensiones administradas para C++.Para obtener más información sobre este cambio, vea Cambios en la semántica de los destructores.

El recolector de elementos no utilizados de CLR elimina objetos y libera administrados no usados su memoria cuando ya no son necesarios.Sin embargo, un tipo puede utilizar recursos que el recolector de elementos no utilizados no sabe liberar.Conocen estos recursos como recursos no administrados (identificadores de archivos nativos, por ejemplo).Se recomienda libera todos los recursos no administrados en el finalizador.Dado que el recolector de elementos no utilizados libera los recursos administrados no determinista, no es seguro hacer referencia a recursos administrados en un finalizador porque es posible que el recolector de elementos no utilizados ha limpiado ya a ese recurso administrado.

Un finalizador de Visual C++ no es igual que el método de Finalize.(La documentación del CLR usa el finalizador y el método de Finalize sinónimo).El método de Finalize llama el recolector de elementos no utilizados, que invoca cada finalizador en una cadena de herencia de la clase.A diferencia de los destructores de Visual C++, una llamada al finalizador de la clase derivada no hace que el compilador para invocar el finalizador en todas las clases base.

Dado que el compilador de Visual C++ admite la versión determinista de recursos, no intente implementar los métodos de Dispose o de Finalize.Sin embargo, si está familiarizado con estos métodos, esto es cómo un finalizador de Visual C++ y el destructor que llama al finalizador asignados a Dispose el modelo:

// 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 administrado puede utilizar los recursos administrados que prefiere liberar determinista, y no si el recolector de elementos no utilizados para liberar no determinista en algún momento después de que el objeto se necesite.La versión determinista de recursos puede mejorar significativamente el rendimiento.

El compilador de Visual C++ permite a la definición del destructor determinista para limpiar objetos.Utilice el destructor para liberar todos los recursos que desee determinista para liberar.Si un finalizador está presente, denomínelo destructora, para evitar la duplicación del código.

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

Si el código que utiliza el tipo no llama al destructor, el recolector de elementos no utilizados libera todos finalmente los recursos administrados.

La presencia del destructor no implica la presencia de un finalizador.Sin embargo, la presencia de un finalizador implica que debe definir el destructor y llamar al finalizador desde el destructor.Esto proporciona la versión determinista de recursos no administrados.

Llamando a un destructor suprimir- por utilizar SuppressFinalize— finalización del objeto.Si un destructor no se denomina, el finalizador de tipo se denominará finalmente por el recolector de elementos no utilizados.

Determinista limpiar los recursos de objeto llamando al destructor puede mejorar el rendimiento en comparación con dejar CLR no determinista concluye el objeto.

El código escrito en Visual C++ y se compila con /clr ejecuta un destructor de un tipo si:

Si un cliente está consumiendo el tipo escrito en otro lenguaje, se denomina destructor como sigue:

  • En una llamada a Dispose.

  • En una llamada a Dispose(void) en el tipo.

  • Si el tipo sale de ámbito en la instrucción de C# using.

Si crea un objeto de un tipo de referencia en el montón administrado (no mediante la semántica de la pila para los tipos de referencia), sintaxis de intento-final de uso para asegurarse de que una excepción no evite destructor run.

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

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

Si el tipo tiene un destructor, el compilador genera un método de Dispose que implemente IDisposable.Si un tipo que está escrito en Visual C++ y tiene un destructor que utiliza en otro lenguaje, llamando a IDisposable::Dispose en ese causas de tipo el destructor del tipo de llamar.Cuando utilizan el tipo de un cliente de Visual C++, no puede llamar directamente a Dispose; en su lugar, llame al destructor mediante el operador de delete.

Si el tipo tiene un finalizador, el compilador genera un método de Finalize(void) que invalide Finalize.

Si un tipo tiene un finalizador o destructor, el compilador genera un método de Dispose(bool), según el modelo de diseño.(Para obtener más información, vea Implementing Finalize and Dispose to Clean Up Unmanaged Resources).No puede crear explícitamente o llamar a Dispose(bool) en Visual C++.

Si un tipo tiene una clase base que se ajusta al modelo de diseño, destructores para todas las clases base se llama al destructor de la clase derivada se denomina.(Si escriben el tipo en Visual C++, el compilador garantiza que los tipos implementan este patrón.) Es decir destructor de cadenas de una clase ref en sus bases y miembros especificados por el destructor C++ norma- primer de clase se ejecuta, los destructores para sus miembros en el inverso del orden en que se construyeron y, finalmente destructores para sus clases base en el inverso del orden en que se construyeron.

Los destructores y los finalizadores tienen tipos de valor o interfaces interiores.

Un finalizador se puede definir o declarar únicamente en un tipo de referencia.Como constructor y el destructor, un finalizador no tiene ningún tipo de valor devuelto.

Después de que el finalizador de un objeto ejecute, los finalizadores en las clases base también se denominan, empezando por el tipo menos derivado.Finalizadores para los miembros de datos automáticamente no se encadena al finalizador de la clase.

Si un finalizador elimina un puntero nativo en un tipo administrado, debe asegurarse de que las referencias o a través del puntero nativo no están obtenidas prematuramente; llame a un destructor en el tipo administrado en lugar de utilizar KeepAlive.

En tiempo de compilación, puede detectar si un tipo tiene un finalizador o destructor.Para obtener más información, vea Compatibilidad de compilador para type traits (Extensiones de componentes de C++).

El ejemplo siguiente muestra dos tipos, uno que tenga recursos no administrados y uno que ha administrado recursos que determinista se libera.

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

Vea también

Referencia

Clases y structs (Extensiones de componentes de C++)

Clases y structs (Extensiones de componentes de C++)