Como obtenho um ponteiro de função para uma função de membro de classe e, posteriormente, chamo essa função de membro com um objeto específico? Eu gostaria de escrever:
class Dog : Animal
{
Dog ();
void bark ();
}
…
Dog* pDog = new Dog ();
BarkFunction pBark = &Dog::bark;
(*pBark) (pDog);
…
Além disso, se possível, gostaria de invocar o construtor por meio de um ponteiro:
NewAnimalFunction pNew = &Dog::Dog;
Animal* pAnimal = (*pNew)();
Isso é possível e, em caso afirmativo, qual é a forma preferida de fazer isso?
c++
function-pointers
class-method
Tony o Pônei
fonte
fonte
Dog dog;
) é mais provável que você queira.Respostas:
Leia este para o detalhe:
fonte
*this.*pt2Member
iria funcionar.*
tem maior precedência sobre.*
... Pessoalmente, eu ainda teria escritothis->*pt2Member
, é um operador a menos.pt2ConstMember
paraNULL
?(object.*method_pointer)
, então queremos*
que tenha maior prioridade.this
está apenas sendo usado para demonstrar que tudo o que você aplicar.*
deve ser um ponteiro para uma instância da (sub) classe. No entanto, esta é uma nova sintaxe para mim, estou apenas supondo com base em outras respostas e recursos vinculados aqui. Estou sugerindo uma edição para deixar isso mais claro.É mais fácil começar com um
typedef
. Para uma função de membro, você adiciona o nome da classe na declaração de tipo:Então, para invocar o método, você usa o
->*
operador:Não acredito que você possa trabalhar com construtores como este - ctors e dtors são especiais. A maneira normal de conseguir esse tipo de coisa seria usar um método de fábrica, que é basicamente uma função estática que chama o construtor para você. Veja o código abaixo para um exemplo.
Modifiquei seu código para fazer basicamente o que você descreve. Existem algumas advertências abaixo.
Agora, embora você possa usar normalmente um
Dog*
no lugar de umAnimal*
graças à magia do polimorfismo, o tipo de um ponteiro de função não segue as regras de pesquisa da hierarquia de classes. Portanto, um ponteiro de método Animal não é compatível com um ponteiro de método Dog, em outras palavras, você não pode atribuir aDog* (*)()
a uma variável do tipoAnimal* (*)()
.A estática
newDog
método é um exemplo simples de fábrica, que simplesmente cria e retorna novas instâncias. Por ser uma função estática, ela possui um regulartypedef
(sem qualificador de classe).Tendo respondido às perguntas acima, eu me pergunto se não há uma maneira melhor de alcançar o que você precisa. Existem alguns cenários específicos em que você faria esse tipo de coisa, mas você pode descobrir que existem outros padrões que funcionam melhor para o seu problema. Se você descrever em termos mais gerais o que está tentando alcançar, a colmeia-mente pode ser ainda mais útil!
Relacionado ao acima, você sem dúvida achará a biblioteca Boost bind e outros módulos relacionados muito úteis.
fonte
->*
antes, mas agora espero nunca mais precisar disso :)Não acho que alguém tenha explicado aqui que um problema é que você precisa de " dicas de membros " em vez de ponteiros de função normais.
Os ponteiros de membro para funções não são simplesmente ponteiros de função. Em termos de implementação, o compilador não pode usar um endereço de função simples porque, em geral, você não sabe o endereço a ser chamado até saber para qual objeto remover a referência (pense em funções virtuais). Você também precisa conhecer o objeto para fornecer o
this
parâmetro implícito, é claro.Tendo dito que você precisa deles, agora direi que você realmente precisa evitá-los. Sério, as sugestões dos membros são uma dor. É muito mais lógico olhar para padrões de projeto orientado a objetos que atingem o mesmo objetivo, ou usar um
boost::function
ou qualquer outro como mencionado acima - supondo que você faça essa escolha, claro.Se você estiver fornecendo aquele ponteiro de função para o código existente, então você realmente precisa de um ponteiro de função simples, você deve escrever uma função como um membro estático da classe. Uma função de membro estático não entende
this
, então você precisará passar o objeto como um parâmetro explícito. Era uma vez um idioma não tão incomum ao longo dessas linhas para trabalhar com código C antigo que precisa de ponteiros de funçãoVisto que
myfunction
é apenas uma função normal (questões de escopo à parte), um ponteiro de função pode ser encontrado no modo C normal.EDIT - este tipo de método é chamado de "método de classe" ou "função de membro estático". A principal diferença de uma função não membro é que, se você fizer referência a ela de fora da classe, deverá especificar o escopo usando o
::
operador de resolução de escopo. Por exemplo, para obter o ponteiro de função, use&myclass::myfunction
e para chamá-lo usemyclass::myfunction (arg);
.Esse tipo de coisa é bastante comum ao usar as APIs Win32 antigas, que foram originalmente projetadas para C em vez de C ++. É claro que, nesse caso, o parâmetro normalmente é LPARAM ou semelhante, em vez de um ponteiro, e é necessária alguma conversão.
fonte
fonte
Exemplo de execução mínima
main.cpp
Compile e execute:
Testado no Ubuntu 18.04.
Você não pode alterar a ordem dos parênteses ou omiti-los. O seguinte não funciona:
O GCC 9.2 falharia com:
C ++ 11 padrão
.*
e->*
são operadores únicos introduzidos em C ++ para este propósito, e não presentes em C.Rascunho do padrão C ++ 11 N3337 :
.*
e->*
.fonte
Vim aqui para aprender como criar um ponteiro de função (não um ponteiro de método) a partir de um método, mas nenhuma das respostas aqui fornece uma solução. Aqui está o que eu descobri:
Portanto, para seu exemplo, você faria agora:
Editar:
usando C ++ 17, há uma solução ainda melhor:
que pode ser usado diretamente sem a macro:
Para métodos com modificadores como,
const
você pode precisar de mais algumas especializações, como:fonte
O motivo pelo qual você não pode usar ponteiros de função para chamar funções-membro é que os ponteiros de função comuns geralmente são apenas o endereço de memória da função.
Para chamar uma função de membro, você precisa saber duas coisas:
Ponteiros de função comuns não podem armazenar ambos. Ponteiros de função de membro C ++ são usados para armazenar a), que é o motivo pelo qual você precisa especificar a instância explicitamente ao chamar um ponteiro de função de membro.
fonte
Um ponteiro de função para um membro de classe é um problema realmente adequado para usar boost :: function. Pequeno exemplo:
fonte
Para criar um novo objeto, você pode usar o novo posicionamento, como mencionado acima, ou fazer com que sua classe implemente um método clone () que cria uma cópia do objeto. Você pode então chamar esse método clone usando um ponteiro de função de membro, conforme explicado acima, para criar novas instâncias do objeto. A vantagem do clone é que às vezes você pode estar trabalhando com um ponteiro para uma classe base onde você não conhece o tipo do objeto. Nesse caso, um método clone () pode ser mais fácil de usar. Além disso, clone () permitirá que você copie o estado do objeto se for o que você deseja.
fonte
Fiz isso com std :: function e std :: bind ..
Eu escrevi esta classe EventManager que armazena um vetor de manipuladores em um unordered_map que mapeia tipos de eventos (que são apenas const unsigned int, eu tenho um grande enum com escopo de namespace deles) para um vetor de manipuladores para esse tipo de evento.
Na minha classe EventManagerTests, configurei um manipulador de eventos, como este:
Esta é a função AddEventListener:
Aqui está a definição do tipo EventHandler:
Então, de volta a EventManagerTests :: RaiseEvent, eu faço o seguinte:
Este é o código para EventManager :: RaiseEvent:
Isso funciona. Eu recebo a chamada em EventManagerTests :: OnKeyDown. Tenho que deletar os vetores na hora de limpar, mas depois que faço isso não há vazamentos. Gerar um evento leva cerca de 5 microssegundos no meu computador, que é cerca de 2008. Não é exatamente super rápido, mas. É justo, desde que eu saiba disso e não o use em código ultra quente.
Eu gostaria de acelerá-lo rolando meu próprio std :: function e std :: bind, e talvez usando um array de matrizes em vez de um unordered_map de vetores, mas ainda não descobri como armazenar uma função de membro ponteiro e chamá-lo do código que não sabe nada sobre a classe que está sendo chamada. A resposta de Eyelash parece muito interessante ..
fonte