| 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) |
|
|
|
| [ 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. |
|
|
|
| [ 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. |
|
|
|
| [ 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. |
|
|
|
| [ 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. |
|
|
|
| [ 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);
}