FAQ Lite
Pointers para Funções Membro

[ 30.1 ] O tipo pointer-para-função-membro é diferente de pointer-para-função?
[ 30.2 ] Como eu passo um pointer de uma função membro para um signal handler, X event callback, etc?
[ 30.3 ] Porque eu continuo tendo erros (type mismatch) quando tento usar uma função membro como uma rotina de tratamento de interrupção?
[ 30.4 ] Porque eu estou tendo problemas para apanhar o endereço de uma função C++?
[ 30.5 ] Como posso evitar erros de sintaxe quando chamo uma função membro usando um pointer-para-função-membro?
[ 30.6 ] Como eu crio e uso uma matriz de pointers para funções membro?

[ 30.1 ] O tipo pointer-para-função-membro é diferente de pointer-para-função?

Sim.

Considere a seguinte função:

    int f(char a, float b); 
O tipo desta mesma função será diferente conforme esta seja uma função nornal, ou uma função membro não-static de alguma classe: 
  • Seu tipo será int (*)(char,float) se for uma função ordinária
  • Seu tipo será int (Fred::*)(char,float) se for uma função membro não-static da class Fred

Nota: Se for uma função membro static da class Fred, seu tipo será o mesmo de uma função normal: int (*)(char,float)

Topo
[ 30.2 ] Como eu passo um pointer de uma função membro para um signal handler, X event callback, etc?

Não passa.

Devido ao fato de que funções membro não têm significado, exceto quando são invocadas vinculadas a um objeto, você não pode passar um pointer de uma função membro diretamente (se o X Windows System fosse reescrito em C++, provavelmente se poderia passar referências para objetos, não apenas pointers para funções; naturalmente os objetos teriam que incorporar as funções requeridas e provavelmente muito mais).

Como solução de contorno para softwares já existentes, use uma função top-level (não membro) como um empacotador, a qual apanha um objeto obtido através de uma outra técnica qualquer (um objeto global, talvez). A função top-level poderia invocar a função membro desejada vinculada ao objeto global.

Por exemplo, suponha que você quer chamar Fred::memberFunction() quando ocorrer uma interrupção:

    class Fred {
    public:
      void memberFunction();
      static void staticMemberFunction();  // A static member function can handle it
      // ...
    };
    
    // Wrapper function uses a global to remember the object:
    Fred* object_which_will_handle_signal;
    void Fred_memberFunction_wrapper()
    {
      object_which_will_handle_signal->memberFunction();
    }
    
    main()
    {
      /* signal(SIGINT, Fred::memberFunction); */   // Can NOT do this
      signal(SIGINT, Fred_memberFunction_wrapper);  // OK
      signal(SIGINT, Fred::staticMemberFunction);   // Also OK
    } 
Nota: Funções membro static não requerem um objeto real para que sejam invocadas, assim pointers-para-funções-membro-static são tipos compatíveis com pointers-para-funções normais.
Topo
[ 30.3 ] Porque eu continuo tendo erros (type mismatch) quando tendo usar uma função membro como uma rotina de tratamento de interrupção?

Isso é um caso particular das duas questões anteriores, portanto primeiramente leia as duas respostas anteriores.

Funções membro não-static têm um parâmetro oculto que corresponde ao pointer this. O pointer this aponta para os dados da instância do objeto. A interrupção de hardware/firmware no sistema não tem como fornecer o argumento pointer this. Você deve usar uma função normal (não membro de uma classe) ou uma função membro static como rotina de tratamento de interrupção.

Uma solução possível é usar uma função membro static como rotina de tratamento de interrupção, e essa função pesquisar alguma informação para determinar que par instância/membro invocar para a interrupção específica. Dessa forma, o efeito é o de chamar uma função membro para a interrupção, mas por razões técnicas você usa uma função intermediária.

Topo
[ 30.4 ] Porque eu estou tendo problemas para apanhar o endereço de uma função C++?

Isso é um corolário da FAQ anterior.

Resposta longa: Em C++, funções membro têm um parâmetro implícito que aponta para o objeto (o pointer this dentro da função membro). Funções C normais devem ser consideradas como tendo uma convenção de chamada diferente para funções membro, de modo que os tipos desses pointers (pointer-para-função-membro vs pointer-para-função) são diferentes e incompatíveis. C++ apresenta um novo tipo de pointer, denominado pointer-para-membro, que pode ser invocado apenas quando vinculado a um objeto.

Nota: Não tende converter um pointer-para-membro para um pointer-para-função. O resultado é indefinido e provavelmente desastroso. Por exemplo, um pointer-para-função-membro não é obrigado a conter o endereço de máquina da função. Como foi dito no último exemplo, se você tem um pointer para uma função C normal, use uma função top-level (não membro), ou uma função membro static.

Topo
[ 30.5 ] Como posso evitar erros de sintaxe quando chamo uma função membro usando um pointer-para-função-membro?

De duas formas: (1) use um typedef, e (2) use uma macro #define

Aqui está o modo de criar um typedef:

    class Fred {
    public:
      int f(char x, float y);
      int g(char x, float y);
      int h(char x, float y);
      int i(char x, float y);
      // ...
    };
    
    // FredMemberFn points to a member of Fred that takes (char,float)
    typedef  int (Fred::*FredMemberFn)(char x, float y); 
Aqui está o modo de criar a macro #define (normalmente eu desaconselho macros #define, mas esse é um dos raros casos em o uso da macro #define simplifica a codificação e melhora a legibilidade do seu código).
#define callMemberFunction(object,ptrToMember)  ((object).*(ptrToMember)) 
Aqui está como você usa esses recursos::
    void userCode(Fred& fred, FredMemberFn memFn)
    {
      callMemberFunction(fred,memFn)('x', 3.14);
      // Would normally be: (fred.*memFn)('x', 3.14);
    } 
Eu recomendo fortemente essa solução. No mundo real, invocar funções membro é algo muito mais complexo do que esse exemplo simples, e as diferenças de facilidade de codificação e de legibilidade do código são significativas. comp.lang.c++  teve que suportar centenas e centenas de questões enviadas por programadores confusos, que não conseguiam compreender completamente a sintaxe correta para solução para esse problema. Praticamente todos esses erros desapareceram quando passaram a usar a solução proposta nessa FAQ.
Topo
[ 30.6 ] Como eu crio e uso uma matriz de pointers para funções membro?

Use a solução de praxe typedef e macro #define e você terá feito 90% do trabalho.

Em primeiro lugar, use um typedef:

    class Fred {
    public:
      int f(char x, float y);
      int g(char x, float y);
      int h(char x, float y);
      int i(char x, float y);
      // ...
    };
    
    // FredMemberFn points to a member of Fred that takes (char,float)
    typedef  int (Fred::*FredMemberFn)(char x, float y); 
que viabiliza a matriz de pointers-para-funções-membro:
FredMemberFn a[4] = { &Fred::f, &Fred::g, &Fred::h, &Fred::i }; 
Em seguida use a macro callMemberFunction:
#define callMemberFunction(object,ptrToMember)  ((object).*(ptrToMember)) 
Isso viabiliza chamar uma das funções membro do objeto fred:
    void userCode(Fred& fred, int memberFunctionNum)
    {
      // Assume memberFunctionNum is between 0 and 3 inclusive:
      callMemberFunction(fred, a[memberFunctionNum]) ('x', 3.14);
    } 
Topo Anterior Próximo Índice
C++ FAQ Lite
Copyright © 1991-98 by Marshall Cline Ph.D., cline@parashift.com
Tradução: Dagoberto Haele Arnaut

| Home | Bookmarks | Universidades | Para Saber mais | Universidades | WEB Directory | Mapa do site |