Functors – ponteiros para métodos
Me pediram para postar um exemplo de um sistema de eventos usando o assunto do post passado (ponteiros para funções e métodos), mas decidi que antes de fazer esse post, irei falar sobre functors – pois irei utilizar esse conceito no sistema de eventos.
Como falei no post anterior, um ponteiro para um método de uma classe tem uma sintaxe estranha e que pode facilmente complicar códigos, e além disso necessitamos de uma referência ao objeto do qual esse método será executado. Para facilita então o uso desses ponteiros especiais, vamos criar um conjunto simples de classes que encapsulam a funcionalidade de um ponteiro para método – esse conceito é chamado de Functor.
Para que a classe seja genérica, necessitaremos utilizar templates. Mas, como um template na prática gera uma classe diferente para cada tipo, necessitamos criar uma classe-base para que nossos functors possam ser tratados por código genérico. E aqui está o código da classe:
class Functor
{
public:
virtual void operator() () = 0;
};
É uma classe abstrata bem simples – com apenas um operador virtual, o operador (). Isso nos permite fazer algo do tipo:
void funcao(Functor &func)
{
// chama funcao encapsulada pelo functor
func();
}
É logico q a classe Functor apenas nao resolve nada, então vamos olhar agora o código da classe que realmente faz a diferença – TemplateFunctor
template <class T>
class TemplateFunctor : public Functor
{
protected:
// definindo o tipo do ponteiro
typedef void (T::*metodo)();
// o ponteiro para o objeto em questao
T *_pObj;
// o ponteiro para o método
metodo _fpMetodo;
public:
// construtor
TemplateFunctor ( T *o, metodo m ) : _pObj(o),_fpMetodo(m) {}
// operador de chamada de funcao ()
void operator() ()
{
(_pObj->*_fpMetodo) ();
}
};
Agora sim, temos um functor funcionando. A classe é relativamente simples – temos a definição do tipo do ponteiro, a definição dos ponteiros para o objeto e para o método, um construtor que garante a construção apropriada do functor, e o operador () q nos permite chamar o método através deste functor como se o objeto fosse o próprio método. Vamos exemplificar a seguir:
// uma classe qualquer
class A
{
public:
A() {}
void metodoQualquer() { /*faz algo*/ }
};
// nao podemos ter um functor sem um objeto
A a();
// criando o functor - passando objeto e método
TemplateFunctor<A> aFunc(&a, &A::metodoQualquer);
// chamando o método através do functor
aFunc();
Algo que provavelmente voce está se pergutando agora é – e se eu quiser métodos com outros tipos de retorno e parametros? Bom, para isso é necessária a criação de classes functor diferentes ou, então, planejar a sua própria functor para trabalhar com parameterização genérica (receber uma union, ou um ponteiro void). Porém, para um sistema de eventos, por exemplo, onde todos os métodos chamados terão a mesma assinatura, este tipo de functor facilita bastante a codificação.
Como sempre, postem suas dúvidas e sugestoes, estarei mais que disponivel para responde-las.
3 comentários até agora
Leave a reply
Muito bom! Posso dar uma sugestão de leitura?
http://www.tutok.sk/fastgl/callback.html
Acho que um dos materiais mais completos sobre o assunto encontrados na net.
Muito interessante este conceito de Functor, nunca tinha ouvido falar. Muito bem explicado.
Bem interessante. Parabéns pelo artigo!