sábado, 12 de maio de 2012

Funtores em C++, parte II

Na última parte, aprendemos como utilizar funtores em C++. Hoje vamos ver qual é a sua vantagem. Já que da maneira como aprendemos, eles não diferem muito de ponteiros para funções.

A vantagem do funtor é que ele é um objeto como qualquer outro, então pode ter métodos, variáveis membro, etc. Podemos dessa forma criar "fábricas" de funtores, seja dinamicamente, em tempo de execução, ou estaticamente, através de templates.

Vou demonstrar como utilizar cada uma das duas técnicas para criar uma classe polinômio.

1. Criando funtores estáticos com templates

Para criarmos a fábrica de funtores estaticamente, vamos parametrizar a classe derivada de Function para definir um polinômio de grau dois.
template<int A0, int A1, int A2>
 class Poly3 : public Function
 {
 public:
  double operator ()(double x) const {
   return A0 + x * (A1 + A2 * x);
  }
 };
Para criar uma nova função é só criar uma objeto atribuindo os parâmetros. Por exemplo, para definirmos a função do post anterior (x^2 - 1), podemos fazer assim:
Function* p = new Poly3<-1, 0, 1>();
A desvantagem dessa abordagem é o que os parâmetros de template devem ser integrais, reduzindo então a gama de funções que podemos representar. A vantagem é que temos aqui o mesmo custo que se criássemos uma classe nova para cada função (cada parametragem diferente vai gerar código diferente).

2. Criando funtores dinâmicos

Usaremos a mesma classe base do post anterior, mas dessa vez nosso funtor será um polinômio. Como parâmetro do construtor teremos um vetor de coeficientes; o grau do polinômio será definido pelo tamanho desse vetor.
template <class T>
 class Polynomial : public Function{
  vector<T> a;
 public:
  Polynomial(vector<T> &coefficients) : a(coefficients) { }
  T operator ()(T x) const {
   T total = 0;
   for(int i=0; i < a.size(); ++i)
    total += a[i] * pow(x, i);
   return total;
  }
 };
Hmmm, funcionalicioso! Note que utilizamos um template para definir o tipo dos coeficientes, dessa forma podemos usar inteiros, reais, matrizes; o limite é só sua ambição e o teorema da incompletude. A função principal fica da seguinte forma:
int main()
    {
  vector<double> a(3);
  a[0] = -1.0;
  a[1] = 0.0;
  a[2] = 1.0;

  Function* p = new Polynomial<double>(a);

        double result = root(p, -10.0, 10.0);
        
  cout << result;

        return 0;
    }
Criamos o vetor de coeficientes e o passamos como parâmtro para o objeto p, que é passado como parâmetro para nossa função root. O resultado é o mesmo do programa anterior (já que criamos o mesmo polinômio). Testa agora mesmo!

* Nota que dessa vez, na função de avaliação do valor do polinômio, eu utilizei a palavra-chave const. Em um post futuro vou falar mais sobre constantes em c++, mas o const depois dos parâmetros do método, significa que ele não altera nenhuma variável membro do objeto, ou seja, ele é uma classe pura. Sempre que puderes utiliza essa palavra chave, ela serve tanto para documentação (os usuários da tua classe não vão precisar olhar no corpo do método para saber se tua classe altera o estado do objeto), quanto para deixar o compilador fazer certas otimizações.

Nenhum comentário:

Postar um comentário