quarta-feira, 14 de março de 2012

Funtores em C++, parte I

Ponteiros para funções são legais. Mas são muito C. Para deixar nosso código mais C++, podemos usar Funtores. Funtores são classes que possuem o operator (); dessa maneira podemos chamar a função usando o identificador do objeto como uma função. Vamos transformar a função do artigo anterior em um funtor.
class Function{
 public:
  double operator ()(double x){
   return x * x - 1.0;
  }
 };
Para utilizar o funtor temos que criar uma instância dessa classe, e chamá-la através da referência ao objeto.
 Function f;
 cout << f(2);   
Para usar nossa função root, então, precisaríamos passar uma referência ao objeto:
 double root(Function &f, double i, double e);
O problema aqui é que perdemos a generalidade da função root, já que ela só funciona para um objeto do tipo Function. Para torná-la mais genérica, vamos utilizar polimorfismo. Abaixo está o código completo da solução. Na linha 6 definimos uma classe abstrata para funções, depois criamos a classe MyFunction que vai herdar de Function, sobreescremos então o operator(). Na linha 18, temos a nova assinatura da função, agora ela recebe um ponteiro para um objeto do tipo Function. Assim, com o operador new podemos criar qualquer tipo de classe que derive de Function como parâmetro de nossa função root.
 template <class T>
 bool eqlZero(T x) {
   return x * x < 0.000000001;
 }

 class Function{
 public:
  virtual double operator ()(double x) = 0;
 };
 
 class MyFunction : public Function{
 public:
  double operator ()(double x){
   return x * x - 1.0;
  }
 };
 // finds one root between i and e
 double root(Function* f, double i, double e) {
   int iterationsLeft = 10000;
   while(iterationsLeft--) {
     double x = (i+e) / 2.0;
     cout << x << endl;
     if(eqlZero((*f)(x))){
       return x;
     }else if((*f)(x) < 0) {
       i = x;
     }else {
       e = x;
     }
   }
 }

 int _tmain(int argc, _TCHAR* argv[]) {
   Function* f = new MyFunction();
   double result = root(f, -10.0, 10.0);
   cout << result;

   return 0;
 }
Teremos assim o mesmo resultado que a função do artigo anterior. Mas além de estilística, não existe uma razão aqui para usar funtores (muito pelo contrário, vamos ter uma penalidade de desempenho devido ao overhead). Na próxima parte vamos ver a vantagem de usar funtores.

Nenhum comentário:

Postar um comentário