Como: Definir e consumir Classes e Structs (C++ /CLI CLI)How to: Define and Consume Classes and Structs (C++/CLI)

Este artigo mostra como definir e consumir tipos de referência definidos pelo usuário e tipos de valor em C++/CLI.This article shows how to define and consume user-defined reference types and value types in C++/CLI.

ConteúdoContents

Instanciação de objetosObject instantiation

Classes implicitamente abstratasImplicitly abstract classes

Visibilidade de tipoType visibility

Visibilidade de membroMember visibility

Classes nativas públicas e privadasPublic and private native classes

Construtores estáticosStatic constructors

Semântica do ponteiroSemantics of the this pointer

Funções ocultar por assinaturaHide-by-signature functions

Construtores de cópiaCopy constructors

Destruidores e finalizadoresDestructors and finalizers

Instanciação de objetosObject instantiation

Tipos de referência (ref) só podem ser instanciados no heap gerenciado, não na pilha ou no heap nativo.Reference (ref) types can only be instantiated on the managed heap, not on the stack or on the native heap. Tipos de valor podem ser instanciados em pilha ou o heap gerenciado.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 implicitamente abstratasImplicitly abstract classes

Uma classe abstrata implicitamente não pode ser instanciada.An implicitly abstract class can't be instantiated. Uma classe é abstrata implicitamente se o tipo base da classe é uma interface e a classe não implementa todas as funções de membro da 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.

Se não for possível construir objetos de uma classe derivada de uma interface, o motivo pode ser que a classe é abstrata implicitamente.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. Para obter mais informações sobre classes abstratas, consulte abstrata.For more information about abstract classes, see abstract.

O exemplo de código a seguir demonstra que o MyClass classe não pode ser instanciado porque função MyClass::func2 não está implementado.The following code example demonstrates that the MyClass class cannot be instantiated because function MyClass::func2 is not implemented. Para habilitar o exemplo compilar, remova a marca de comentário de 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.
}

Visibilidade de tipoType visibility

Você pode controlar a visibilidade dos tipos common language runtime (CLR) para que, se um assembly for referenciado, os tipos no assembly possam ser visível ou não é visível fora do assembly.You can control the visibility of common language runtime (CLR) types so that, if an assembly is referenced, types in the assembly can be visible or not visible outside the assembly.

public indica que um tipo é visível para qualquer arquivo de origem que contém um #using diretiva para o assembly que contém o tipo.public indicates that a type is visible to any source file that contains a #using directive for the assembly that contains the type. private indica que um tipo não é visível para os arquivos de origem que contêm um #using diretiva para o assembly que contém o tipo.private indicates that a type is not visible to source files that contain a #using directive for the assembly that contains the type. No entanto, os tipos privados são visíveis dentro do mesmo assembly.However, private types are visible within the same assembly. Por padrão, a visibilidade de uma classe é private.By default, the visibility for a class is private.

Por padrão, antes do Visual Studio 2005, os tipos nativos tinham acessibilidade pública fora do assembly.By default prior to Visual Studio 2005, native types had public accessibility outside the assembly. Habilitar aviso do compilador (nível 1) C4692 para ajudá-lo a ver onde privados tipos nativos são usados incorretamente.Enable Compiler Warning (level 1) C4692 to help you see where private native types are used incorrectly. Use o make_public pragma dar acessibilidade pública para um tipo nativo em um arquivo de código fonte que você não pode modificar.Use the make_public pragma to give public accessibility to a native type in a source code file that you can't modify.

Para obter mais informações, confira Diretiva #using.For more information, see #using Directive.

O exemplo a seguir mostra como declarar tipos e especificar sua acessibilidade e, em seguida, acessar esses tipos dentro do assembly.The following sample shows how to declare types and specify their accessibility, and then access those types inside the assembly. Obviamente, se um assembly que possui tipos privados é referenciado usando #using, somente os tipos públicos no assembly serão visíveis.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();
}

SaídaOutput

in Public_Class
in Private_Class
in Private_Class_2

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

O próximo exemplo mostra como acessar tipos fora do assembly.The next sample shows how to access types outside the assembly. Neste exemplo, o cliente consumir o componente que é criado no exemplo anterior.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;
}

SaídaOutput

in Public_Class

Visibilidade de membroMember visibility

Você pode fazer acesso a um membro de uma classe pública de dentro do mesmo assembly diferente de acesso a ele de fora do assembly usando pares de especificadores de acesso public, protected, e privateYou can make access to a member of a public class from within the same assembly different than access to it from outside the assembly by using pairs of the access specifiers public, protected, and private

Esta tabela resume o efeito dos vários especificadores de acesso:This table summarizes the effect of the various access specifiers:

EspecificadorSpecifier EfeitoEffect
públicospublic Membro é acessível dentro e fora do assembly.Member is accessible inside and outside the assembly. Ver pública para obter mais informações.See public for more information.
particularesprivate Membro não está acessível, nem dentro como fora do assembly.Member is not accessible, neither inside nor outside the assembly. Ver privada para obter mais informações.See private for more information.
protegidosprotected Membro é acessível dentro e fora do assembly, mas apenas para tipos derivados.Member is accessible inside and outside the assembly, but only to derived types. Ver protegidos para obter mais informações.See protected for more information.
internointernal Membro é público dentro do assembly, mas privada fora do assembly.Member is public inside the assembly but private outside the assembly. internal é uma palavra-chave contextual.internal is a context-sensitive keyword. Para obter mais informações, consulte contextual as palavras-chave.For more information, see Context-Sensitive Keywords.
público público protegido - ou - protegidopublic protected -or- protected public Membro é público dentro do assembly, mas protegidos fora do assembly.Member is public inside the assembly but protected outside the assembly.
privada privado protegido - ou - protegidoprivate protected -or- protected private Membro é protegida dentro do assembly, mas privada fora do assembly.Member is protected inside the assembly but private outside the assembly.

O exemplo a seguir mostra um tipo público que tem membros que são declarados com a acessibilidade diferente e, em seguida, mostra o acesso a esses membros de dentro do 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();
}

SaídaOutput

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

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

O exemplo a seguir consome o componente que é criado no exemplo anterior e, portanto, mostra como acessar os membros de fora do 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();
}

SaídaOutput

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 nativas públicas e privadasPublic and private native classes

Um tipo nativo pode ser referenciado em um tipo gerenciado.A native type can be referenced from a managed type. Por exemplo, uma função em um tipo gerenciado pode utilizar um parâmetro cujo tipo é um struct nativo.For example, a function in a managed type can take a parameter whose type is a native struct. Se o tipo gerenciado e a função são públicos em um assembly, em seguida, o tipo nativo deve também ser público.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;
};

Em seguida, crie o arquivo de código fonte que consome o tipo nativo:Next, create the source code file that consumes the native type:

// compile with: /clr /LD
#include "mcppv2_ref_class3.h"
// public managed type
public ref struct R {
   // public function that takes a native type
   void f(N nn) {}
};

Agora, compile um cliente: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);
}

Construtores estáticosStatic constructors

Um tipo CLR — por exemplo, uma classe ou struct — pode ter um construtor estático que pode ser usado para inicializar membros de dados estáticos.A CLR type—for example, a class or struct—can have a static constructor that can be used to initialize static data members. Um construtor estático é chamado no máximo uma vez e é chamado antes de qualquer membro estático do tipo é acessado na primeira vez.A static constructor is called at most once, and is called before any static member of the type is accessed the first time.

Um construtor de instância sempre é executado após um construtor estático.An instance constructor always runs after a static constructor.

O compilador embutida de uma chamada para um construtor não é possível se a classe tiver um construtor estático.The compiler cannot inline a call to a constructor if the class has a static constructor. O compilador embutida de uma chamada para qualquer função de membro não é possível se a classe é um tipo de valor, tem um construtor estático e não tem um construtor de instância.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. O CLR pode embutido da chamada, mas o compilador não pode.The CLR may inline the call, but the compiler cannot.

Defina um construtor estático como uma função de membro privado, como ele se destina a ser chamado somente pelo CLR.Define a static constructor as a private member function, because it is meant to be called only by the CLR.

Para obter mais informações sobre construtores estáticos, consulte como: Definir um construtor estático de Interface (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();
}

SaídaOutput

in static constructor
10
11

Semântica do ponteiroSemantics of the this pointer

Quando você estiver usando o Visual C++ para definir tipos, o this ponteiro em um tipo de referência é do tipo "identificador".When you are using Visual C++ to define types, the this pointer in a reference type is of type "handle". O this ponteiro em um tipo de valor é do tipo "ponteiro interior".The this pointer in a value type is of type "interior pointer".

Essas semânticas diferentes da this ponteiro pode causar um comportamento inesperado quando um indexador padrão é chamado.These different semantics of the this pointer can cause unexpected behavior when a default indexer is called. O exemplo a seguir mostra a maneira correta de acessar um indexador padrão em um tipo de ref e um tipo de valor.The next example shows the correct way to access a default indexer in both a ref type and a value type.

Para saber mais, vejaFor 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();
}

SaídaOutput

10.89
10.89

Funções ocultar por assinaturaHide-by-signature functions

No C++ padrão, uma função em uma classe base permanece oculto por uma função que tem o mesmo nome em uma classe derivada, mesmo se a função de classe derivada não tem o mesmo número ou tipo de parâmetros.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. Isso é conhecido como ocultar por nome semântica.This is referred to as hide-by-name semantics. Um tipo de referência, uma função em uma classe base pode apenas estar ocultos por uma função em uma classe derivada se o nome e a lista de parâmetros são os mesmos.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. Isso é conhecido como ocultar por assinatura semântica.This is known as hide-by-signature semantics.

Uma classe é considerada uma classe de ocultar por assinatura quando todas as suas funções são marcadas nos metadados como hidebysig.A class is considered a hide-by-signature class when all of its functions are marked in the metadata as hidebysig. Por padrão, todas as classes que são criadas sob /clr ter hidebysig funções.By default, all classes that are created under /clr have hidebysig functions. Quando uma classe tem hidebysig funções, o compilador não oculta funções por nome em nenhuma classe base direta, mas se o compilador encontra uma classe oculta por nome em uma cadeia de herança, ele continua esse comportamento de ocultar por nome.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.

Semântica de ocultar por assinatura, quando uma função é chamada em um objeto, o compilador identifica a classe mais derivada que contém uma função que atendem a chamada de função.Under hide-by-signature semantics, when a function is called on an object, the compiler identifies the most derived class that contains a function that could satisfy the function call. Se houver apenas uma função na classe que pode satisfazer a chamada, o compilador chama essa função.If there is only one function in the class that could satisfy the call, the compiler calls that function. Se houver mais de uma função na classe que pode satisfazer a chamada, o compilador usa regras de resolução para determinar qual função a ser chamada de sobrecarga.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. Para obter mais informações sobre regras de sobrecarga, consulte sobrecarregamento de função.For more information about overload rules, see Function Overloading.

Para uma chamada de função fornecida, uma função em uma classe base pode ter uma assinatura que o torna uma correspondência um pouco melhor do que uma função em uma classe derivada.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. No entanto, se a função foi chamada explicitamente em um objeto da classe derivada, a função na classe derivada é chamada.However, if the function was explicitly called on an object of the derived class, the function in the derived class is called.

Porque o valor de retorno não é considerado parte da assinatura de uma função, uma função de classe base fica oculto se ele tem o mesmo nome e utiliza o mesmo número e tipo de argumentos como uma função de classe derivada, mesmo que ele difere no tipo do valor de retorno.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.

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

SaídaOutput

Base::Test

O próximo exemplo mostra que o Microsoft C++ compilador chama uma função na classe mais derivada — mesmo se uma conversão é necessária para corresponder a uma ou mais dos parâmetros — e chamar uma função em uma classe base que é uma correspondência melhor para a chamada de função.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);
}

SaídaOutput

Derived::Test2

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

SaídaOutput

Derived::Test4
97

Construtores de cópiaCopy constructors

O padrão C++ informa que um construtor de cópia é chamado quando um objeto for movido, de modo que um objeto é criado e destruído no mesmo endereço.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.

No entanto, quando /clr é usado para compilação e uma função que é compilada para uma nativa de função em que uma classe nativa de chamadas MSIL — ou mais de um — é passado por valor e onde a classe nativa tem um construtor de cópia e/ou destrutor, nenhuma cópia construtor é chamado e o objeto é destruído em um endereço diferente onde ele foi criado.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. Isso pode causar problemas se a classe tiver um ponteiro em si mesmo, ou se o código é objetos de acompanhamento por endereço.This could cause problems if the class has a pointer into itself, or if the code is tracking objects by address.

Para obter mais informações, consulte /clr (compilação de Common Language Runtime).For more information, see /clr (Common Language Runtime Compilation).

O exemplo a seguir demonstra que um construtor de cópia não é gerado.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);
}

SaídaOutput

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

Destruidores e finalizadoresDestructors and finalizers

Os destruidores em um tipo de referência realizar uma determinística limpeza de recursos.Destructors in a reference type perform a deterministic clean-up of resources. Os finalizadores limpar recursos não gerenciados e podem ser chamados de forma determinista pelo destruidor ou de forma não determinística pelo coletor de lixo.Finalizers clean up unmanaged resources and can be called deterministically by the destructor or nondeterministically by the garbage collector. Para obter informações sobre destruidores em C++ padrão, consulte destruidores.For information about destructors in standard C++, see Destructors.

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

O comportamento de destruidores em uma classe gerenciada do Visual C++ difere em extensões gerenciadas para C++.The behavior of destructors in a managed Visual C++ class differs from Managed Extensions for C++. Para obter mais informações sobre essa alteração, consulte alterações na semântica do destruidor.For more information about this change, see Changes in Destructor Semantics.

O coletor de lixo do CLR exclui objetos gerenciados não usados e libera sua memória quando elas não são mais necessárias.The CLR garbage collector deletes unused managed objects and releases their memory when they are no longer required. No entanto, um tipo pode usar recursos que o coletor de lixo não sabe como liberar.However, a type may use resources that the garbage collector does not know how to release. Esses recursos são conhecidos como recursos não gerenciados (nativo identificadores de arquivo, por exemplo).These resources are known as unmanaged resources (native file handles, for example). É recomendável que você libere todos os recursos não gerenciados no finalizador.We recommend that you release all unmanaged resources in the finalizer. Como os recursos gerenciados são liberados de forma não determinística pelo coletor de lixo, não é seguro referir-se recursos gerenciados em um finalizador porque é possível que o coletor de lixo já foi limpo desse recurso gerenciado.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.

Um finalizador do Visual C++ não é o mesmo que o Finalize método.A Visual C++ finalizer is not the same as the Finalize method. (Documentação do CLR usa finalizador e o Finalize método como sinônimos).(CLR documentation uses finalizer and the Finalize method synonymously). O Finalize método é chamado pelo coletor de lixo, que chama cada finalizador em uma cadeia de herança de classe.The Finalize method is called by the garbage collector, which invokes each finalizer in a class inheritance chain. Ao contrário de destruidores do Visual C++, uma chamada de finalizador da classe derivada não faz com que o compilador chamar o finalizador em todas as classes base.Unlike Visual C++ destructors, a derived-class finalizer call does not cause the compiler to invoke the finalizer in all base classes.

Porque o Microsoft C++ compilador é compatível com a liberação determinística dos recursos, não tente implementar o Dispose ou Finalize métodos.Because the Microsoft C++ compiler supports deterministic release of resources, don't try to implement the Dispose or Finalize methods. No entanto, se você estiver familiarizado com esses métodos, aqui está como um finalizador do Visual C++ e um destruidor que chamará o finalizador mapeados para o Dispose padrão: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();
   }
}

Um tipo gerenciado também pode usar os recursos gerenciados que preferir para liberar de forma determinista e não deixar para o coletor de lixo libere forma não determinística em algum momento depois que o objeto não é mais necessário.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. A liberação determinística dos recursos pode melhorar significativamente o desempenho.The deterministic release of resources can significantly improve performance.

O Microsoft C++ compilador permite a definição de um destruidor para limpar os objetos de forma determinista.The Microsoft C++ compiler enables the definition of a destructor to deterministically clean up objects. Use o destruidor para liberar todos os recursos que você deseja liberar de forma determinista.Use the destructor to release all resources that you want to deterministically release. Se um finalizador estiver presente, chamá-lo de destruidor, para evitar a duplicação de código.If a finalizer is present, call it from the destructor, to avoid code duplication.

// compile with: /clr /c
ref struct A {
   // destructor cleans up all resources
   ~A() {
      // clean up code to release managed resource
      // ...
      // to avoid code duplication,
      // call finalizer to release unmanaged resources
      this->!A();
   }

   // finalizer cleans up unmanaged resources
   // destructor or garbage collector will
   // clean up managed resources
   !A() {
      // clean up code to release unmanaged resources
      // ...
   }
};

Se o código que consome seu tipo não chama o destruidor, o coletor de lixo, eventualmente, libera todos os recursos gerenciados.If the code that consumes your type does not call the destructor, the garbage collector eventually releases all managed resources.

A presença de um destruidor não implica a presença de um finalizador.The presence of a destructor does not imply the presence of a finalizer. No entanto, a presença de um finalizador implica que você deve definir um destruidor e chamar o finalizador desse destruidor.However, the presence of a finalizer implies that you must define a destructor and call the finalizer from that destructor. Isso fornece para a liberação determinística dos recursos não gerenciados.This provides for the deterministic release of unmanaged resources.

Suprime a chamar o destruidor — usando SuppressFinalize— finalização do objeto.Calling the destructor suppresses—by using SuppressFinalize—finalization of the object. Se o destruidor não é chamado, o finalizador do seu tipo eventualmente será chamado pelo coletor de lixo.If the destructor is not called, your type's finalizer will eventually be called by the garbage collector.

Determinística, limpando os recursos de seu objeto chamando o destruidor pode melhorar o desempenho em comparação ao permitir que o CLR forma não determinística finalize o objeto.Deterministically cleaning up your object's resources by calling the destructor can improve performance compared with letting the CLR nondeterministically finalize the object.

Código escrito em Visual C++ e compilado usando /clr executa o destruidor de um tipo se:Code that's written in Visual C++ and compiled by using /clr runs a type's destructor if:

Se seu tipo está sendo consumido por um cliente que é escrito em outra linguagem, o destruidor é chamado da seguinte maneira:If your type is being consumed by a client that's written in another language, the destructor is called as follows:

  • Em uma chamada para Dispose.On a call to Dispose.

  • Em uma chamada para Dispose(void) no tipo.On a call to Dispose(void) on the type.

  • Se o tipo sai do escopo em c# using instrução.If the type goes out of scope in a C# using statement.

Se você criar um objeto do tipo de referência no heap gerenciado (não usando a semântica de pilha para tipos de referência), use try-finally sintaxe para garantir que uma exceção não impede que o destruidor em execução.If you create an object of a reference type on the managed heap (not using stack semantics for reference types), use try-finally syntax to ensure that an exception doesn't prevent the destructor from running.

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

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

Se o tipo tem um destruidor, o compilador gera uma Dispose método que implementa IDisposable.If your type has a destructor, the compiler generates a Dispose method that implements IDisposable. Se um tipo que é escrito em Visual C++ e tem um destruidor que é consumido de outra linguagem, chamar IDisposable::Dispose nesse tipo faz com que o destruidor do tipo a ser chamado.If a type that's written in Visual C++ and has a destructor that's consumed from another language, calling IDisposable::Dispose on that type causes the type's destructor to be called. Quando o tipo é consumido de um cliente do Visual C++, você não pode chamar diretamente Dispose; em vez disso, chamar o destruidor usando o delete operador.When the type is consumed from a Visual C++ client, you can't directly call Dispose; instead, call the destructor by using the delete operator.

Se o tipo tiver um finalizador, o compilador gera uma Finalize(void) método que substitui Finalize.If your type has a finalizer, the compiler generates a Finalize(void) method that overrides Finalize.

Se um tipo tem um finalizador ou um destruidor, o compilador gera um Dispose(bool) método, de acordo com o padrão de design.If a type has either a finalizer or a destructor, the compiler generates a Dispose(bool) method, according to the design pattern. (Para obter informações, consulte padrão de descarte).(For information, see Dispose Pattern). Não é possível criar ou chamar explicitamente Dispose(bool) no Visual C++.You cannot explicitly author or call Dispose(bool) in Visual C++.

Se um tipo tem uma classe base que está em conformidade com o padrão de design, os destruidores para todas as classes base são chamados quando o destruidor da classe derivada é chamado.If a type has a base class that conforms to the design pattern, the destructors for all base classes are called when the destructor for the derived class is called. (Se seu tipo é escrito em Visual C++, o compilador garante que seus tipos de implementam esse padrão). Em outras palavras, o destruidor de uma classe de referência encadeia suas bases e membros conforme especificado pelo padrão C++ — primeiro, o destruidor da classe é a execução e, em seguida, os destruidores de seus membros na ordem inversa da ordem na qual eles foram construídos, e, finalmente, o destruidores de suas classes base na ordem inversa da ordem na qual eles foram construídos.(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.

Destruidores e finalizadores não são permitidos dentro de tipos de valor ou interfaces.Destructors and finalizers are not allowed inside value types or interfaces.

Um finalizador só pode ser definido ou declarado em um tipo de referência.A finalizer can only be defined or declared in a reference type. Como um construtor e destruidor, um finalizador não tem nenhum tipo de retorno.Like a constructor and destructor, a finalizer has no return type.

Depois que o finalizador de um objeto é executado, os finalizadores em todas as classes de base também são chamados, começando com o tipo menos derivado.After an object's finalizer runs, finalizers in any base classes are also called, beginning with the least derived type. Os finalizadores para membros de dados não são automaticamente encadeados pelo finalizador de uma classe.Finalizers for data members are not automatically chained to by a class’s finalizer.

Se um finalizador exclui um ponteiro nativo em um tipo gerenciado, você deve garantir que as referências a ou através do ponteiro nativo não sejam coletadas prematuramente; chamar o destruidor em um tipo gerenciado em vez de usar 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.

Em tempo de compilação, você pode detectar se um tipo tem um finalizador ou um destruidor.At compile time, you can detect whether a type has a finalizer or a destructor. Para obter mais informações, consulte suporte do compilador para características de tipo.For more information, see Compiler Support for Type Traits.

O próximo exemplo mostra dois tipos, que possui recursos não gerenciados e que tem recursos que são lançados de forma determinista gerenciados.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");
}

Consulte tambémSee also

Classes e StructsClasses and Structs
Classes e StructsClasses and Structs