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

Ferramentas pessoais