C ++, a equivalência entre o ponteiro-para-funções e ponteiro-para-membro-funções?

StackOverflow https://stackoverflow.com/questions/411114

  •  03-07-2019
  •  | 
  •  

Pergunta

Eu estou acostumado a pensar em funções membro como sendo apenas um caso especial de funções normais, onde as funções de membro têm um parâmetro extra no início da sua lista de parâmetro para o ponteiro 'this', isto é, o objeto no qual a função de membro é suposto para agir. Eu usei boost :: função desta forma no passado e nunca encontrou problemas:

boost::function f<(void)(MyObject*, int, int)> = &MyObject::method_that_takes_two_ints;

Mas eu já vi esta sintaxe para ponteiros de funções membro:

void (MyObject::*f)( int, int ) = &MyObject::method_that_takes_two_ints;

Nesta sintaxe, o 'isto' parâmetro não é visível. Que me fez pensar se sob o capô ponteiro-para-membro-funções são realmente uma besta separado, e que o impulso estava cuidando de detalhes para mim.

O que faz a ditar o padrão sobre a colocação do 'isto' parâmetro? Talvez apenas no meu compilador a 'isto' argumento extra vem em primeiro lugar, e talvez em outros compiladores poderia estar no fim? Am I apenas sorte que a minha maneira de pensar é consistente com a forma como os meus compiladores (gcc4, VS2005) lidar com isso? São ponteiro-para-membro-funções sempre apenas um caso especial de ponteiro-para-funções com um parâmetro extra ou pode o compilador implementá-los de forma diferente?

Foi útil?

Solução

A norma diz quase nada sobre onde o ponteiro this deve ser colocado, e de fato é bastante comum o uso de uma convenção de chamada diferente para funções de membro. (Assim, o ponteiro 'this' não é apenas um primeiro argumento extra, na verdade é armazenado em um local diferente do que o primeiro argumento geralmente é)

Em particular, MSVC usa o thiscall convenção de chamada para funções de membro, e stdcall em outro lugar. http://www.hackcraft.net/cpp/MSCallingConventions/#thiscall descreve as diferenças entre eles, mas note que thiscall armazena o ponteiro this no registo ECX, enquanto as lojas stdcall todas parâmetros na pilha.

Você está definitivamente melhor tratá-los como tipos completamente distintos. Um ponteiro para uma função membro é não apenas um ponteiro para uma função com um parâmetro extra.

Outras dicas

O ponteiro this não é armazenada ao longo de um ponteiro para membro (ponteiros de função membro são um caso especial deste). Se você acabou de fazer

void (MyObject::*f)( int, int ) = &MyObject::method_that_takes_two_ints;

então o que é armazenado é apenas a informação que função membro deve ser chamado em um objet que mais tarde tem que fornecer. Se você quiser chamá-lo, você tem que passar um objeto ao longo, onde o compilador terá o ponteiro this de.

MyObject o; (o.*f)(1, 2);

Um ponteiro função membro é apenas um ponteiro membro cujo tipo (que apontavam para) é um tipo de função. The Standard diz que ponteiros de função membro não tem o seu próprio "Tipo de função de membro" que eles apontam para e que de alguma forma iria incluir o tipo este ponteiro.

int main() {
    typedef void fun() const;
    fun MyObject::*mem_function_ptr = 
        &MyObject::const_method_that_takes_two_ints;
}

fun em que o código é o tipo de função. O tipo que uma função "normal" tem. Uma função de ponteiro-para-, em oposição a uma função de ponteiro-membro, é apenas um ponteiro para uma função tendo esse tipo:

void foo() { cout << "hello"; }
int main() {
    typedef void fun();
    fun * f = &foo;
}

Enquanto uma função de membro ponteiro-a-possui o nível de membro-ponteiro adicional no topo do referido tipo de função.

Algo sobre o ponteiro this e como se relaciona com o objeto que ele aponta para (, apenas o material não técnico teórico):

Cada função membro tem um parâmetro oculto chamado implicit object parameter que tem tipo MyObject& ou MyObject const& dependendo se você tem um const ou função membro nonconst. O objeto que você chamar a função membro em, o, é a implied object argument, que é passado para o parâmetro. Na teoria do padrão que compõem as regras que descrevem como funções membro são chamados, o parâmetro objeto implícito é um primeiro parâmetro oculto. Isso é conceitual, e não significa que é o caso real em implementações. O argumento de objeto implícito é então ligado a esse parâmetro objeto implícito, possivelmente causando conversões implícitas (por isso, se você chamar uma função membro const em um objeto não-const, um convertidos conversão qualificação de MyObject para MyObject const&. Isso é o que torna as funções não-const uma escolha melhor do que funções const para chamada, por um objeto não-const). Por exemplo, pode-se dizer neste código:

struct A {
    operator int() const { return 0; }
};

int main() { 
    A a;
    int i = a; // implicit conversion using the conversion function
}

Que o a argumento de objeto implícito do tipo A está vinculado ao parâmetro de objeto implícito do tipo A const&, cujo objeto é então apontado pelo ponteiro this ter o tipo A const* aqui. Importante notar é que o parâmetro objeto implícito é apenas uma construção teórica, para formalizar como as regras para chamar uma função membro são feitas (e construtores não incluí-los), enquanto a este ponteiro é realmente existente. this é um ponteiro, porque quando this foi introduzido, C ++ não têm referências ainda.

Espero que o ajudará a entender o assunto.

Um excelente artigo sobre ponteiros de função membro é de CodeProject Membro Função ponteiros ea mais rápida possível C ++ delegados . Este artigo descreve ponteiros de função membro dos casos simples todo o caminho através de ponteiros de função de membro virtual com herança múltipla. Como um bônus, que fornece uma implementação de delegados que podem ser realmente útil.

Sim, ponteiros-to-funções e ponteiros-to-membros são completamente diferentes animais. Os apontadores para os membros têm de ser dado um exemplo objecto a ser desreferenciado usando os operadores ->* ou .*. Não há nenhum parâmetro this utilizado ao fazer um ponteiro-se membro porque this é determinado quando o ponteiro ao membro é usado (o objecto do lado esquerdo do ->* ou .*).

Observe o que há provavelmente menos de uma diferença entre uma função de ponteiro-para-membro e uma variável de ponteiro-para-membro do que há entre uma função ponteiro-para-membro e um ponteiro de função regular.

Normalmente funções de membro e funções regulares pode ter completamente diferentes convenções de chamada para que você não pode lançar entre eles.

Note-se que o tamanho de uma função-para-apontadora de membro pode ser diferente, utilizando diferentes compiladores.

Outra coisa a nota, como está escrito na O Old New coisa blogue :

O tamanho de um ponteiro-to-função de membro pode mudar dependendo da classe.

Eles são definitivamente tipos distintos e quaisquer suposições que você fizer vai ser plataforma / específica do compilador.

Esta página tem mais informações sobre a implementação de pontos de função de membro de Eu sempre quis saber, incluindo detalhes de implementação para numerosos compiladores populares.

Para responder a todas as perguntas: Sim, eles são ponteiros especiais, diferentes dos ponteiros comuns. Sim, boost :: função reconhece-los.

O padrão não diz nada sobre os detalhes internos de pilhas de chamadas. Na verdade, muitos compiladores podem usar um inteiro rergisters, registradores de ponto flutuante, e / ou a pilha de acordo com a lista de argumentos real. Um ponteiro 'this' é apenas um caso mais especial.

boost :: função resolve esse problema usando dois caminhos de código internamente. Você pode ver isso inspecionando a pilha de chamadas para os dois casos. Se seus boost :: função armazena um ponteiro para função de membro, o operador () irá dividir a lista de argumentos. O primeiro argumento é usado como o objeto no qual a função membro é chamado com os argumentos restantes.

Para responder suplemento de todo mundo, Boost.Function funciona especializando o operador de atribuição de ponteiros de função de membro, para permitir que ele para detectar quando você passou um. Quando você chamar essa função, ele irá reinterpretá-la internamente para o método adequado de chamar o ponteiro função membro ((obj->*fun)(args)).

Eu acho que você pode encontrar neste link bastante interessante:

http://www.parashift.com/c++ -faq-lite / ponteiros-to-members.html

É uma descrição muito boa de tudo o que você gostaria de saber sobre o ponteiro-to-membros.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top