Question

Sans aucun doute, je choisirais d’utiliser la STL pour la plupart des projets de programmation C ++. La question m’a récemment été posée: "Existe-t-il des cas où vous ne voudriez pas utiliser le STL?" ...

Plus j'y réfléchissais, plus je réalisais qu'il devrait DEVRAIT y avoir des cas où je choisirais de ne pas utiliser le STL ... Par exemple, un très grand projet à long terme dont la base de code devrait durer des années. Peut-être qu'une solution de conteneur personnalisée qui répond précisément aux besoins du projet vaut la peine initiale? Que pensez-vous, existe-t-il des cas où vous choisiriez de NE PAS STL?

Était-ce utile?

La solution

Les projets avec des exigences strictes en matière de mémoire, par exemple pour les systèmes intégrés, peuvent ne pas convenir à la STL, car il peut être difficile de contrôler et de gérer ce qui est pris et renvoyé dans le tas. Comme Evan l'a mentionné, l'écriture d'allocateurs appropriés peut vous aider, mais si vous comptez chaque octet utilisé ou concerné par la fragmentation de la mémoire, il serait peut-être plus sage de lancer manuellement une solution adaptée à votre problème spécifique, car le STL a été optimisé. pour l'usage le plus général.

Vous pouvez également choisir de ne pas utiliser STL pour un cas particulier, car il existe plusieurs conteneurs applicables qui ne figurent pas dans la norme actuelle, tels que boost :: array ou boost :: unordered_map.

Autres conseils

Les principales raisons de ne pas utiliser STL sont les suivantes:

  1. Votre implémentation C ++ est ancienne et supporte des modèles épouvantables.
  2. Vous ne pouvez pas utiliser l'allocation de mémoire dynamique.

Dans la pratique, les deux conditions sont très rares.

Pour un projet à long terme, déployer vos propres conteneurs dont les fonctionnalités se chevauchent avec le STL ne fera qu'augmenter les coûts de maintenance et de développement.

Il y a tellement d'avantages à utiliser stl. Pour un projet à long terme, les avantages sont supérieurs aux coûts.

  1. Les nouveaux programmeurs sont capables de comprendre les conteneurs dès le premier jour, ce qui leur laisse plus de temps pour apprendre l'autre code du projet. (en supposant qu'ils connaissent déjà STL comme le ferait n'importe quel programmeur C ++ compétent)
  2. La correction des bogues dans les conteneurs est une perte de temps et un gaspillage de temps qui pourrait être consacré à l'amélioration de la logique métier.
  3. Il est fort probable que vous ne les écriviez pas et que la STL est de toute façon mise en œuvre.

Cela étant dit, les conteneurs STL ne traitent pas du tout la concurrence. Donc, dans un environnement où vous avez besoin de simultanéité, j'utiliserais d'autres conteneurs, tels que les conteneurs simultanés Intel TBB. Celles-ci sont bien plus avancées avec le verrouillage fin, de sorte que différents threads peuvent modifier le conteneur simultanément sans qu'il soit nécessaire de sérialiser l'accès au conteneur.

Habituellement, j'estime que le mieux est d'utiliser la LIST avec des allocateurs personnalisés au lieu de remplacer les conteneurs LIST par des roulés à la main. La bonne chose à propos de la STL est que vous ne payez que ce que vous utilisez.

Je pense que c'est un scénario typique vs achat. Cependant, je pense que dans ce cas, je voudrais presque toujours «acheter» et utiliser STL - ou une meilleure solution (quelque chose de Boost peut-être), avant de lancer la mienne. Vous devriez concentrer l'essentiel de vos efforts sur les tâches de votre application, et non sur les blocs de construction qu'elle utilise.

Je ne le pense pas vraiment. En fabriquant mes propres conteneurs, j'essaierais même de les rendre compatibles avec la STL car la puissance des algorithmes génériques est trop importante pour être abandonnée. Vous devez au moins utiliser nominalement la STL, même si vous ne faites qu'écrire votre propre conteneur et en spécialiser chaque algorithme. De cette façon, chaque algorithme de tri peut être appelé sort (c.begin (), c.end ()). Si vous spécialisez le tri pour avoir le même effet, même s'il fonctionne différemment.

Codage pour Symbian.

STLPort prend en charge Symbian 9, le cas de non-utilisation de STL est donc plus faible qu’avant ("ce n’est pas disponible" est un cas assez convaincant), mais STL est toujours étranger à toutes les bibliothèques Symbian. plus de problèmes que de simplement faire les choses à la manière Symbian.

Bien entendu, on pourrait faire valoir pour ces motifs que le codage pour Symbian n’est pas un "projet de programmation C ++".

La plupart des projets sur lesquels j'ai travaillé avaient un code base plus ancien que toute version réellement utilisable de STL - nous avons donc choisi de ne pas l'introduire maintenant.

Cela peut se produire lorsque vous utilisez déjà une bibliothèque externe qui fournit déjà les fonctionnalités dont vous avez besoin à partir de la STL. Par exemple, ma société développe une application dans des zones limitées en espace et utilise déjà Qt pour le toolkit de fenêtrage. Etant donné que Qt fournit des classes de conteneur de type STL, nous les utilisons au lieu d’ajouter le STL à notre projet.

Introduction:

STL est une excellente bibliothèque, utile dans de nombreux cas, mais elle ne résout définitivement pas toutes les situations. Répondre à STL ou! STL, c'est comme répondre "Est-ce que STL répond à vos besoins ou non?"

Les avantages du TSL

  • Dans la plupart des situations, STL dispose d'un conteneur qui convient à une solution donnée.
  • C'est bien documenté
  • C'est bien connu (généralement les programmeurs le savent déjà, il est plus court d'entrer dans un projet)
  • Il est testé et stable.
  • C'est une plateforme croisée
  • Il est inclus avec chaque compilateur (n’ajoute pas de dépendance à la 3ème bibliothèque)
  • STL est déjà implémenté et prêt
  • STL est brillant, ...

Contras de STL

Peu importe que vous ayez besoin d’un simple graphe, d’un arbre rouge-noir ou d’une base de données très complexe avec une intelligence artificielle gérant un accès simultané via un ordinateur quantique. Le fait est que STL ne résout pas et ne réglera jamais tout.

Les aspects suivants ne sont que quelques exemples, mais ils en sont essentiellement la conséquence: STL est une véritable bibliothèque avec des limites.

  • Exceptions: relais STL sur les exceptions, donc si pour une raison quelconque vous ne pouvez pas accepter les exceptions (par exemple, critiques pour la sécurité), vous ne pouvez pas utiliser STL. Droite! des exceptions peuvent être désactivées, mais cela ne résout pas le problème de la conception de la STL et finira par provoquer un crash.

  • Besoin d'une structure de données spécifique (pas encore incluse): graphique, arborescence, etc.

  • Contraintes de complexité particulières: vous pourriez découvrir que le conteneur à usage général STL n’est pas l’optimum pour votre code de goulot d’étranglement.

  • Considérations sur la simultanéité: soit vous avez besoin de la simultanéité, soit le format STL ne fournit pas ce dont vous avez besoin (par exemple, le verrou lecteur-graveur ne peut pas (facilement) être utilisé en raison de l'opérateur [] bidirectionnel) . Soit vous pouvez concevoir un conteneur tirant parti du multi-threading pour un accès / recherche / insertion / quoi que ce soit beaucoup plus rapide.

  • STL doit répondre à vos besoins, mais l'inverse est également vrai: vous devez répondre aux besoins de STL. N'essayez pas d'utiliser std :: vector dans un microcontrôleur intégré avec 1K de RAM non gérée.

  • Compatibilité avec d'autres bibliothèques: il se peut que pour des raisons historiques, les bibliothèques que vous utilisez n'acceptent pas le format STL (par exemple, QtWidgets en fait un usage intensif, sa propre liste QList). Convertir les conteneurs dans les deux sens peut ne pas être la meilleure solution.

Implémentation de votre propre conteneur

Après avoir lu cela, vous pourriez penser: " Eh bien, je suis sûr de pouvoir faire quelque chose de mieux que le STL pour mon cas spécifique. " ATTENDRE!

La mise en œuvre correcte de votre conteneur devient très rapidement une tâche énorme: il ne s’agit pas seulement de mettre en œuvre quelque chose qui fonctionne, vous devrez peut-être:

  • Documentez-le en profondeur, y compris les limitations, la complexité de l'algorithme, etc.
  • Attendez-vous à des bugs et résolvez-les
  • Besoins supplémentaires entrants: vous savez, cette fonction est manquante, cette conversion entre types, etc.
  • Au bout d'un moment, vous voudrez peut-être refactoriser et changer toutes les dépendances (trop tard?)
  • ....
  

Le code utilisé est qu’au fond du code, comme un conteneur, il faut du temps pour le mettre en oeuvre et qu’il doit être soigneusement étudié.

Utilisation d'une bibliothèque tierce

Non STL ne signifie pas nécessairement coutume. Il y a beaucoup de bonnes bibliothèques sur le réseau, certaines même avec une licence ouverte permissive.

Ajouter ou non une bibliothèque tierce supplémentaire est un autre sujet, mais cela vaut la peine d'être considéré.

J'ai rencontré des problèmes d'utilisation de STL dans du code multithread. Même si vous ne partagez pas les objets STL entre les threads, de nombreuses implémentations utilisent des constructions non sécuritaires pour les threads (comme ++ pour le comptage de références au lieu d'un style d'incrémentation interlocké ou avec des allocateurs non sécurisés pour les threads).

Dans chacun de ces cas, j'ai quand même choisi d'utiliser STL et de résoudre les problèmes (il y a suffisamment de points d'ancrage pour obtenir ce que vous voulez).

Même si vous choisissez de créer vos propres collections, il serait judicieux de suivre le style STL pour les itérateurs afin de pouvoir utiliser des algorithmes et d'autres fonctions STL ne fonctionnant que sur des itérateurs.

Le problème principal que j’ai vu est de devoir intégrer un code hérité reposant sur un nouvel opérateur non lanceur.

J'ai commencé à programmer C vers 1984 environ et je n’ai jamais utilisé la STL. Au fil des ans, j'ai créé mes propres bibliothèques de fonctions. Elles ont évolué et se sont développées lorsque la STL n'était pas encore stable et / ou manquait de prise en charge multiplateforme. Ma bibliothèque commune a grandi pour inclure du code par d'autres (principalement des choses comme libjpeg, libpng, ffmpeg, mysql) et quelques autres et je préférerais garder la quantité de code externe au minimum. Je suis sûr que la STL est excellente, mais franchement, je suis satisfait des éléments de ma boîte à outils et je ne vois pas la nécessité de la charger avec plus d'outils. Mais je vois certainement les grands sauts et les limites que les nouveaux programmeurs peuvent faire en utilisant la STL sans avoir à coder tout cela à partir de zéro.

C ++ standard autorise perversement l'implémentation de certaines opérations itératives pour lever des exceptions . Cette possibilité peut être problématique dans certains cas. Vous pouvez donc implémenter votre propre conteneur simple qui garantit de ne pas générer d'exceptions pour les opérations critiques.

Étant donné que presque tous ceux qui ont répondu avant moi semblaient si friands des conteneurs STL, j’ai pensé qu’il serait utile de dresser une liste des bonnes raisons de ne pas les utiliser, à partir des problèmes rencontrés par moi-même.

Celles-ci peuvent être raisonnablement regroupées en trois grandes catégories:

1) Faible efficacité

Les conteneurs STL s'exécutent généralement plus lentement ET utilisent trop de mémoire pour le travail. La raison en est peut-être en partie imputée à des implémentations trop génériques des algorithmes et des structures de données sous-jacentes, avec des coûts de performance supplémentaires liés à toutes les contraintes de conception supplémentaires requises par les tonnes de prérequis d'API non pertinents pour la tâche à exécuter.

Une utilisation imprudente de la mémoire et des performances médiocres vont de pair, car la mémoire est adressée par l’UC en cache sur des lignes de 64 octets. Si vous n’utilisez pas la localité de référence à votre avantage, vous perdez des cycles ET de précieux Kb. de la mémoire cache.

Par exemple, std :: list nécessite 24 octets par élément plutôt que le nombre optimal 4.

https://lemire.me/blog/2016/09/15/the-memory-usage-of-stl-containers-can-be-surprising/

En effet, il est implémenté en compressant deux pointeurs 64 bits, 1 int et 4 octets de remplissage de la mémoire, plutôt que de faire quelque chose d'aussi fondamental que l'allocation de petites quantités de mémoire contiguë et le suivi séparé des éléments utilisés, ou en utilisant la technique du pointeur xor pour stocker les deux directions d'itération dans un pointeur.

https://en.wikipedia.org/wiki/XOR_linked_list

En fonction des besoins de votre programme, ces inefficiences peuvent s’ajouter à de grands succès.

2) Limites / normes rampantes

Bien entendu, le problème vient parfois du fait que vous avez besoin d’une fonction parfaitement commune ou d’une classe de conteneur légèrement différente qui n’est tout simplement pas implémentée dans STL, telle que decl_min () dans une file d’attente prioritaire.

Une pratique courante consiste ensuite à encapsuler le conteneur dans une classe et à implémenter la fonctionnalité manquante vous-même avec un état supplémentaire externe au conteneur et / ou plusieurs appels à des méthodes de conteneur, qui peuvent imiter le comportement souhaité, mais avec une performance bien supérieure. complexité inférieure et O () supérieure à une implémentation réelle de la structure de données, car il n'y a aucun moyen d'étendre le fonctionnement interne du conteneur. Sinon, vous finissez par mélanger deux ou plusieurs conteneurs différents parce que vous avez simultanément besoin de deux choses ou plus fondamentalement incompatibles dans un conteneur STL donné, telles qu'un tas minmax, un trie (car vous devez pouvoir utiliser des pointeurs agnostiques ), etc.

Ces solutions sont peut-être laides et viennent s'ajouter aux autres inefficacités. Cependant, la façon dont le langage évolue a tendance à ne plus ajouter que de nouvelles méthodes STL correspondant au flot de fonctionnalités de C ++ et ignorant les fonctionnalités de base manquantes.

3) Concurrence / parallélisme

Les conteneurs STL ne sont pas thread-safe, encore moins concurrents. À l'ère actuelle des processeurs grand public à 16 threads, il est surprenant que l'implémentation de conteneur par défaut pour un langage moderne vous oblige encore à écrire des mutex autour de chaque accès mémoire, comme en 1996. C'est, pour tout programme parallèle non trivial, C’est une bonne affaire, car avoir des barrières de mémoire oblige les threads à sérialiser leur exécution, et si cela se produit à la même fréquence qu’un appel STL, vous pouvez dire adieu à votre performance parallèle.

En bref, la STL est bonne tant que vous ne vous souciez pas des performances, de l’utilisation de la mémoire, des fonctionnalités ou du parallélisme. STL est bien sûr toujours parfaitement adapté aux nombreuses fois où vous n’êtes lié par aucune de ces préoccupations et par d’autres priorités telles que la lisibilité, la portabilité, la maintenabilité ou la vitesse de codage prévalent.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top