Me deparei com este estranho trecho de código que compila bem:
class Car
{
public:
int speed;
};
int main()
{
int Car::*pSpeed = &Car::speed;
return 0;
}
Por que o C ++ possui esse ponteiro para um membro de dados não estáticos de uma classe? Qual é a utilidade desse ponteiro estranho no código real?
Respostas:
É um "ponteiro para membro" - o código a seguir ilustra seu uso:
Quanto ao motivo pelo qual você gostaria de fazer isso, ele fornece outro nível de indireção que pode resolver alguns problemas complicados. Mas, para ser sincero, nunca precisei usá-los em meu próprio código.
Edit: Não consigo pensar de maneira improvável em um uso convincente de ponteiros para dados de membros. O ponteiro para funções-membro pode ser usado em arquiteturas conectáveis, mas mais uma vez produzir um exemplo em um espaço pequeno me derrota. A seguir, é minha melhor tentativa (não testada) - uma função Apply que faria algum processamento antes e depois antes de aplicar uma função membro selecionada pelo usuário a um objeto:
Os parênteses ao redor
c->*func
são necessários porque o->*
operador tem precedência menor do que o operador de chamada de função.fonte
Este é o exemplo mais simples que consigo pensar que transmite os raros casos em que esse recurso é pertinente:
O ponto a ser observado aqui é o ponteiro passado para count_fruit. Isso evita que você escreva funções count_apples e count_oranges separadas.
fonte
&bowls.apples
e&bowls.oranges
?&bowl::apples
e&bowl::oranges
não aponta para nada.&bowl::apples
e&bowl::oranges
não aponte para membros de um objeto ; eles apontam para os membros de uma classe . Eles precisam ser combinados com um ponteiro para um objeto real antes de apontar para alguma coisa. Essa combinação é alcançada com o->*
operador.Outra aplicação são listas intrusivas. O tipo de elemento pode dizer à lista quais são seus próximos / anteriores ponteiros. Portanto, a lista não usa nomes codificados, mas ainda pode usar ponteiros existentes:
fonte
next
.Aqui está um exemplo do mundo real no qual estou trabalhando agora, a partir de sistemas de processamento / controle de sinal:
Suponha que você tenha alguma estrutura que represente os dados que você está coletando:
Agora, suponha que você os coloque em um vetor:
Agora, suponha que você queira calcular alguma função (digamos a média) de uma das variáveis em um intervalo de amostras e que você queira fatorar esse cálculo médio em uma função. O ponteiro para membro facilita:
Nota Editada em 05/08/2016 para uma abordagem mais concisa das funções de modelo
E, é claro, você pode modelá-lo para calcular uma média para qualquer iterador futuro e qualquer tipo de valor que suporte adição além de si e divisão por size_t:
EDIT - O código acima tem implicações de desempenho
Você deve observar, como logo descobri, que o código acima tem algumas implicações sérias de desempenho. O resumo é que, se você está calculando uma estatística resumida em uma série temporal, ou calculando uma FFT, etc., deve armazenar os valores para cada variável contiguamente na memória. Caso contrário, a iteração sobre a série causará uma falta de cache para cada valor recuperado.
Considere o desempenho deste código:
Em muitas arquiteturas, uma instância de
Sample
preencherá uma linha de cache. Portanto, em cada iteração do loop, uma amostra será extraída da memória para o cache. Serão usados 4 bytes da linha de cache e o restante jogado fora, e a próxima iteração resultará em outra falha de cache, acesso à memória e assim por diante.Muito melhor para fazer isso:
Agora, quando o primeiro valor x for carregado da memória, os próximos três também serão carregados no cache (supondo um alinhamento adequado), o que significa que você não precisa de nenhum valor carregado para as próximas três iterações.
O algoritmo acima pode ser melhorado um pouco mais através do uso de instruções SIMD em, por exemplo, arquiteturas SSE2. No entanto, elas funcionam muito melhor se os valores estiverem todos contíguos na memória e você pode usar uma única instrução para carregar quatro amostras juntas (mais nas versões posteriores do SSE).
YMMV - projete suas estruturas de dados para se adequar ao seu algoritmo.
fonte
double Sample::*
parte é a chave!Mais tarde, você pode acessar esse membro, em qualquer instância:
Observe que você precisa de uma instância para chamá-la, para que não funcione como um delegado.
É usado raramente, eu precisei talvez uma ou duas vezes em todos os meus anos.
Normalmente, usar uma interface (ou seja, uma classe base pura em C ++) é a melhor opção de design.
fonte
A IBM possui mais documentação sobre como usar isso. Resumidamente, você está usando o ponteiro como um deslocamento para a classe. Você não pode usar esses ponteiros além da classe a que se referem, portanto:
Parece um pouco obscuro, mas um aplicativo possível é se você estiver tentando escrever código para desserializar dados genéricos em muitos tipos diferentes de objetos, e seu código precisar lidar com tipos de objetos sobre os quais não sabe absolutamente nada (por exemplo, seu código é em uma biblioteca e os objetos nos quais você desserializa foram criados por um usuário da sua biblioteca). Os ponteiros de membro oferecem uma maneira genérica e semi-legível de se referir às compensações individuais dos membros de dados, sem a necessidade de recorrer a truques de tipo * nulos * da maneira que você pode usar para estruturas C.
fonte
Permite ligar variáveis e funções de membros de maneira uniforme. A seguir, é apresentado um exemplo com a sua classe Car. O uso mais comum seria obrigatório
std::pair::first
e::second
ao usar nos algoritmos STL e no Boost em um mapa.fonte
Você pode usar uma matriz de ponteiro para dados de membros (homogêneos) para ativar uma interface dupla de membro nomeado (iexdata) e matriz de subscrito (ie x [idx]).
fonte
union
tipo de trocadilho dessa maneira não é permitido pelo padrão, pois invoca inúmeras formas de comportamento indefinido ... enquanto essa resposta está correta.float *component[] = { &x, &y, &z }; return *component[idx];
seja, o ponteiro para componente parece não ter outro objetivo, exceto a ofuscação.Uma maneira que usei é se tenho duas implementações de como fazer algo em uma classe e quero escolher uma em tempo de execução sem ter que passar continuamente por uma instrução if
Obviamente, isso só é praticamente útil se você sentir que o código está sendo martelado o suficiente para que a instrução if esteja atrasando as coisas feitas, por exemplo. profundamente nas entranhas de algum algoritmo intensivo em algum lugar. Eu ainda acho que é mais elegante do que a declaração if, mesmo em situações em que não tem uso prático, mas essa é apenas a minha opinião.
fonte
Algorithm
e duas classes derivadas, por exemplo,AlgorithmA
eAlgorithmB
. Nesse caso, ambos os algoritmos são bem separados e garantem o teste independente.Ponteiros para classes não são ponteiros reais ; uma classe é uma construção lógica e não possui existência física na memória; no entanto, quando você constrói um ponteiro para um membro de uma classe, ele desloca um objeto da classe do membro onde o membro pode ser encontrado; Isso dá uma conclusão importante: Como os membros estáticos não estão associados a nenhum objeto, um ponteiro para um membro NÃO PODE apontar para um membro estático (dados ou funções) de qualquer maneira Considere o seguinte:
Fonte: A referência completa C ++ - Herbert Schildt 4th Edition
fonte
Eu acho que você só gostaria de fazer isso se os dados do membro fossem muito grandes (por exemplo, um objeto de outra classe bastante pesada) e você tiver alguma rotina externa que funcione apenas em referências a objetos dessa classe. Você não deseja copiar o objeto membro, portanto, isso permite distribuí-lo.
fonte
Aqui está um exemplo em que o ponteiro para membros de dados pode ser útil:
fonte
Suponha que você tenha uma estrutura. Dentro dessa estrutura há * algum tipo de nome * duas variáveis do mesmo tipo, mas com significado diferente
Ok, agora digamos que você tenha vários
foo
s em um contêiner:Ok, agora suponha que você carregue os dados de fontes separadas, mas os dados são apresentados da mesma maneira (por exemplo, você precisa do mesmo método de análise).
Você poderia fazer algo assim:
Nesse ponto, a chamada
readValues()
retornará um contêiner com um uníssono de "entrada-a" e "entrada-b"; todas as teclas estarão presentes e os foos com a ou b ou ambos.fonte
Apenas para adicionar alguns casos de uso à resposta do @ anon e do @ Oktalist, aqui está um ótimo material de leitura sobre a função ponteiro para membro e dados de ponteiro para membro.
https://www.dre.vanderbilt.edu/~schmidt/PDF/C++-ptmf4.pdf
fonte