Ponteiros de método: conceito fundamental

Canal: C / C++  |  Autor: Redação Oficina da Net  |  Publicado em: 09/09/2007  |  Views: 3.794
Diferente de ponteiros de função (funções globais ou estáticas) - que são a grosso modo ponteiros como qualquer um - os ponteiros de método possuem uma semântica toda especial que costuma intimidar até quem está acostumado com a aritmética de ponteiros avançada. Não é pra menos. É praticamente uma definição à parte, com algumas limitações e que deixa a desejar os quase sempre criativos programadores da linguagem, que vira e mexe estão pedindo mudanças no C++0x.

Três regras iniciais que devem ser consideradas para usarmos ponteiros para métodos são:

    * A semântica para lidar com ponteiros de método é totalmente diferente de ponteiros de função.
    * Ponteiros de método de classes distintas nunca se misturam.
    * Para chamarmos um ponteiro de método precisamos sempre de um objeto da classe para a qual ele aponta.

Visto isso, passemos a um exemplo simples, um chamador de métodos aleatórios, que ilustra o princípio básico de utilização:

#include <windows.h>
#include <iostream>
#include <time.h>

using namespace std;


// declaramos que existe uma classe com esse nome
class FuzzyCall;

// ponteiro para métodos da classe acima
typedef void (FuzzyCall::*FP_Fuzzy)();


/** Classe que faz chama um método aleatório. */
class FuzzyCall
{
public:

   FuzzyCall()
   {
      srand(GetTickCount()); // chacoalha o saco de bingo
   }

   FP_Fuzzy GiveMeAMethod() { return m_methods[rand() % 3]; }

private:
   void MethodOne()   { cout << "One!n"; }
   void MethodTwo()   { cout << "Two!n"; }
   void MethodThree() { cout << "Three!n"; }

   static FP_Fuzzy m_methods[3];
};

/** Array com os métodos que podem ser chamados aleatoriamente. */
FP_Fuzzy FuzzyCall::m_methods[3] = { &MethodOne, &MethodTwo, &MethodThree };


/** Recebe um ponteiro para um método de FuzzyCall e chama com um objeto local. */
void passThrough(FP_Fuzzy pMethod)
{
   FuzzyCall fuzzyObject; // esse é o objeto local

   ( fuzzyObject.*pMethod )(); // essa é a chamada
}


/** No princípio Deus disse: 'int main!'
*/

int main()
{
   FuzzyCall fuzzyObject1;
   FP_Fuzzy pMethod;

   // pegamos um método da classe qualquer
   pMethod = fuzzyObject1.GiveMeAMethod();

   // e passamos para uma outra função
   passThrough(pMethod);
}



Como podemos ver, para o typedef de ponteiros de método é necessário especificar o escopo da classe. Com isso, o compilador já sabe que só poderá aceitar endereços de métodos pertencentes à mesma classe com o mesmo protótipo.

Na hora de atribuir, usamos o operador de endereço e o nome do método (com escopo, se estivermos fora da classe). É importante notar que, diferente de ponteiros de função, o operador de endereço é obrigatório. Do contrário:

error C4867: 'FuzzyCall::MethodOne': function call missing argument list;
   use '&FuzzyCall::MethodOne' to create a pointer to member


E, por fim, a chamada. Como é a chamada de um método, é quase intuitiva a necessidade de um objeto para chamá-la. Do contrário não teríamos um this para alterar o objeto em qualquer método não estático, certo? Daí a necessidade do padrão C++ especificar dois operadores especialistas para esse fim, construídos a partir da combinação de operadores já existentes em C:

FuzzyCall fuzzyObject; // esse é o objeto local
FuzzyCall* pFuzzy = &fuzzyObject; // ponteiro para esse mesmo objeto

( fuzzyObject.*pMethod )(); // [objeto] .* [ponteiro de método]
( fuzzyObject->*pMethod )(); // [ponteiro para objeto] ->* [ponteiro de método]


Esses operadores obrigam o programador a sempre ter um objeto e um ponteiro. Daí não tem como errar. Infelizmente, devido à ordem de precedência, temos que colocar os parênteses em torno da expressão para chamar o método. Pelo menos fica equivalente ao que precisávamos fazer antes da padronização da linguagem C.

Fonte: http://www.cbrasil.org
Creative Commons Esta obra está licenciada sob uma Licença Creative Commons. Você pode copiar, distribuir, exibir, executar, desde que seja dado crédito ao autor original (Citando nome do autor, data, local e link de onde tirou o texto). Você não pode fazer uso comercial desta obra. Você não pode criar obras derivadas.
Vote no artigo:

Compartilhe:

[x] Fechar Preencha os campos abaixo para indicar esta página:
Seu nome:
Seu e-mail:
Nome do indicado:
E-mail do indicado:
Deixe uma mensagem:
Anti-spam:

(nova imagem)
Preencha o que vê:
Twitter diHITT Facebook delicious envie por e-mail comentar

comentarComentários:

Preencha o formulário para comentar:

[x] Fechar
Nome:*
E-mail:* (não será exibido)
Site: (http://)
Comentário:*
Anti-spam:

(nova imagem)
Preencha o que vê:

Deseja receber as respostas dos comentários

Gabriel
Publicado em:
05/12/2008 - 13:04
Gabriel
Ola eu gostei desse postutorial!!!
E em C++ e ta tudo bem explicado!!!
Mas ta no estagio "avançadinho"!!
pra quem ta começando em C tem que pegar umas coisa mais leve,tipo:
Para obter ponteiros a métodos ou funções sobrecarregadas, como exemplo:

class Foo
{
public:
void bla(int a);
int bla() const;
};
Se tentarmos usar o nome da função diretamente, o compilador não saberá a qual dos métodos você se refere. Para sair da ambiguidade, necessitamos usar a “assinatura” (os tipos dos parâmetros e do valor retornado) da função indiretamente, possivelmente com o uso de variáveis temporárias:

void (Foo::*ptr1)(int)= &Foo::bla;
int (Foo::*ptr2)() const= &Foo::bla;