2010-02-01 13 views
2

J'ai créé un conteneur pour les données génériques de type faible qui est accessible via l'opérateur d'indice.Accès au conteneur et attribution par le même opérateur?

Le conteneur std::map permet à la fois l'accès aux données et l'insertion des éléments via l'opérateur, alors que je ne pense pas que ce soit le cas pour std::vector.

Quelle est la meilleure façon de procéder (style C++)? Dois-je autoriser l'allocation via l'opérateur d'indice ou avoir une méthode d'insertion séparée?

EDIT

devrais-je dire, je ne demande pas si je devrais utiliser vecteur ou un plan, je voulais juste savoir ce que les gens pensaient sur l'accès et l'insertion d'être combinés de cette façon.

+1

Quelle sorte de conteneur? Il y a des raisons pour le comportement de 'std :: map' et' std :: vector'; quel est ton contenant le plus? –

+0

Re: votre édition. Cela dépend toujours de ce que fait votre classe. Le comportement de la carte est plutôt unique en C++, son 'operator []' fournit une fonctionnalité très spécifique. – UncleBens

Répondre

0

Méthode d'insertion séparée, définitivement. Le operator[] sur std::map est juste stupide et rend le code difficile à lire et à déboguer. Aussi vous ne pouvez pas accéder aux données d'un contexte const si vous utilisez un operator[] à insérer (ce qui conduira à un-const-cancer, le cousin encore plus mauvais de const-cancer).

+0

"Rechercher et mettre à jour si la clé existe, sinon insérer par défaut et mettre à jour" est quelque chose que je me retrouve toujours à me battre avec dict de Python. 'std :: map :: operator []' a un but très clair. Et si ça ne fonctionnait pas ainsi, que se passerait-il si la clé n'existait pas? Comportement simplement indéfini? Alors, comment cela serait-il utilisable? Êtes-vous en train de dire qu'il ne devrait pas y avoir une autre méthode utile, parce que ce n'est pas utile si vous voulez seulement faire une recherche? Allez, la langue vous protège même de l'utiliser de manière incorrecte pour la recherche dans des contextes constants. – UncleBens

+0

Je suis d'accord qu'il existe une sorte de protection car elle ne peut pas être utilisée dans un contexte const. Néanmoins, oui, comme pour std :: vector \ deque ou tout autre conteneur avec un opérateur [], l'utiliser signifie que vous êtes sûr que l'index \ key existe, sinon, utilisez la fonction map :: find. –

+0

Contrairement à un vecteur/deque où je sais que des "clés" 0 ... taille existent, avec une carte il n'y a aucun moyen de savoir quelles clés existent (sauf si vous stockez toutes les clés ailleurs - bien sûr vous pouvez tester avec find(), mais alors il serait inutile d'utiliser [] pour accéder à la valeur). Je ne sais pas pour vous, mais j'utilise l'opérateur d'index de map si je me fiche de savoir si la clé existe, car l'ajout d'un défaut est précisément ce que je veux. – UncleBens

2

Dans le cas de vecteurs: La notation d'indice n'insère pas - elle écrase.

Ce reste de ce post distille les informations de l'article 1-5 de Effective STL. Si vous connaissez la portée de vos données avant la main - et que la taille est fixe - et que vous n'insérez pas à des emplacements qui contiennent des données au-dessus - alors vous pouvez utiliser insert dans des vecteurs sans effets secondaires désagréables .

Cependant, dans les insertions cas général de vecteurs ont des implications telles que le décalage des membres vers le haut et la mémoire double ou de épuisée (ce qui provoque un flot de copies provenant des objets de l'ancien vecteur à des emplacements dans le nouveau vecteur) lorsque vous faites ad hoc insertions . Les vecteurs sont conçus pour quand vous connaissez les caractéristiques de localité de vos données.

Les vecteurs viennent avec une fonction de membre d'insertion ... et cette fonction est très intelligente avec la plupart des implémentations en ce qu'elle peut déduire des optimisations des itérateurs votre approvisionnement. Tu ne peux pas utiliser ça?

Si vous souhaitez effectuer des insertions de données ponctuelles, vous devez utiliser une liste. Peut-être pouvez-vous utiliser une liste pour collecter les données, puis, une fois finalisée, remplir un vecteur en utilisant le constructeur basé sur l'intervalle ou basé sur la plage?

+1

Pour nitpick, dans les vecteurs d'objets, il n'est généralement pas possible de 'memcpy()' lors de la croissance d'un vecteur. Il est nécessaire d'appeler le constructeur de la copie. –

+0

@David (+1) Vous avez en effet raison, j'ai mis à jour le texte (j'ai tendance à abstraire). Les constructeurs de copies sont encore pire que memcpy: D –

+1

'std :: map :: operator []' renvoie la valeur si la clé existe, sinon * insère * un objet par défaut pour la clé et la retourne. Comme il est renvoyé par référence, vous pouvez l'écraser. – UncleBens

1

cela dépend de ce que vous voulez. Une carte peut être significativement plus lente qu'un vecteur si vous souhaitez utiliser la chose comme un tableau. Une carte est très utile si l'index que vous voulez utiliser n'est pas séquentiel et que vous en avez CHARGES. Il est généralement plus rapide d'utiliser un vecteur, de le trier et de faire une recherche binaire pour trouver ce que vous recherchez. J'ai utilisé cette méthode pour remplacer des cartes en tonnes de logiciel et je n'ai toujours pas trouvé quelque chose où il était plus lent de le faire avec un vecteur. Donc, IMO, std :: vector est le meilleur moyen, même si une carte pourrait être utile si vous l'utilisez correctement.