Les tableaux doivent-ils être utilisés en C ++?

95

Depuis std::listet std::vectorexistent, y a-t-il une raison d'utiliser des tableaux C traditionnels en C ++, ou devraient-ils être évités, tout comme malloc?

Andreas
la source
18
@Als: Cette question concerne la différence entre deux conteneurs spécifiques, alors que cette question concerne la différence entre les tableaux bruts et les conteneurs standard en général.
Jon Purdy

Réponses:

109

Dans C ++ 11 où std::arrayest disponible, la réponse est "oui, les tableaux doivent être évités". Avant C ++ 11, vous devrez peut-être utiliser des tableaux C pour allouer des tableaux dans le stockage automatique (c'est-à-dire sur la pile).

dasblinkenlight
la source
3
Cependant, de nombreux compilateurs n'ont toujours pas de support C ++ 11. Vous devrez décider quand il est préférable d'utiliser l'un plutôt que l'autre étant donné le manque de std :: array
Nowayz
4
std :: array est un modèle qui a un impact sur les grands projets en termes de temps de construction et éventuellement de taille de code puisque pour chaque combinaison de T, N le modèle est instancié à nouveau.
zvrba
std :: vector garantit l'alignement des données par standard, donc il peut être utilisé presque partout. Avec C ++ 11, il n'y a vraiment aucune raison d'utiliser des tableaux C.
Nils
16
Les tableaux @Nils garantissent également l'alignement. De plus, l'allocation de stockage automatique («la pile») est bien plus rapide que l'allocation de stockage dynamique. Si je sais que j'ai exactement 3 éléments [par exemple, les coordonnées d'un triangle], il n'y a aucune raison d'utiliser vector.
zvrba
9
@zvrba - Vérifiez l'assembly généré lors de l'utilisation de std :: array par rapport aux tableaux C. Aucune différence.
Nemanja Trifunovic
85

Certainement, bien qu'avec std::arrayC ++ 11, pratiquement uniquement pour les données statiques. Les tableaux de style C présentent trois avantages importants par rapport à std::vector:

  • Ils ne nécessitent pas d'allocation dynamique. Pour cette raison, les tableaux de style C doivent être préférés là où vous êtes susceptible d'avoir beaucoup de très petits tableaux. Dites quelque chose comme un point à n dimensions:

    template <typename T, int dims>
    class Point
    {
        T myData[dims];
    // ...
    };
    

    Typiquement, on pourrait imaginer un qui dimssera très petit (2 ou 3), Tun type intégré ( double), et que vous pourriez vous retrouver avec std::vector<Point>des millions d'éléments. Vous ne voulez certainement pas des millions d'allocations dynamiques de 3 doubles.

  • L'initialisation statique de support. Ce n'est un problème que pour les données statiques, où quelque chose comme:

    struct Data { int i; char const* s; };
    Data const ourData[] =
    {
        { 1, "one" },
        { 2, "two" },
        //  ...
    };
    

    Ceci est souvent préférable à l'utilisation d'un vecteur (et std::string), car cela évite tous les problèmes d'ordre d'initialisation; les données sont préchargées avant qu'un code réel puisse être exécuté.

  • Enfin, lié à ce qui précède, le compilateur peut calculer la taille réelle du tableau à partir des initialiseurs. Vous n'êtes pas obligé de les compter.

Si vous avez accès à C ++ 11, std::arrayrésout les deux premiers problèmes et devrait certainement être utilisé de préférence aux tableaux de style C dans le premier cas. Cependant, cela n'aborde pas le troisième et le fait que le compilateur dimensionne le tableau en fonction du nombre d'initialiseurs est toujours une raison valable pour préférer les tableaux de style C.

James Kanze
la source
11
L'initialisation du tableau de style C élimine également le besoin de vous répéter. int i[] = { 1, 2, 3 };continue de travailler avec int i[] = { 1, 2, 3, 4 };. array<int, 3>doit être changé manuellement en array<int, 4>.
10
@JoeWreschnig Un changement que vous pouvez facilement oublier. Si vous ajoutez un élément, le compilateur devrait se plaindre, mais si vous en supprimez un, vous vous retrouverez avec un élément supplémentaire 0 initialisé à la fin. J'utilise encore beaucoup les tableaux de style C pour ce type de données statiques.
James Kanze
3
La première phrase n'a pas de sens.
Konrad Rudolph
4
Le troisième point peut être résolu assez élégamment en utilisant une make_arrayfonction , similaire à make_pairetc. Pointe du chapeau à @R. Martinho Fernandes .
Konrad Rudolph
@KonradRudolph: Bien sûr que oui. Andreas demande «Les tableaux doivent-ils être utilisés en C ++?», Ce à quoi James répond «Certainement, bien qu'avec std::arrayen C ++ 11, [ils devraient être utilisés] pratiquement uniquement pour les données statiques».
Jon Purdy
15

Ne dites jamais «jamais», mais je conviens que leur rôle est grandement diminué par les véritables structures de données de STL.

Je dirais également que l'encapsulation à l'intérieur des objets devrait minimiser l'impact de choix comme celui-ci. Si le tableau est un membre de données privé, vous pouvez l'échanger vers l'intérieur ou l'extérieur sans affecter les clients de votre classe.

duffymo
la source
11

J'ai travaillé sur des systèmes critiques de sécurité où vous ne pouvez pas utiliser l'allocation de mémoire dynamique. La mémoire doit toujours être sur la pile. Par conséquent, dans ce cas, vous utiliserez des tableaux car la taille est fixée au moment de la compilation.

Ed Heal
la source
8
Avant C ++ 11, j'aurais accepté, mais std::array<T>alloue sur les piles et n'a fondamentalement aucune surcharge sur un tableau brut.
111111
5
@ 111111 - D'accord. Mais je sais que certaines personnes dans ce secteur n'ont pas encore migré vers C ++ 11
Ed Heal
Je sais que c'est la raison pour laquelle je ne vous ai pas voté contre, mais je pense que Boost avait une version et il est facile de créer la vôtre également.
111111
6
mais dans les systèmes critiques pour la sécurité, vous n'utilisez pas les nouvelles fonctionnalités du compilateur (moins testées) et vous n'utilisez pas boost.
James Kanze
3
De nombreux systèmes critiques pour la sécurité sont construits sur des compilateurs ANCIENS qui n'ont même pas les nouvelles fonctionnalités du compilateur, car le changement de chaînes d'outils est un processus lent et coûteux qui nécessite des tonnes de paperasse, de tests et de certifications.
Brian McFarland
6

arrayin c++vous donne une alternative rapide de taille fixe de taille dynamique std::vectoret std::list. std :: array est l'un des ajouts dans c++11. Il offre l'avantage des conteneurs std tout en fournissant la sémantique de type agrégé des tableaux de style C.

Donc , c++11j'utiliserais certainement std::array, là où c'est nécessaire, sur le vecteur. Mais j'éviterais les tableaux de style C dans C++03.

Vikas
la source
4

Le plus souvent, non , je ne peux pas penser à une raison d'utiliser des tableaux bruts, par exemple vectors. Si le code est nouveau .

Vous devrez peut-être recourir à des tableaux si vos bibliothèques doivent être compatibles avec du code qui attend des tableaux et des pointeurs bruts.

Luchian Grigore
la source
1
... mais depuis C ++ 03 un vecteur "a" en fait un tableau auquel vous pouvez accéder par pointeur pour lire ou écrire. Cela couvre la plupart des cas de code qui attend des pointeurs vers des tableaux. Ce n'est vraiment que lorsque ce code alloue ou libère le tableau que vous ne pouvez pas utiliser de vecteur.
Steve Jessop
@SteveJessop pouvez-vous accéder au tableau interne?
Luchian Grigore
1
@LuchianGrigore: vector.data()en C ++ 11 ou &vector.front()antérieur.
Mike Seymour
@Luchian: à condition que le vecteur ne soit pas vide, vous pouvez prendre un pointeur vers un élément (et s'il est vide, vous pouvez passer un pointeur nul et une longueur de 0 à toute fonction raisonnablement écrite qui accepte la casse d'arête d'un tampon de taille zéro). À peu près le seul but de la garantie de contiguïté vectorielle ajoutée dans C ++ 03 était de permettre aux vecteurs d'être utilisés comme tampons par un code orienté pointeur.
Steve Jessop
1
@SteveJessop Et le fait que beaucoup de gens pensaient que c'était garanti de toute façon, et il était jugé préférable de ne pas les décevoir.
James Kanze
4

Je sais que beaucoup de gens indiquent std :: array pour allouer des tableaux sur la pile, et std :: vector pour le tas. Mais aucun ne semble prendre en charge l'alignement non natif. Si vous faites n'importe quel type de code numérique sur lequel vous souhaitez utiliser les instructions SSE ou VPX (nécessitant ainsi respectivement un alignement de 128 ou 256 octets), les tableaux C semblent toujours être votre meilleur pari.

gct
la source
3

Je dirais que les tableaux sont toujours utiles, si vous stockez une petite quantité statique de données, pourquoi pas.

james82345
la source
2

Le seul avantage d'un tableau (bien sûr enveloppé dans quelque chose qui gérera automatiquement sa désallocation en cas de besoin) sur lequel std::vectorje peux penser est qu'il vectorne peut pas transmettre la propriété de ses données, à moins que votre compilateur ne prenne en charge C ++ 11 et déplace les constructeurs.

Tadeusz Kopec
la source
6
"vector ne peut pas transmettre la propriété de ses données" - oui, il peut, en C ++ 03, utiliser swap.
Steve Jessop
2

Les tableaux de style C sont une structure de données fondamentale, il y aura donc des cas où il est préférable de les utiliser. Pour le cas général, cependant, utilisez les structures de données plus avancées qui arrondissent les coins des données sous-jacentes. C ++ vous permet de faire des choses très intéressantes et utiles avec la mémoire, dont beaucoup fonctionnent avec des tableaux simples.

James Wynn
la source
3
En quoi les tableaux de style C sont-ils plus fondamentaux que std::arrays? Les deux seront dans de nombreux cas compilés dans le même assembly.
gauche vers
1
Plus fondamental en ce qu'il est plus basique. Vous savez ce qu'un tableau va faire, std :: array peut avoir des bizarreries d'implémentation car il repose sur la bibliothèque standard.
James Wynn
1
@JamesWynn Pas vraiment. std::arraya une sémantique définie avec précision construite sur des tableaux statiques.
Konrad Rudolph
1

Vous devez utiliser des conteneurs STL en interne, mais vous ne devez pas passer de pointeurs vers de tels conteneurs entre différents modules, sinon vous vous retrouverez dans l'enfer des dépendances. Exemple:

std::string foo;
//  fill foo with stuff
myExternalOutputProc(foo.c_str());

est une très bonne solution mais pas

std::string foo;
//  fill foo with stuff
myExternalOutputProc(&foo);

La raison en est que std :: string peut être implémenté de différentes manières, mais une chaîne de style C est toujours une chaîne de style C.

user877329
la source
Je pense que ce que vous essayez de dire est: ne liez pas différents codes objet ensemble si différents compilateurs / implémentations de la bibliothèque standard ont été utilisés pour les créer. C'est certainement vrai. Comment cela se rapporte-t-il à la question initiale?
jogojapan
Il est juste un conseil quand utiliser les tableaux ou conteneurs STL. Créez des données à l'aide d'un conteneur, transmettez-les sous forme de tableau. Pour d'autres données que des chaînes, vous auriez quelque chose comme myExternalOutputProc (foo.rawPointerGet (), foo.count ());
user877329
Mais ces problèmes ne surviennent que lorsque vous combinez différentes implémentations de la bibliothèque standard dans le même projet. C'est fou. Dans n'importe quel morceau de code normal, il est parfaitement acceptable de passer, disons, un vecteur, par référence (ou, en C ++ 11, de le déplacer) à une fonction.
jogojapan
1
Il se trouve que j'aime les plug-ins
user877329