125 votes

vector::at vs. vector::operator[]

Je sais que at() est plus lent que [] en raison de son contrôle des limites, qui est également abordé dans des questions similaires telles que Vitesse de l'opérateur C++ Vector at/[] ou ::std::vector::at() vs operator[] << résultats surprenants ! !! 5 à 10 fois plus lent/rapide ! . Je ne comprends pas ce que le at() La méthode est bonne pour.

Si j'ai un vecteur simple comme celui-ci : std::vector<int> v(10); et je décide d'accéder à ses éléments en utilisant at() au lieu de [] dans une situation où j'ai un indice i et je ne suis pas sûr que ce soit dans les limites des vecteurs, cela m'oblige à l'envelopper dans un bloc try-catch :

try
{
    v.at(i) = 2;
}
catch (std::out_of_range& oor)
{
    ...
}

bien que je sois capable d'obtenir le même comportement en utilisant size() et de vérifier l'index par moi-même, ce qui me semble plus facile et plus pratique pour moi :

if (i < v.size())
    v[i] = 2;

Donc ma question est :
Quels sont les avantages d'utiliser vecteur::at sur vecteur::opérateur[] ?
Quand dois-je utiliser vecteur::at plutôt que vecteur::taille + vecteur::opérateur[] ?

87voto

pmdj Points 5911

Je dirais que les exceptions qui vector::at() ne sont pas vraiment destinés à être pris en compte par le code qui les entoure immédiatement. Ils sont principalement utiles pour détecter les bogues dans votre code. Si vous avez besoin de vérifier les limites au moment de l'exécution parce que, par exemple, l'indice provient de l'entrée de l'utilisateur, il est en effet préférable d'utiliser une méthode de type if déclaration. Donc, en résumé, concevez votre code avec l'intention que vector::at() ne lèvera jamais d'exception, de sorte que s'il le fait et que votre programme s'arrête, c'est le signe d'un bogue. (tout comme un assert() )

18voto

Alexandre C. Points 31758

cela m'oblige à l'entourer d'un bloc try-catch

Non, ce n'est pas le cas (le bloc try/catch peut être en amont). Il est utile lorsque vous souhaitez qu'une exception soit levée plutôt que de faire entrer votre programme dans le domaine des comportements indéfinis.

Je suis d'accord sur le fait que la plupart des accès hors limites aux vecteurs sont une erreur du programmeur (dans ce cas, vous devriez utiliser la fonction assert pour localiser ces erreurs plus facilement ; la plupart des versions de débogage des bibliothèques standard le font automatiquement pour vous). Vous ne voulez pas utiliser des exceptions qui peuvent être avalées en amont pour signaler les erreurs du programmeur : vous voulez être capable de fixer le bug .

Comme il est peu probable qu'un accès hors limites à un vecteur fasse partie du déroulement normal du programme (si c'est le cas, vous avez raison : vérifiez au préalable avec la commande size au lieu de laisser l'exception surgir), je suis d'accord avec votre diagnostic : at est essentiellement inutile.

11voto

Tony D Points 43962

Quels sont les avantages de l'utilisation de vector::at par rapport à vector::operator[] ? Quand dois-je utiliser vector::at plutôt que vector::size + vector::operator[] ?

Le point important ici est que les exceptions permettent de séparer le flux normal du code de la logique de gestion des erreurs, et qu'un seul bloc catch peut gérer les problèmes générés par une myriade de sites throw, même s'ils sont disséminés dans les appels de fonction. Donc, ce n'est pas que at() est nécessairement plus facile pour une seule utilisation, mais que parfois cela devient plus facile - et moins obscurcissant pour la logique du cas normal - lorsque vous avez beaucoup d'indexation à valider.

Il est également intéressant de noter que dans certains types de code, un index est incrémenté de manière complexe, et utilisé continuellement pour consulter un tableau. Dans de tels cas, il est beaucoup plus facile d'assurer des vérifications correctes en utilisant les méthodes suivantes at() .

À titre d'exemple concret, j'ai un code qui segmente le langage C++ en éléments lexicaux, puis un autre code qui déplace un index sur le vecteur des éléments lexicaux. En fonction de ce qui est rencontré, je peux souhaiter incrémenter et vérifier l'élément suivant, comme dans :

if (token.at(i) == Token::Keyword_Enum)
{
    ASSERT_EQ(tokens.at(++i), Token::Idn);
    if (tokens.at(++i) == Left_Brace)
        ...
    or whatever

Dans ce genre de situation, il est très difficile de vérifier si vous avez inapproprié a atteint la fin de l'entrée car cela dépend beaucoup des tokens exacts rencontrés. La vérification explicite à chaque point d'utilisation est pénible, et il y a beaucoup plus de place pour les erreurs de programmation lorsque les incréments avant/après, les décalages au point d'utilisation, les raisonnements erronés sur la validité continue d'un test précédent, etc. entrent en jeu.

11voto

Brangdon Points 411

at peut être plus clair si vous avez un pointeur sur le vecteur :

return pVector->at(n);
return (*pVector)[n];
return pVector->operator[](n);

Performances mises à part, le premier de ces éléments est un code plus simple et plus clair.

8voto

James Kanze Points 96599

Dans les builds de débogage, il n'est pas garanti pour at() pour être plus lent que operator[] Je m'attendrais à ce qu'ils soient à peu près à la même vitesse. La différence est que at() spécifie exactement ce qui se passera en cas d'erreur de limites (une exception), où comme dans le cas de operator[] il s'agit d'un comportement indéfini - un crash dans tous les systèmes que j'utilise (g++ et VC++), du moins lorsque les drapeaux de débogage normaux sont utilisés. (Une autre différence est qu'une fois que je suis sûr de mon code, je peux obtenir une augmentation substantielle de la vitesse d'exécution de operator[] en désactivant le débogage. Si les performances l'exigent - je ne le ferais que si c'était nécessaire).

En pratique, at() est rarement approprié. Si le contexte est tel que vous savez que l'index peut être invalide, vous voulez probablement un test explicite (par exemple, retourner une valeur par défaut ou autre), et si vous savez qu'il ne peut pas être invalide, vous voulez abandonner (et si vous ne savez pas s'il peut être invalide ou non, je vous suggère de spécifier plus précisément l'interface de votre fonction). Il y a quelques exceptions, cependant, où l'index invalide peut résulter de l'analyse des données de l'utilisateur, et l'erreur devrait provoquer l'abandon de toute la requête (mais pas l'arrêt du serveur) ; dans de tels cas, une exception est appropriée, et at() fera pour vous.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X