Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Semântica para destruidores de classe alterou significativamente de gerenciado Extensions para C++ para Visual C++ 2008.
No gerenciado Extensions um destruidor de classe era permitido dentro de uma classe de referência, mas não dentro de uma classe de valor.Isso não alterou a nova sintaxe.Entretanto, a semântica do destruidor de classe foram alteradas.Este tópico aborda os motivos que alterar e discute como ele afeta a conversão de código existente do CLR.Provavelmente é a alterar de nível de programador mais importante entre as duas versões da linguagem.
Finalização não-determinística
Antes da memória associada a um objeto é recuperada pelo coletor de lixo, um associado Finalize método, se presente, é invocado. Você pode considerar esse método sistema autônomo um tipo de super-destructor porque não está vinculado à tempo de vida de programa do objeto.Nos referimos a isso sistema autônomo finalização.O intervalo de apenas quando ou até mesmo se um Finalize método é invocado é indefinido. Este é o que queremos dizer quando dizemos que a coleta de lixo apresenta finalização não-determinística.
Finalização não determinista funciona bem com o gerenciamento de memória dinâmica.Quando a memória disponível se torna escassa, o coletor de lixo é ativado.Em um lixo coletado ambiente, destruidores para liberar memória são desnecessários.Finalização não-determinística não funciona bem, no entanto, quando um objeto mantém um recurso crítico, sistema autônomo uma conexão de banco de dados ou um bloquear de algum tipo.Neste csistema autônomoe nós deve relesistema autônomoe desse recurso sistema autônomo logo sistema autônomo possíveis.No mundo nativo, que é atingida usando um emparelhar de construtor/destruidor.sistema autônomo logo sistema autônomo o tempo de vida das extremidades do objeto, quando termina o bloco local no qual ela é declarada, ou quando a pilha unravels causa de uma exceção gerada, o destruidor é executado e o recurso automaticamente é relesistema autônomoed.Essa abordagem funciona muito bem e sua ausência em gerenciado Extensions muita foi perdida.
A solução fornecida pelo CLR é uma classe implementar o Dispose método para o IDisposable interface. O problema aqui é que Dispose requer uma invocação explícita pelo usuário. Isso é propenso a erros.A linguagem translation from VPE for Csharp fornece uma forma modesta de automação na forma de um especial using demonstrativo. O design de gerenciado Extensions não fornecido nenhum suporte especial.
Destruidores nas extensões gerenciadas para C++
No gerenciado Extensions, o destruidor de classe de referência é implementado usando as duas etapas a seguir:
- O destruidor fornecido pelo usuário é renomeado internamente para Finalize. Se a classe tem uma classe base (Lembre-se, em modelo de objeto CLR, é suportada somente herança única), o compilador injeta uma telefonar para seu finalizador após execução do código fornecido pelo usuário.Por exemplo, considere a seguinte hierarquia simples extraída da especificação de linguagem de gerenciado Extensions:
__gc class A {
public:
~A() { Console::WriteLine(S"in ~A"); }
};
__gc class B : public A {
public:
~B() { Console::WriteLine(S"in ~B"); }
};
Neste exemplo, ambos os destruidores são renomeados Finalize. B's Finalize tem uma chamada de A's Finalize método adicionado após a chamada da WriteLine. Este é o que o coletor de lixo por padrão invocará durante a finalização.Eis o que essa transformação interna pode parecer com:
// internal transformation of destructor under Managed Extensions
__gc class A {
public:
void Finalize() { Console::WriteLine(S"in ~A"); }
};
__gc class B : public A {
public:
void Finalize() {
Console::WriteLine(S"in ~B");
A::Finalize();
}
};
Na segunda etapa, o compilador sintetiza um destruidor virtual.Esse destruidor é o que nossos programas de usuário de gerenciado Extensions chamar diretamente ou por meio de um aplicativo da expressão de excluir.Ele nunca é invocado pelo coletor de lixo.
Duas instruções são colocadas dentro desse destruidor sintetizada.Uma é uma telefonar para GC::SuppressFinalize para certificar-se de que estão sem mais invocações de Finalize. O segundo é a chamada real da Finalize, que representa o destruidor fornecido pelo usuário para essa classe. Eis o que isso pode parecer com:
__gc class A {
public:
virtual ~A() {
System::GC::SuppressFinalize(this);
A::Finalize();
}
};
__gc class B : public A {
public:
virtual ~B() {
System::GC::SuppressFinalize(this);
B::Finalize();
}
};
Enquanto essa implementação permite que o usuário chamar explicitamente a classe Finalize método agora em vez de cada vez que você não tem controle sobre, ele não realmente vincular com o Dispose método de solução. Isso é alterado em Visual C++ 2008.
Destruidores na nova sintaxe
Na sintaxe de novo, o destruidor foi renomeado internamente para o Dispose método e a classe de referência será estendido automaticamente para implementar o IDispose interface. Isto é, em Visual C++ 2008, nosso emparelhar de classes é transformado da seguinte maneira:
// internal transformation of destructor under the new syntax
__gc class A : IDisposable {
public:
void Dispose() {
System::GC::SuppressFinalize(this);
Console::WriteLine( "in ~A");
}
};
__gc class B : public A {
public:
void Dispose() {
System::GC::SuppressFinalize(this);
Console::WriteLine( "in ~B");
A::Dispose();
}
};
Quando qualquer um destruidor é invocado explicitamente sob a nova sintaxe ou delete é aplicada a uma alça de acompanhamento, a base Dispose método é invocado automaticamente. Se for uma classe derivada, uma telefonar do Dispose método da classe base é inserido no fechar do método sintetizado.
Mas isso não obterá nos até a finalização determinística.Para conectar-se que, precisamos do suporte adicional de objetos de referência local.(Isso tem suporte semelhante de gerenciado Extensions e, portanto, não é um problema de tradução.)
Declarando um objeto de referência
Visual C++ 2008 oferece suporte a declaração de um objeto de uma classe de referência na pilha local ou sistema autônomo membro de uma classe sistema autônomo se fosse acessível diretamente. Quando combinado com a associação do destruidor com o Dispose método, o resultado é a chamada automatizada da semântica de finalização em tipos de referência.
Primeiro, definimos nossa classe de referência de modo que a criação do objeto funciona sistema autônomo a aquisição de um recurso com o seu construtor de classe.Em segundo lugar, no destruidor de classe, nós liberar o recurso adquirido quando o objeto foi criado.
public ref class R {
public:
R() { /* acquire expensive resource */ }
~R() { /* release expensive resource */ }
// … everything else …
};
O objeto for declarado localmente usando o nome do tipo, mas sem o chapéu que acompanha.Todos sistema autônomo usos do objeto, sistema autônomo chamar um método, são feitos através do ponto de seleção de membro (.) em vez de seta ()->). No participante do bloco, o destruidor associado, transformado em Dispose, é invocado automaticamente, conforme mostrado aqui:
void f() {
R r;
r.methodCall();
// r is automatically destructed here –
// that is, r.Dispose() is invoked
}
sistema autônomo ocorre com o using demonstrativo em translation from VPE for Csharp, isso não enfrente a restrição CLR subjacente que todos sistema autônomo tipos de referência devem ser alocado no heap CLR. A semântica subjacente permanecem inalterada.O usuário poderia maneira equivalente ter escrito o seguinte (e esse é provavelmente a transformação interna realizada pelo compilador):
// equivalent implementation
// except that it should be in a try/finally clause
void f() {
R^ r = gcnew R;
r->methodCall();
delete r;
}
Na verdade, sob a nova sintaxe destruidores estiver novamente emparelhados com construtores sistema autônomo uma aquisição/versão automatizada mecanismo vinculado à tempo de vida do objeto local.
Declarando um finalizar Explicit
A nova sintaxe, sistema autônomo vimos, o destruidor é sintetizado no Dispose método. Isso significa que quando o destruidor não é invocado explicitamente, o coletor de lixo durante a finalização, não sistema autônomo antes localizará um associado Finalize método do objeto. Para dar suporte a destruição e finalização, apresentamos uma sintaxe especial para fornecer um finalizador.Por exemplo:
public ref class R {
public:
!R() { Console::WriteLine( "I am the R::finalizer()!" ); }
};
The ! prefixo é análogo a () til~) que introduz um destruidor de classe – ou seja, os dois métodos post-tempo de vida têm um token de colocação de um prefixo do nome da classe. Se o sintetizada Finalize método ocorre dentro de uma classe derivada, uma invocação da classe base Finalize método é inserido no seu participante. Se o destruidor é invocado explicitamente, o finalizador será suprimido.Eis o que a transformação pode parecer com:
// internal transformation under new syntax
public ref class R {
public:
void Finalize() {
Console::WriteLine( "I am the R::finalizer()!" );
}
};
Mover de extensões gerenciadas para C++ para Visual C++ 2005
O comportamento em tempo de execução de um gerenciado Extensions para C++ programa é alterado quando ele é compilado em Visual C++ 2008 sempre que uma classe de referência contém um destruidor de não-comum. O algoritmo de tradução necessária é semelhante à seguinte:
Se houver um destruidor, reescreva que seja o finalizador da classe.
If a Dispose método estiver presente, reescrever que para o destruidor de classe.
Se um destruidor estiver presente, mas não houver nenhum Dispose método, reter o destruidor ao executar o primeiro item.
In mover seu código de gerenciado Extensions para a nova sintaxe, poderá ignorado realizar essa transformação.Se o aplicativo dependentes de alguma forma a execução de métodos de finalização associado, o comportamento do aplicativo silenciosamente ser diferente daquele pretendido.