Tableaux statiques VS. tableaux dynamics en C ++ 11

Je sais que c’est un très vieux débat qui a déjà fait l’object de nombreuses discussions dans le monde entier. Mais j’ai actuellement du mal à choisir la méthode à utiliser plutôt qu’une autre entre des tableaux statiques et dynamics dans un cas particulier. En fait, je n’aurais pas utilisé C ++ 11, j’aurais utilisé des tableaux statiques. Mais je suis maintenant confus car il pourrait y avoir des avantages équivalents avec les deux.

Première solution:

template class Foo { private: int array[N]; public: // Some functions } 

Deuxième solution:

 template class Foo { private: int* array; public: // Some functions } 

Je ne peux pas arriver à choisir puisque les deux ont leurs propres avantages:

  • Les tableaux statiques sont plus rapides et nous ne nous soucions pas du tout de la gestion de la mémoire.
  • Les tableaux dynamics ne pèsent en rien tant que la mémoire n’est pas allouée. Après cela, ils sont moins pratiques à utiliser que les tableaux statiques. Mais depuis C ++ 11, nous pouvons tirer de grands avantages de la sémantique de déplacement, que nous ne pouvons pas utiliser avec des tableaux statiques.

Je ne pense pas qu’il existe une bonne solution, mais j’aimerais avoir un conseil ou simplement savoir ce que vous pensez de tout cela.

Je ne suis pas d’accord avec le “ça dépend”. N’utilisez jamais l’option 2. Si vous souhaitez utiliser une constante de temps de traduction, utilisez toujours l’option 1 ou std :: array. L’un des avantages énumérés ci-dessus, à savoir que les tableaux dynamics ne pèsent en rien jusqu’à leur affectation, est en réalité un désavantage énorme et horrible qu’il faut souligner avec une grande importance.

Ne jamais avoir d’objects qui ont plus d’une phase de construction . Plus jamais. Cela devrait être une règle mémorisée à travers un gros tatouage. Juste ne le fais jamais.

Lorsque vous avez des objects zombies qui ne sont pas encore tout à fait vivants, même s’ils ne sont pas tout à fait morts non plus, la gestion de leur durée de vie devient de plus en plus complexe. Vous devez vérifier dans chaque méthode si elle est totalement vivante ou si elle prétend seulement être vivante. La sécurité des exceptions nécessite des cas particuliers dans votre destructeur. Au lieu d’une simple construction et d’une destruction automatique, vous avez maintenant ajouté des exigences qui doivent être vérifiées à N endroits différents (# méthodes + dtor). Et le compilateur ne se soucie pas de vérifier. Et d’autres ingénieurs ne diffuseront pas cette exigence. Ils peuvent donc ajuster votre code de manière non sécurisée, en utilisant des variables sans vérification. Et maintenant, toutes ces méthodes ont de multiples comportements en fonction de l’état de l’object. Chaque utilisateur de l’object doit donc savoir à quoi s’attendre. Les zombies vont ruiner votre vie (codage) .

Au lieu de cela, si vous avez deux durées de vie naturelles différentes dans votre programme, utilisez deux objects différents. Mais cela signifie que vous avez deux états différents dans votre programme, vous devriez donc avoir une machine à états, un état n’ayant qu’un object et un autre état avec les deux, séparés par un événement asynchrone. S’il n’y a pas d’événement asynchrone entre les deux points, s’ils s’inscrivent tous dans une même étendue de fonctions, la séparation est alors artificielle et vous devriez effectuer une construction monophasée.

Le seul cas où une taille de temps de traduction devrait se traduire par une allocation dynamic est lorsque la taille est trop grande pour la stack. Cela amène ensuite à l’optimisation de la mémoire, et il convient de toujours l’évaluer à l’aide d’outils de profilage et de mémoire pour voir ce qui convient le mieux. L’option 2 ne sera jamais meilleure (elle utilise un pointeur nu – nous perdons donc à nouveau RAII ainsi que tout nettoyage et toute gestion automatiques, en ajoutant des invariants et en rendant le code plus complexe et facilement cassable par d’autres). Vecteur (comme suggéré par bitmask) serait la première chose à faire, même si vous n’aimerez peut-être pas les coûts d’allocation de tas à temps. D’autres options peuvent être un espace statique dans l’image de votre application. Mais encore une fois, ceux-ci ne devraient être pris en compte qu’une fois que vous avez déterminé que vous avez une contrainte de mémoire et que ce qu’il faut faire à partir de là devrait être déterminé par les besoins mesurables réels.

Utilisez ni. Il vaut mieux utiliser std::vector dans presque tous les cas. Dans les autres cas, cela dépend fortement de la raison pour laquelle std::vector serait insuffisant et ne peut donc pas être répondu de manière générale!

J’ai actuellement un problème pour décider lequel je devrais utiliser plus qu’un autre dans un cas particulier.

Vous devrez examiner vos options au cas par cas pour déterminer la solution optimale pour le contexte donné. Autrement dit, il est impossible de généraliser. Si un conteneur était idéal pour chaque scénario, l’autre serait obsolète.

Comme déjà mentionné, envisagez d’utiliser les implémentations std avant d’écrire la vôtre.

Plus de détails:

Longueur fixe

  • Faites attention à la quantité de stack que vous consumz.
  • Peut consumr plus de mémoire si vous le traitez comme un conteneur de taille dynamic.
  • Copies rapides.

Longueur variable

  • La réaffectation et le redimensionnement peuvent être coûteux.
  • Peut consumr plus de mémoire que nécessaire.
  • Mouvements rapides.

Le meilleur choix nécessite également que vous compreniez la complexité de la création, de la copie, de l’affectation, etc. des types d’élément.

Et si vous utilisez des implémentations std , rappelez-vous que les implémentations peuvent varier.

Enfin, vous pouvez créer un conteneur pour ces types qui résume les détails de l’implémentation et sélectionne dynamicment un membre de données approprié en fonction de la taille et du contexte – résumant les détails derrière une interface générale. Ceci est également utile parfois pour désactiver des fonctionnalités ou pour rendre certaines opérations (par exemple, des copies coûteuses) plus évidentes.

En bref, vous devez en savoir beaucoup sur les types et l’utilisation, et mesurer plusieurs aspects de votre programme pour déterminer le type de conteneur optimal pour un scénario spécifique.