CPP/CLI Tip of the Week
De ccppbrasil.org
C++/CLI - Todo tipo poderá ter um destrutor (na verdade é um Dispose implícito)
Os tipos nas principais linguagens do .NET (C#, VB) não possuem algo equivalente ao destructor do C++. Para obtermos finalização determinística de um recurso necessitamos implementar no tipo a interface IDisposable e utilizar o Dispose pattern.
Assim como em C++ ("tradicional"), no C++/CLI podemos definir um destructor! O destructor neste caso serve para liberação imediata do(s) recurso(s), sendo seu comportamento semelhante ao C++. No entanto, quando existir a presença de um destructor num tipo CLI (ou seja .NET), deve-se entender que o IL produzido chamará o método Dispose (isto mesmo, o compilador infere uma implementação de IDisposable e Dispose pattern). Interessante e prático, não é mesmo?
Dado a classe abaixo, note a presença do destructor e como ele é disparado dentro do método Run:
//Command Line Compilation
//cl /CLR CPPCLI_TipOfTheWeek_1.cpp
#using <mscorlib.dll>
using namespace System;
template<typename T>
ref class DummyRAIISmartPointer
{
T^ Instance_;
public:
DummyRAIISmartPointer( T^ instance ) : Instance_( instance ){}
~DummyRAIISmartPointer(){ delete Instance_; }
T^ operator->(){ return Instance_; }
};
ref class Tip1
{
public:
Tip1() : TraceDestructor_( false ){}
Tip1( Boolean traceDestructor ) : TraceDestructor_( traceDestructor ){}
~Tip1()
{
if( TraceDestructor_ )
Console::WriteLine( "Disposed {0} - GC can free this instance", GetHashCode() );
}
void Run()
{
Tip1^ t11 = gcnew Tip1( true );
t11->Call( "Will be released by delete statement" );
delete t11; // Delete calls the destructor,
// in C++/CLI destructor is mapped to Disposed method
{
Tip1 t12( true );
t12.Call( "Will be released by scope (out of scope)" );
}
DummyRAIISmartPointer<Tip1> t13( gcnew Tip1( true ) );
t13->Call( "Will be released by scope (out of scope)" );
Tip1^ t14 = gcnew Tip1( true );
t14->Call( "Hello resource leak! Will not be released" );
}
void Call( String^ text ){ Console::WriteLine( "{0} - {1}", GetHashCode(), text ); }
private:
Boolean TraceDestructor_;
};
int main(array<System::String ^> ^args)
{
Tip1().Run();
/*
Console Output Sample:
58225482 - Will be released by delete statement
Disposed 58225482 - GC can free this instance
54267293 - Will be released by scope (out of scope)
Disposed 54267293 - GC can free this instance
18643596 - Will be released by scope (out of scope)
33574638 - Hello resource leak! Will not be released
Disposed 18643596 - GC can free this instance
*/
return 0;
}
1- Na instância t11, o destructor será chamado após a execução do delete;
2- Na instância t12, o destructor será chamado após a saída do escopo - note que t12 possui semântica de stack, porém todo tipo .NET é alocado na CLI Heap;
3- Na instância t13, o destructor será chamado pelo "smart pointer";
4- Na instância t14, o destructor não será chamado.
Ou seja, C++/CLI se baseia no mesmo comportamento do destructor do C++ para liberação de recursos de forma determinística. Isto torna a adaptação de um código e de um programador C++ bem tranquilo no mundo do .NET.
Artigo original: C++/CLI Tip of the Week #1
