Presque aucune, bien que cela soit certes une réponse étrange, et probablement loin d’être appropriée pour tout le monde.
Mais j’ai trouvé beaucoup plus utile, dans mon cas personnel, de stocker toutes les occurrences d’un type particulier dans une séquence centrale à accès aléatoire (thread-safe) et de travailler plutôt avec des index 32 bits (adresses relatives, par exemple). , plutôt que des pointeurs absolus.
Pour un début:
- Il divise par deux les besoins en mémoire du pointeur analogique sur les plates-formes 64 bits. Jusqu'à présent, je n'ai jamais eu besoin de plus de 4,29 milliards d'instances d'un type de données particulier.
- Il s'assure que toutes les occurrences d'un type particulier
T
, ne seront jamais trop dispersées en mémoire. Cela tend à réduire les erreurs de cache pour tous les types de modèles d'accès, même en traversant des structures liées telles que des arbres si les nœuds sont liés ensemble en utilisant des index plutôt que des pointeurs.
- Les données parallèles deviennent faciles à associer en utilisant des tableaux parallèles peu coûteux (ou des tableaux fragmentés) au lieu d'arbres ou de tables de hachage.
- Les intersections d'ensembles peuvent être trouvées en temps linéaire ou mieux en utilisant, par exemple, un jeu de bits parallèle.
- Nous pouvons trier radicalement les index et obtenir un modèle d'accès séquentiel très convivial pour le cache.
- Nous pouvons suivre le nombre d'allocations d'un type de données particulier.
- Minimise le nombre d'endroits où des problèmes tels que la sécurité des exceptions doivent être traités, si vous vous souciez de ce genre de choses.
Cela dit, la commodité est un inconvénient, tout comme la sécurité. Nous ne pouvons pas accéder à une instance de T
sans avoir accès à la fois au conteneur et à l' index. Et un simple vieux int32_t
ne nous dit rien sur le type de données auquel il fait référence, il n’ya donc pas de type de sécurité. Nous pourrions accidentellement essayer d'accéder à l' Bar
aide d'un index Foo
. Pour atténuer le deuxième problème, je fais souvent ce genre de chose:
struct FooIndex
{
int32_t index;
};
Ce qui semble un peu ridicule, mais cela me redonne le type de sécurité afin que les personnes ne puissent pas accéder accidentellement à Bar
un index Foo
sans une erreur du compilateur. Pour le côté pratique, j’accepte seulement le léger inconvénient.
Une autre chose qui pourrait être un inconvénient majeur pour les gens est que je ne peux pas utiliser un polymorphisme basé sur l'héritage de type POO, car cela nécessiterait un pointeur de base pouvant pointer vers toutes sortes de sous-types différents avec des exigences de taille et d'alignement différentes. Mais je n'utilise pas beaucoup l'héritage ces jours-ci - je préfère l'approche ECS.
En ce shared_ptr
qui me concerne , j'essaie de ne pas trop l'utiliser. La plupart du temps, j'estime qu'il n'est pas logique de partager la propriété, ce qui peut entraîner des fuites logiques. Souvent, au moins à un niveau élevé, une chose a tendance à appartenir à une chose. Là où j’ai souvent trouvé tentant de l’utiliser, shared_ptr
c’était de prolonger la durée de vie d’un objet dans des endroits où la propriété n’était pas vraiment gérée, comme une fonction locale dans un thread pour s’assurer que cet objet n’est pas détruit avant la fin du thread. En l'utilisant.
Pour résoudre ce problème, au lieu d'utiliser shared_ptr
GC ou quelque chose du genre, je privilégie souvent les tâches éphémères exécutées à partir d'un pool de threads, et le fais ainsi si ce thread demande à détruire un objet, que la destruction réelle soit reportée à un coffre-fort. moment où le système peut s’assurer qu’aucun thread n’a besoin d’accéder audit type d’objet.
Parfois, je finis toujours par utiliser le comptage par reflexion mais le traite comme une stratégie de dernier recours. Et il y a quelques cas où il est vraiment logique de partager la propriété, comme la mise en place d'une structure de données persistante, et j'estime qu'il est parfaitement logique de rechercher shared_ptr
tout de suite.
Donc de toute façon, j'utilise principalement des index, et j'utilise avec parcimonie les pointeurs brut et intelligent. J'aime les index et les types de portes qu'ils ouvrent lorsque vous savez que vos objets sont stockés de manière contiguë et non dispersés dans la mémoire.