Je suis tombé sur cet extrait de code étrange qui compile très bien:
class Car
{
public:
int speed;
};
int main()
{
int Car::*pSpeed = &Car::speed;
return 0;
}
Pourquoi C ++ a-t-il ce pointeur vers un membre de données non statique d'une classe? Quelle est l'utilité de cet étrange pointeur dans du vrai code?
Réponses:
C'est un "pointeur vers un membre" - le code suivant illustre son utilisation:
Quant à savoir pourquoi vous voudriez faire cela, eh bien cela vous donne un autre niveau d'indirection qui peut résoudre certains problèmes délicats. Mais pour être honnête, je n'ai jamais eu à les utiliser dans mon propre code.
Edit: je ne peux pas penser à part d'une utilisation convaincante pour les pointeurs vers les données des membres. Le pointeur sur les fonctions membres peut être utilisé dans des architectures enfichables, mais encore une fois, produire un exemple dans un petit espace me bat. Ce qui suit est mon meilleur essai (non testé) - une fonction Appliquer qui effectuerait un pré et post-traitement avant d'appliquer une fonction membre sélectionnée par l'utilisateur à un objet:
Les parenthèses autour
c->*func
sont nécessaires car l'->*
opérateur a une priorité plus faible que l'opérateur d'appel de fonction.la source
Ceci est l'exemple le plus simple auquel je puisse penser et qui traduit les rares cas où cette fonctionnalité est pertinente:
La chose à noter ici est le pointeur passé à count_fruit. Cela vous évite d'avoir à écrire des fonctions count_apples et count_oranges distinctes.
la source
&bowls.apples
ainsi&bowls.oranges
?&bowl::apples
et&bowl::oranges
ne pointe vers rien.&bowl::apples
et&bowl::oranges
ne pointez pas sur les membres d'un objet ; ils désignent les membres d'une classe . Ils doivent être combinés avec un pointeur sur un objet réel avant de pointer vers quelque chose. Cette combinaison est réalisée avec l'->*
opérateur.Une autre application sont les listes intrusives. Le type d'élément peut indiquer à la liste quels sont ses pointeurs suivant / précédent. La liste n'utilise donc pas de noms codés en dur mais peut toujours utiliser des pointeurs existants:
la source
next
.Voici un exemple concret sur lequel je travaille en ce moment, à partir des systèmes de traitement / contrôle du signal:
Supposons que vous ayez une structure qui représente les données que vous collectez:
Supposons maintenant que vous les bourriez dans un vecteur:
Supposons maintenant que vous vouliez calculer une fonction (disons la moyenne) d'une des variables sur une plage d'échantillons, et que vous souhaitiez factoriser ce calcul moyen dans une fonction. Le pointeur sur membre simplifie:
Note modifiée le 08/08/2016 pour une approche modèle-fonction plus concise
Et, bien sûr, vous pouvez le modéliser pour calculer une moyenne pour tout itérateur direct et tout type de valeur qui prend en charge l'addition avec lui-même et la division par size_t:
EDIT - Le code ci-dessus a des implications sur les performances
Vous devriez noter, comme je l'ai rapidement découvert, que le code ci-dessus a de sérieuses implications en termes de performances. Le résumé est que si vous calculez une statistique récapitulative sur une série temporelle ou calculez une FFT, etc., vous devez stocker les valeurs de chaque variable de manière contiguë en mémoire. Sinon, l'itération sur la série entraînera un échec de cache pour chaque valeur récupérée.
Considérez les performances de ce code:
Sur de nombreuses architectures, une instance de
Sample
remplira une ligne de cache. Ainsi, à chaque itération de la boucle, un échantillon sera extrait de la mémoire dans le cache. 4 octets de la ligne de cache seront utilisés et le reste jeté, et la prochaine itération entraînera un autre échec de cache, un accès à la mémoire, etc.Il vaut mieux faire cela:
Maintenant, lorsque la première valeur x est chargée à partir de la mémoire, les trois suivantes seront également chargées dans le cache (en supposant un alignement approprié), ce qui signifie que vous n'avez pas besoin de valeurs chargées pour les trois prochaines itérations.
L'algorithme ci-dessus peut être amélioré un peu plus en utilisant des instructions SIMD, par exemple sur les architectures SSE2. Cependant, ceux-ci fonctionnent beaucoup mieux si les valeurs sont toutes contiguës en mémoire et que vous pouvez utiliser une seule instruction pour charger quatre échantillons ensemble (davantage dans les versions SSE ultérieures).
YMMV - concevez vos structures de données en fonction de votre algorithme.
la source
double Sample::*
partie est la clé!Vous pouvez accéder ultérieurement à ce membre, sur n'importe quelle instance:
Notez que vous avez besoin d'une instance pour l'appeler, elle ne fonctionne donc pas comme un délégué.
Il est rarement utilisé, j'en ai eu besoin une ou deux fois au cours de toutes mes années.
Normalement, utiliser une interface (c'est-à-dire une classe de base pure en C ++) est le meilleur choix de conception.
la source
IBM a plus de documentation sur la façon de l'utiliser. En bref, vous utilisez le pointeur comme décalage dans la classe. Vous ne pouvez pas utiliser ces pointeurs en dehors de la classe à laquelle ils se réfèrent, donc:
Cela semble un peu obscur, mais une application possible est si vous essayez d'écrire du code pour désérialiser des données génériques dans de nombreux types d'objets différents, et que votre code doit gérer des types d'objets dont il ne sait absolument rien (par exemple, votre code est dans une bibliothèque, et les objets dans lesquels vous désérialisez ont été créés par un utilisateur de votre bibliothèque). Les pointeurs membres vous donnent un moyen générique et semi-lisible de faire référence aux décalages individuels des membres de données, sans avoir à recourir à des astuces void * sans type comme vous le feriez pour les structures C.
la source
Il permet de lier les variables et fonctions membres de manière uniforme. Voici un exemple avec votre classe de voiture. Une utilisation plus courante serait contraignante
std::pair::first
et::second
lors de l'utilisation dans les algorithmes STL et Boost sur une carte.la source
Vous pouvez utiliser un tableau de pointeurs vers des données de membre (homogènes) pour activer une interface double, membre nommé (iexdata) et tableau-indice (ie x [idx]).
la source
union
pour taper-pun de cette manière n'est pas autorisé par la norme car il invoque de nombreuses formes de comportement indéfini ... alors que cette réponse est correcte.float *component[] = { &x, &y, &z }; return *component[idx];
c'est -à- dire que le pointeur vers le composant semble ne servir à rien sauf à l'obscurcissement.Une façon dont je l'ai utilisé est si j'ai deux implémentations sur la façon de faire quelque chose dans une classe et que je veux en choisir une au moment de l'exécution sans avoir à parcourir continuellement une instruction if, c'est-à-dire
De toute évidence, cela n'est utile que si vous sentez que le code est suffisamment martelé pour que l'instruction if ralentisse les choses, par exemple. profondément dans les entrailles d'un algorithme intensif quelque part. Je pense toujours qu'elle est plus élégante que la déclaration if même dans des situations où elle n'a aucune utilité pratique, mais ce n'est que mon opinion.
la source
Algorithm
et deux classes dérivées, par exemple,AlgorithmA
etAlgorithmB
. Dans un tel cas, les deux algorithmes sont bien séparés et sont garantis pour être testés indépendamment.Les pointeurs vers les classes ne sont pas de vrais pointeurs; une classe est une construction logique et n'a aucune existence physique en mémoire, cependant, lorsque vous construisez un pointeur vers un membre d'une classe, cela donne un décalage dans un objet de la classe du membre où le membre peut être trouvé; Cela donne une conclusion importante: puisque les membres statiques ne sont associés à aucun objet, un pointeur vers un membre NE PEUT PAS pointer vers un membre statique (données ou fonctions) que ce soit. Tenez compte des points suivants:
Source: La référence complète C ++ - Herbert Schildt 4e édition
la source
Je pense que vous ne voudriez le faire que si les données des membres étaient assez grandes (par exemple, un objet d'une autre classe assez lourde), et que vous avez une routine externe qui ne fonctionne que sur les références aux objets de cette classe. Vous ne voulez pas copier l'objet membre, cela vous permet donc de le faire circuler.
la source
Voici un exemple où un pointeur vers des membres de données pourrait être utile:
la source
Supposons que vous ayez une structure. À l'intérieur de cette structure se trouvent * une sorte de nom * deux variables du même type mais avec une signification différente
Bon, maintenant disons que vous avez un tas de
foo
s dans un conteneur:Bon, supposons maintenant que vous chargez les données à partir de sources distinctes, mais les données sont présentées de la même manière (par exemple, vous avez besoin de la même méthode d'analyse).
Vous pouvez faire quelque chose comme ça:
À ce stade, l'appel
readValues()
renvoie un conteneur avec l'unisson de "input-a" et "input-b"; toutes les clés seront présentes et les foos auront a ou b ou les deux.la source
Juste pour ajouter quelques cas d'utilisation pour la réponse de @ anon et @ Oktalist, voici un excellent matériel de lecture sur la fonction pointeur vers membre et les données pointeur vers membre.
https://www.dre.vanderbilt.edu/~schmidt/PDF/C++-ptmf4.pdf
la source