Compartilhar via


Como: criar uma instância de Classes e estruturas

Este artigo mostra como definir e consumir tipos definidos pelo usuário de referência e tipos de valor C++/CLI.

Sumário

Instanciação de objeto

Implicitamente classes abstratas

Visibilidade do tipo

Visibilidade do membro

Classes nativas públicas e privadas

Construtores estáticos

Semântica desse ponteiro

funções de Couro-por- assinatura

Construtores de cópia

Destruidores e finalizers

Instanciação de objeto

Os tipos e os tipos de valor de (ref) de referência só podem ser criada uma instância no heap gerenciado, não na pilha ou heap nativo.

// mcppv2_ref_class2.cpp
// compile with: /clr
ref class MyClass {
public:
   int i;

   // nested class
   ref class MyClass2 {
   public:
      int i;
   };

   // nested interface
   interface struct MyInterface {
      void f();
   };
};

ref class MyClass2 : public MyClass::MyInterface {
public:
   virtual void f() {
      System::Console::WriteLine("test");
   }
};

public value struct MyStruct {
   void f() {
      System::Console::WriteLine("test");
   }   
};

int main() {
   // instantiate ref type on garbage-collected heap
   MyClass ^ p_MyClass = gcnew MyClass;
   p_MyClass -> i = 4;

   // instantiate value type on garbage-collected heap
   MyStruct ^ p_MyStruct = gcnew MyStruct;
   p_MyStruct -> f();

   // instantiate value type on the stack
   MyStruct p_MyStruct2;
   p_MyStruct2.f();

   // instantiate nested ref type on garbage-collected heap
   MyClass::MyClass2 ^ p_MyClass2 = gcnew MyClass::MyClass2;
   p_MyClass2 -> i = 5;
}

Implicitamente classes abstratas

Implicitamente uma classe abstrata não pode ser criada uma instância. Uma classe abstrata é implicitamente se o tipo de base da classe é uma interface e a classe não implementa funções de membro de qualquer interface.

Se você não conseguir criação de objetos de uma classe que foi derivada de uma interface, o motivo poderá ser que a classe é implicitamente abstrato. Para obter mais informações sobre classes abstratas, consulte sumário.

O exemplo de código a seguir demonstra que a classe de MyClass não pode ser criada uma instância porque a função MyClass::func2 não é implementada. Para habilitar o exemplo para criar, remover 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.
}

Visibilidade do tipo

Você pode controlar a visibilidade dos tipos de Common Language Runtime (CLR) de modo que, se um assembly for referenciado, digite no assembly possa ser visível ou não visível fora do assembly.

public indica que um tipo é visível para qualquer arquivo de origem que contenha uma política de #using para o assembly que contém o tipo. private indica que um tipo não é visível aos arquivos de origem que contêm uma política de #using para o assembly que contém o tipo. Porém, os tipos particulares são visíveis dentro do mesmo assembly. Por padrão, a visibilidade de uma classe é private.

Por padrão antes do Visual C++ 2005 nativo, o tipo tinha a acessibilidade pública do assembly. Habilitar Aviso do compilador (nível 1) C4692 para ajudar você a ver onde os tipos nativos particulares são usados incorretamente. Use o pragma de make_public para dar a acessibilidade pública a um nativo em um arquivo de código-fonte que você não possa alterar.

Para obter mais informações, consulte Diretiva #using (C++).

O exemplo a seguir mostra como declarar tipos e especificar sua acessibilidade, e acessar esses tipos no assembly. Logicamente, se um assembly com tipos particulares é referenciado usando #using, somente utilitário no assembly será visível.

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

Saída

  

Agora, vamos reescrever o exemplo anterior de modo que é criado como uma 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");}
};

O exemplo a seguir mostra como acessar tipos fora do assembly. Neste exemplo, o cliente que consome o componente é criado no exemplo 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;
}

Saída

  

Visibilidade do membro

Você pode fazer o acesso a um membro de uma classe pública de dentro do mesmo assembly diferente do que o acesso a ele fora do assembly usando pares dos especificadores public, protected, e privatede acesso

Esta tabela resume o efeito de vários especificadores do access:

Especificador

Efeito

public

O membro é acessível dentro e fora do assembly. Consulte public (C++) para maiores informações.

private

O membro não for acessível, ou dentro ou fora do assembly. Consulte private (C++) para maiores informações.

protected

O membro é acessível dentro e fora do assembly, mas apenas a tipos derivados. Consulte protected (C++) para maiores informações.

internal

O membro é public dentro do assembly privado mas fora do assembly. internal é uma palavra-chave contextual. Para obter mais informações, consulte Palavras-chave Contextuais (Extensões de Componentes C++).

public protected
-or-
protected public

O membro é public dentro do assembly mas é protegido fora do assembly.

private protected
-or-
protected private

O membro é protegido no assembly privado mas fora do assembly.

O exemplo a seguir mostra um tipo utilitário que tem os membros que são declarados com as acessibilidades diferentes, e mostra acessar dos membros de dentro do assembly.

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

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

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

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

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

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

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

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

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

int main() {
   Public_Class ^ a = gcnew Public_Class;
   MyClass ^ b = gcnew MyClass;
   a->Public_Function();
   a->Protected_Public_Function();
   a->Public_Protected_Function();

   // accessible inside but not outside the assembly
   a->Internal_Function();

   // call protected functions
   b->Test();

   // not accessible inside or outside the assembly
   // a->Private_Function();
}

Saída

  

Agora vamos compilar o exemplo anterior como uma 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("=======================");
   }
};

O exemplo a seguir utiliza o componente que é criado no exemplo anterior, e assim mostra como acessar os membros fora do assembly.

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

Saída

  

Classes nativas públicas e privadas

Um tipo nativo do pode ser referenciado de um tipo gerenciado. Por exemplo, uma função em um tipo gerenciado pode usar um parâmetro cujo tipo é uma estrutura nativo. Se o tipo gerenciado e função é public em um assembly, o tipo nativo também deve ser público.

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

Em seguida, crie o arquivo de origem que consome o 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) {}
};

Agora, crie um 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);
}

Construtores estáticos

Tipo CLR para o exemplo, uma classe ou struct- pode ter um construtor estático que pode ser usado para inicializar membros de dados estáticos. Um construtor estático é chamado no máximo uma vez, e é chamado antes que qualquer membro de tipo estático é acessado na primeira vez.

Um construtor da instância sempre é executado após um construtor estático.

O compilador não puder embutido uma chamada a um construtor se a classe tiver um construtor estático. O compilador não puder embutido uma chamada a qualquer função de membro se a classe for um tipo de valor, tem um construtor estático, e não tiver um construtor da instância. CLR puder embutido a chamada, mas o compilador não pode.

Define um construtor estático como uma função de membro particular, pois se significa ser chamado somente por CLR.

Para obter mais informações sobre os construtores estáticos, consulte Como definir um construtor estático de interface (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();
}

Saída

  

Semântica desse ponteiro

Quando você estiver usando o Visual C++ para definir tipos, o ponteiro de this em um tipo de referência é do tipo “identificador”. O ponteiro de this em um tipo de valor é do tipo “ponteiro interior”.

Este semânticas diferentes do ponteiro de this pode provocar comportamento inesperado quando um indicador padrão é chamado. O exemplo a seguir mostra a forma correta de acessar um indicador padrão em um tipo de referência e em um tipo de valor.

Para obter mais informações, consulte

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

Saída

  

Funções de Couro-por- assinatura

No padrão C++, uma função em uma classe base é ocultada por uma função que tenha o mesmo nome em uma classe derivada, mesmo se a função de classe derivada não tem o mesmo número e tipo dos parâmetros. Isso é conhecido como semântica de couro-por- nome . Em um tipo de referência, uma função em uma classe base só pode ser ocultada por uma função em uma classe derivada se o nome e a lista de parâmetros são os mesmos. Isso é conhecido como a semântica de couro-por- assinatura .

Uma classe é considerada uma classe de couro-por- assinatura quando todas as funções são marcadas em metadados como hidebysig. Por padrão, todas as classes que são criadas em /clr têm funções de hidebysig . No entanto, uma classe que é criada usando /clr:oldSyntax não tem funções de hidebysig ; em vez disso, são funções de couro-por- nome. Quando uma classe tem funções de hidebysig , o compilador não oculta funções por nome em alguns classes base diretas, mas se o compilador encontrar uma classe de couro-por- nome em uma cadeia de herança, continua o comportamento de couro-por- nome.

Na semântica de couro-por- assinatura, quando uma função é chamada um objeto, o compilador identifica a maioria de classe derivada que contém uma função que pode atender a chamada de função. Se houver apenas uma função da classe que pode atender a chamada, as chamadas do compilador que funcionam. Se houver mais de uma função da classe que pode atender a chamada, o compilador usa regras de resolução de sobrecarga para determinar quais função a ser chamada. Para obter mais informações sobre regras de sobrecarga, consulte Sobrecarga de função.

Para uma chamada de função específica, uma função em uma classe base pode ter uma assinatura que você faça uma correspondência clara melhor do que uma função em uma classe derivada. No entanto, se a função foi chamado explicitamente um objeto de classe derivada, a função na classe derivada é chamada.

Como o valor de retorno não é considerado parte da assinatura de uma função, uma função da classe base é ocultada se tem o mesmo nome e usar o mesmo número e tipo dos argumentos que uma função de classe derivada, mesmo que difere no tipo do valor de retorno.

O exemplo a seguir mostra que uma função em uma classe base não estiver oculta por uma função em uma classe 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();
}

Saída

  

O exemplo seguinte mostra que o compilador do Visual C++ chama uma função no mais uniforme classe derivada se uma conversão é necessária para corresponder a um ou mais parâmetros do e não chamar uma função em uma classe base que é uma melhor correspondência para a chamada de função.

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

Saída

  

O exemplo a seguir mostra que é possível ocultar uma função mesmo se a classe base tem a mesma assinatura que a classe 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);
}

Saída

  

O exemplo a seguir define um componente que é construído usando /clr:oldSyntax. As classes que são definidas usando extensões gerenciadas para C++ têm funções de membro de couro-por- nome.

// hide_by_signature_4.cpp
// compile with: /clr:oldSyntax /LD
using namespace System;
public __gc struct Base0 {
   void Test() { 
      Console::WriteLine("in Base0::Test");
   }
};

public __gc struct Base1 : public Base0 {
   void Test(int i) { 
      Console::WriteLine("in Base1::Test");
   }
};

O exemplo a seguir utiliza o componente que é criado no exemplo anterior. Observe como a funcionalidade de couro-por- assinatura não é aplicada às classes base de tipos que são criados usando /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
}

Construtores de cópia

O padrão do C++ pressupõe que um construtor de cópia é chamado quando um objeto é movido, de modo que um objeto é criado e destruído no mesmo endereço.

No entanto, quando /clr é usado para criar e uma função que é criada para chamadas de MSIL uma função nativo onde um classe nativo ou mais do que um valor é passado por e onde a classe nativo tem um construtor e/ou um destruidor de cópia, nenhum construtor de impressão é chamado e o objeto é destruído em um endereço diferente de onde foi criado. Isso pode causar problemas se a classe tiver um ponteiro em se, ou se o código estiver rastreando objetos pelo endereço.

Para obter mais informações, consulte /clr (compilação do Common Language Runtime).

O exemplo a seguir demonstra quando um construtor de impressão não é gerado.

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

Saída

  

Destruidores e finalizers

Os destruidores em um tipo de referência executa uma limpeza determinística de recursos. Finalizers limpa os recursos não gerenciados e pode ser chamado de forma determinista por destruidor ou nondeterministically pelo coletor de lixo. Para obter informações sobre os destruidores padrão do C++, consulte Destruidores (C++).

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

O comportamento de destruidores em uma classe gerenciada do Visual C++ difere das extensões gerenciadas para C++. Para obter mais informações sobre essa alteração, consulte Alterações feitas na semântica do destruidor.

O coletor de lixo de CLR excluir objetos não gerenciados usado e libere a memória quando são necessários já não. No entanto, um tipo pode usar os recursos que o coletor de lixo não sabe se liberar. Esses recursos são conhecidos como recursos não gerenciados (identificadores de arquivos nativos, por exemplo). Recomendamos que você libera todos os recursos não gerenciados em finalizer. Como os recursos gerenciados são liberados nondeterministically pelo coletor de lixo, não é seguro fazer referência aos recursos gerenciados em um finalizador pois é possível que o coletor de lixo já limpou esse recurso gerenciado.

Um finalizador do Visual C++ não é o mesmo que o método de Finalize . (A documentação de CLR usa o finalizador e o método de Finalize sinònima). O método de Finalize é chamado pelo coletor de lixo, que invoca cada finalizador em uma cadeia de herança da classe. Diferentemente dos destruidores do Visual C++, uma chamada finalizadores da classe derivada não faz com que o compilador chama o finalizador em todas as classes base.

Como o compilador do Visual C++ da suporte à versão determinística de recursos, não tente implementar os métodos de Dispose ou de Finalize . No entanto, se você estiver familiarizado com esses métodos, veja como um finalizador do Visual C++ e um destruidor que chama o mapa finalizadores ao padrão de Dispose :

// Visual C++ code
ref class T {
   ~T() { this->!T(); }   // destructor calls finalizer
   !T() {}   // finalizer
};

// equivalent to the Dispose pattern
void Dispose(bool disposing) {
   if (disposing) {
      ~T();
   } else {
      !T();
   }
}

Um tipo gerenciado também pode usar os recursos gerenciados do que você prefere liberar de forma determinista, e não os sair do coletor de lixo para liberar nondeterministically em algum momento depois que o objeto é mais necessário. A versão determinística de recursos pode melhorar significativamente o desempenho.

O compilador do Visual C++ habilita a definição de um destruidor para limpar de forma determinista objetos. Use o destruidor para liberar todos os recursos que você deseja liberar de forma determinista. Se um finalizador estiver presente, a chamada de destruidor, para evitar a duplicação de 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
      // ...
   }
};

Se o código que consome seu tipo não chama o destruidor, o coletor de lixo se houver libera todos os recursos gerenciados.

A presença de um destruidor não implica a presença de um finalizador. No entanto, a presença de um finalizador significa que você deve definir um destruidor e chamar o finalizador desse destruidor. Isso oferece a versão determinística dos recursos não gerenciados.

Chamando o destruidor suprimir- por usar SuppressFinalize— acabamento do objeto. Se o destruidor não for chamado, o finalizador de seu tipo será chamado se houver pelo coletor de lixo.

De forma determinista limpar os recursos do objeto chamando o destruidor pode melhorar o desempenho em comparação com deixar CLR finaliza nondeterministically o objeto.

Código que é escrito em Visual C++ e compilado com /clr executa o destruidor de um tipo se:

Se seu tipo está sendo consumido pelo cliente que foi gravado em outro idioma, o destruidor é chamado como segue:

  • Em uma chamada para Dispose.

  • Em uma chamada para Dispose(void) no tipo.

  • Se o tipo sai do escopo em uma instrução do C# using .

Se você criar um objeto de tipo de referência no heap gerenciado (que não usam a semântica da pilha para tipos de referência), use a sintaxe de tentativa-final para assegurar que uma exceção não impede o destruidor de execução.

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

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

Se seu tipo tem um destruidor, o compilador gerencie um método de Dispose que implementa IDisposable. Se um tipo que é escrito em Visual C++ e tem um destruidor que foi consumido de outro idioma, chamando IDisposable::Dispose no que faz com que o tipo destruidor do tipo ser chamado. Quando o tipo é consumido de um cliente do Visual C++, você não pode chamar diretamente Dispose; em vez disso, chame o destruidor usando o operador de delete .

Se seu tipo tem um finalizador, o compilador gerencie um método de Finalize(void) que substitui Finalize.

Se um tipo tem um finalizador ou um destruidor, o compilador gerencie um método de Dispose(bool) , de acordo com o padrão de design. (Para obter mais informações, consulte Implementing Finalize and Dispose to Clean Up Unmanaged Resources). Você não pode criar explicitamente ou chame Dispose(bool) no Visual C++.

Se um tipo tem uma classe base que esteja em conformidade com o padrão de design, os destruidores para todas as classes base são chamados quando o destruidor para a classe derivada é chamado. (Se o tipo é escrito em Visual C++, o compilador garante que seus tipos implementa esse padrão.) Ou é o destruidor de cadeias de uma classe de referência para suas bases e membros conforme especificado pelo destruidor C++ correspondentes ao primeiro da classe é executado, os destruidores para seus membros no contrário da ordem em que foram construídos e, finalmente os destruidores para suas classes base no contrário da ordem em que foram criados.

Os destruidores e os finalizers não são permitidas tipos de valor ou interfaces internas.

Um finalizador só pode ser definido ou declarado em um tipo de referência. Como um construtor e um destruidor, um finalizador não tem nenhum tipo de retorno.

Depois que o finalizador de um objeto é executado, os finalizers em qualquer classe base são chamados também, começando com o tipo derivado. Finalizers para membros de dados não é encadeado automaticamente por um finalizador de uma classe.

Se um finalizador exclui um ponteiro nativo em um tipo gerenciado, você deve assegurar que as referências ou pelo ponteiro nativo não sejam coletadas prematuramente; chame o destruidor no tipo gerenciado em vez de usar KeepAlive.

Em tempo de compilação, você pode detectar se um tipo tem um finalizador ou um destruidor. Para obter mais informações, consulte Suporte para Compilador de Traços de Tipo (Extensões de Componentes C++).

O exemplo a seguir mostra dois tipos, um que tem recursos não gerenciados e um gerenciado que os recursos que são liberados de forma determinista.

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

Consulte também

Referência

Classes e Estruturas (Extensões de Componentes C++)

Classes e Estruturas (Extensões de Componentes C++)