C ++ a hérité des tableaux de C où ils sont utilisés pratiquement partout. C ++ fournit des abstractions plus faciles à utiliser et moins sujettes aux erreurs ( std::vector<T>
depuis C ++ 98 et std::array<T, n>
depuis C ++ 11 ), donc le besoin de tableaux ne se pose pas aussi souvent qu'en C. Cependant, lorsque vous lisez hérité code ou interagir avec une bibliothèque écrite en C, vous devez avoir une solide compréhension du fonctionnement des tableaux.
Cette FAQ est divisée en cinq parties:
- tableaux au niveau du type et accès aux éléments
- création et initialisation de tableaux
- affectation et passage de paramètres
- tableaux multidimensionnels et tableaux de pointeurs
- pièges courants lors de l'utilisation de tableaux
Si vous pensez que quelque chose d'important manque dans cette FAQ, écrivez une réponse et liez-la ici en tant que partie supplémentaire.
Dans le texte suivant, "tableau" signifie "tableau C", pas le modèle de classe std::array
. Une connaissance de base de la syntaxe du déclarant C est supposée. Notez que l'utilisation manuelle de new
et delete
comme illustré ci-dessous est extrêmement dangereuse face aux exceptions, mais c'est le sujet d' une autre FAQ .
(Remarque: Ceci est censé être une entrée de la FAQ C ++ de Stack Overflow . Si vous voulez critiquer l'idée de fournir une FAQ sous cette forme, alors la publication sur la méta qui a commencé tout cela serait l'endroit pour le faire. Réponses à cette question est surveillée dans le salon de discussion C ++ , où l'idée de FAQ a commencé en premier, donc votre réponse est très susceptible d'être lue par ceux qui ont eu l'idée.)
std::array
s,std::vector
s etgsl::span
s - je m'attendrais franchement à une FAQ sur la façon d'utiliser les tableaux en C ++ pour dire "maintenant, vous pouvez commencer à envisager de ne pas les utiliser."Réponses:
Tableaux au niveau du type
Un type de tableau est désigné par
T[n]
oùT
est le type d'élément etn
est une taille positive , le nombre d'éléments dans le tableau. Le type de tableau est un type de produit du type d'élément et de la taille. Si l'un ou les deux de ces ingrédients diffèrent, vous obtenez un type distinct:Notez que la taille fait partie du type, c'est-à-dire que les types de tableau de taille différente sont des types incompatibles qui n'ont absolument rien à voir les uns avec les autres.
sizeof(T[n])
est équivalent àn * sizeof(T)
.Décroissance de la matrice au pointeur
La seule "connexion" entre
T[n]
etT[m]
est que les deux types peuvent être implicitement convertis enT*
, et le résultat de cette conversion est un pointeur vers le premier élément du tableau. Autrement dit, partout où unT*
est requis, vous pouvez fournir unT[n]
, et le compilateur fournira silencieusement ce pointeur:Cette conversion est connue sous le nom de "désintégration de matrice à pointeur", et c'est une source majeure de confusion. La taille du tableau est perdue dans ce processus, car il ne fait plus partie du type (
T*
). Pro: Oublier la taille d'un tableau au niveau du type permet à un pointeur de pointer vers le premier élément d'un tableau de n'importe quelle taille. Con: étant donné un pointeur sur le premier (ou tout autre) élément d'un tableau, il n'y a aucun moyen de détecter la taille de ce tableau ou où exactement le pointeur pointe par rapport aux limites du tableau. Les pointeurs sont extrêmement stupides .Les tableaux ne sont pas des pointeurs
Le compilateur génère silencieusement un pointeur vers le premier élément d'un tableau chaque fois qu'il est jugé utile, c'est-à-dire chaque fois qu'une opération échoue sur un tableau mais réussit sur un pointeur. Cette conversion de tableau en pointeur est triviale, car la valeur du pointeur qui en résulte est simplement l'adresse du tableau. Notez que le pointeur n'est pas stocké en tant que partie du tableau lui-même (ou ailleurs dans la mémoire). Un tableau n'est pas un pointeur.
Un contexte important dans lequel un tableau ne se désintègre pas en un pointeur vers son premier élément est lorsque l'
&
opérateur lui est appliqué. Dans ce cas, l'&
opérateur renvoie un pointeur sur l' ensemble du tableau, pas seulement un pointeur sur son premier élément. Bien que dans ce cas, les valeurs (les adresses) soient les mêmes, un pointeur vers le premier élément d'un tableau et un pointeur vers le tableau entier sont des types complètement distincts:L'art ASCII suivant explique cette distinction:
Notez que le pointeur sur le premier élément ne pointe que sur un seul entier (représenté comme une petite boîte), tandis que le pointeur sur l'ensemble du tableau pointe sur un tableau de 8 entiers (représenté sur une grande boîte).
La même situation se produit dans les classes et est peut-être plus évidente. Un pointeur sur un objet et un pointeur sur son premier membre de données ont la même valeur (la même adresse), mais ce sont des types complètement distincts.
Si vous n'êtes pas familier avec la syntaxe du déclarant C, les parenthèses dans le type
int(*)[8]
sont essentielles:int(*)[8]
est un pointeur vers un tableau de 8 entiers.int*[8]
est un tableau de 8 pointeurs, chaque élément de typeint*
.Accéder aux éléments
C ++ fournit deux variantes syntaxiques pour accéder aux éléments individuels d'un tableau. Aucun d'eux n'est supérieur à l'autre, et vous devez vous familiariser avec les deux.
Arithmétique du pointeur
Étant donné un pointeur
p
sur le premier élément d'un tableau, l'expressionp+i
renvoie un pointeur sur le i-ème élément du tableau. En déréférençant ce pointeur par la suite, on peut accéder à des éléments individuels:Si
x
dénote un tableau , la décroissance du tableau vers le pointeur se déclenchera, car l'ajout d'un tableau et d'un entier n'a pas de sens (il n'y a pas d'opération plus sur les tableaux), mais l'ajout d'un pointeur et d'un entier est logique:(Notez que le pointeur généré implicitement n'a pas de nom, j'ai donc écrit
x+0
pour l'identifier.)Si, d'autre part,
x
désigne un pointeur sur le premier (ou tout autre) élément d'un tableau, la décroissance tableau sur pointeur n'est pas nécessaire, car le pointeur sur lequeli
on va ajouter existe déjà:Notez que dans le cas représenté,
x
est une variable de pointeur (visible par la petite case à côtéx
), mais elle pourrait tout aussi bien être le résultat d'une fonction renvoyant un pointeur (ou toute autre expression de typeT*
).Opérateur d'indexation
Étant donné que la syntaxe
*(x+i)
est un peu maladroite, C ++ fournit la syntaxe alternativex[i]
:Étant donné que l'addition est commutative, le code suivant fait exactement la même chose:
La définition de l'opérateur d'indexation conduit à l'équivalence intéressante suivante:
Cependant,
&x[0]
n'est généralement pas équivalent àx
. Le premier est un pointeur, le second un tableau. Ce n'est que lorsque le contexte déclenche la décroissance tableau vers pointeur peutx
et peut&x[0]
être utilisé de manière interchangeable. Par exemple:Sur la première ligne, le compilateur détecte une affectation d'un pointeur vers un pointeur, qui réussit de manière triviale. Sur la deuxième ligne, il détecte une affectation d'un tableau vers un pointeur. Étant donné que cela n'a pas de sens (mais pointeur affectation à pointeur a du sens), la décroissance tableau-à-pointeur se déclenche comme d'habitude.
Gammes
Un tableau de type
T[n]
a desn
éléments, indexés de0
àn-1
; il n'y a aucun élémentn
. Et pourtant, pour prendre en charge les plages semi-ouvertes (où le début est inclusif et la fin est exclusif ), C ++ permet le calcul d'un pointeur vers le n-ème élément (inexistant), mais il est illégal de déréférencer ce pointeur:Par exemple, si vous souhaitez trier un tableau, les deux éléments suivants fonctionnent également bien:
Notez qu'il est illégal de fournir
&x[n]
comme deuxième argument car il est équivalent à&*(x+n)
, et la sous-expression*(x+n)
invoque techniquement un comportement non défini en C ++ (mais pas en C99).Notez également que vous pouvez simplement fournir
x
comme premier argument. C'est un peu trop laconique à mon goût, et cela rend également la déduction des arguments de modèle un peu plus difficile pour le compilateur, car dans ce cas, le premier argument est un tableau mais le deuxième argument est un pointeur. (Encore une fois, la décroissance du tableau vers le pointeur entre en jeu.)la source
Les programmeurs confondent souvent des tableaux multidimensionnels avec des tableaux de pointeurs.
Tableaux multidimensionnels
La plupart des programmeurs connaissent les tableaux multidimensionnels nommés, mais beaucoup ne savent pas que les tableaux multidimensionnels peuvent également être créés de manière anonyme. Les tableaux multidimensionnels sont souvent appelés «tableaux de tableaux» ou « vrais tableaux multidimensionnels».
Tableaux multidimensionnels nommés
Lorsque vous utilisez des tableaux multidimensionnels nommés, toutes les dimensions doivent être connues au moment de la compilation:
Voici à quoi ressemble un tableau multidimensionnel nommé en mémoire:
Notez que les grilles 2D telles que celles ci-dessus ne sont que des visualisations utiles. Du point de vue du C ++, la mémoire est une séquence "plate" d'octets. Les éléments d'un tableau multidimensionnel sont stockés dans l'ordre des lignes principales. Autrement dit,
connect_four[0][6]
etconnect_four[1][0]
sont voisins dans la mémoire. En fait,connect_four[0][7]
etconnect_four[1][0]
dénotons le même élément! Cela signifie que vous pouvez prendre des tableaux multidimensionnels et les traiter comme de grands tableaux unidimensionnels:Tableaux multidimensionnels anonymes
Avec les tableaux multidimensionnels anonymes, toutes les dimensions sauf la première doivent être connues au moment de la compilation:
Voici à quoi ressemble un tableau multidimensionnel anonyme en mémoire:
Notez que le tableau lui-même est toujours alloué comme un seul bloc en mémoire.
Tableaux de pointeurs
Vous pouvez surmonter la restriction de largeur fixe en introduisant un autre niveau d'indirection.
Tableaux nommés de pointeurs
Voici un tableau nommé de cinq pointeurs qui sont initialisés avec des tableaux anonymes de différentes longueurs:
Et voici à quoi cela ressemble en mémoire:
Étant donné que chaque ligne est désormais allouée individuellement, l'affichage des tableaux 2D en tant que tableaux 1D ne fonctionne plus.
Tableaux anonymes de pointeurs
Voici un tableau anonyme de 5 pointeurs (ou tout autre nombre de) qui sont initialisés avec des tableaux anonymes de différentes longueurs:
Et voici à quoi cela ressemble en mémoire:
Conversions
La décomposition de tableau à pointeur s'étend naturellement aux tableaux de tableaux et aux tableaux de pointeurs:
Cependant, il n'y a pas de conversion implicite de
T[h][w]
àT**
. Si une telle conversion implicite existait, le résultat serait un pointeur vers le premier élément d'un tableau deh
pointeurs versT
(chacun pointant vers le premier élément d'une ligne du tableau 2D d'origine), mais ce tableau de pointeurs n'existe nulle part dans mémoire encore. Si vous souhaitez une telle conversion, vous devez créer et remplir manuellement le tableau de pointeurs requis:Notez que cela génère une vue du tableau multidimensionnel d'origine. Si vous avez plutôt besoin d'une copie, vous devez créer des tableaux supplémentaires et copier les données vous-même:
la source
int connect_four[H][7];
,int connect_four[6][W];
int connect_four[H][W];
ainsi queint (*p)[W] = new int[6][W];
etint (*p)[W] = new int[H][W];
sont des déclarations valides, quandH
etW
sont connus au moment de la compilation.Affectation
Pour aucune raison particulière, les tableaux ne peuvent pas être assignés les uns aux autres. Utilisez
std::copy
plutôt:C'est plus flexible que ce que pourrait fournir une véritable affectation de tableau car il est possible de copier des tranches de tableaux plus grands dans des tableaux plus petits.
std::copy
est généralement spécialisé pour les types primitifs pour donner des performances maximales. Il est peu probable questd::memcpy
performances meilleures. En cas de doute, mesurez.Bien que vous ne puissiez pas attribuer directement des tableaux, vous pouvez affecter des structures et des classes qui contiennent des membres de tableau. En effet , les membres du tableau sont copiés par membre par l'opérateur d'affectation qui est fourni par défaut par le compilateur. Si vous définissez manuellement l'opérateur d'affectation pour vos propres types de structure ou de classe, vous devez recourir à la copie manuelle pour les membres du tableau.
Passage de paramètres
Les tableaux ne peuvent pas être transmis par valeur. Vous pouvez les passer par pointeur ou par référence.
Passer par le pointeur
Comme les tableaux eux-mêmes ne peuvent pas être transmis par valeur, un pointeur vers leur premier élément est généralement transmis par valeur à la place. Ceci est souvent appelé "passer par le pointeur". Étant donné que la taille du tableau n'est pas récupérable via ce pointeur, vous devez passer un deuxième paramètre indiquant la taille du tableau (la solution C classique) ou un deuxième pointeur pointant après le dernier élément du tableau (la solution d'itérateur C ++) :
Comme alternative syntaxique, vous pouvez également déclarer les paramètres en tant que
T p[]
, et cela signifie exactement la même chose queT* p
dans le contexte des listes de paramètres uniquement :Vous pouvez penser que le compilateur de réécriture
T p[]
pourT *p
dans le contexte des listes de paramètres uniquement . Cette règle spéciale est en partie responsable de toute la confusion concernant les tableaux et les pointeurs. Dans tous les autres contextes, déclarer quelque chose comme un tableau ou comme un pointeur fait un énorme différence.Malheureusement, vous pouvez également fournir une taille dans un paramètre de tableau qui est silencieusement ignoré par le compilateur. Autrement dit, les trois signatures suivantes sont exactement équivalentes, comme l'indiquent les erreurs du compilateur:
Passer par référence
Les tableaux peuvent également être transmis par référence:
Dans ce cas, la taille du tableau est importante. Étant donné que l'écriture d'une fonction qui n'accepte que des tableaux d'exactement 8 éléments est de peu d'utilité, les programmeurs écrivent généralement des fonctions telles que des modèles:
Notez que vous ne pouvez appeler un tel modèle de fonction qu'avec un tableau réel d'entiers, pas avec un pointeur sur un entier. La taille du tableau est automatiquement déduite et pour chaque taille
n
, une fonction différente est instanciée à partir du modèle. Vous pouvez également écrire des modèles de fonctions très utiles qui résument à la fois le type d'élément et la taille.la source
void foo(int a[3])
a
que l'on passe le tableau par valeur, la modification à l'a
intérieur defoo
modifiera le tableau d'origine. Cela devrait être clair car les tableaux ne peuvent pas être copiés, mais cela peut valoir la peine de le renforcer.ranges::copy(a, b)
int sum( int size_, int a[size_]);
- à partir de (je pense) C995. Pièges courants lors de l'utilisation de tableaux.
5.1 Piège: Faire confiance aux liens de type non sûrs.
OK, on vous a dit, ou vous avez découvert vous-même, que les globaux (variables de portée d'espace de noms accessibles en dehors de l'unité de traduction) sont Evil ™. Mais saviez-vous à quel point ils sont vraiment mauvais ™? Considérez le programme ci-dessous, composé de deux fichiers [main.cpp] et [numbers.cpp]:
Dans Windows 7, cela se compile et se lie très bien avec MinGW g ++ 4.4.1 et Visual C ++ 10.0.
Étant donné que les types ne correspondent pas, le programme se bloque lorsque vous l'exécutez.
Explication formelle: le programme a un comportement indéfini (UB), et au lieu de se bloquer, il peut donc simplement se bloquer, ou peut-être ne rien faire, ou il peut envoyer des e-mails menaçants aux présidents des États-Unis, de Russie, d'Inde, Chine et Suisse, et faites voler les démons nasaux hors de votre nez.
Explication pratique: dans
main.cpp
le tableau est traité comme un pointeur, placé à la même adresse que le tableau. Pour un exécutable 32 bits, cela signifie que la premièreint
valeur du tableau est traitée comme un pointeur. -À- dire, dansmain.cpp
lanumbers
variable contient ou semble contenir,(int*)1
. Cela oblige le programme à accéder à la mémoire tout en bas de l'espace d'adressage, qui est classiquement réservé et à l'origine de pièges. Résultat: vous obtenez un crash.Les compilateurs ont pleinement le droit de ne pas diagnostiquer cette erreur, car C ++ 11 §3.5 / 10 dit, à propos de l'exigence de types compatibles pour les déclarations,
Le même paragraphe détaille la variation autorisée:
Cette variation autorisée n'inclut pas la déclaration d'un nom en tant que tableau dans une unité de traduction et en tant que pointeur dans une autre unité de traduction.
5.2 Piège: faire une optimisation prématurée (
memset
et amis).Pas encore écrit
5.3 Piège: utiliser l'idiome C pour obtenir le nombre d'éléments.
Avec une profonde expérience en C, il est naturel d'écrire…
Puisqu'un
array
désintègre pour pointer vers le premier élément si nécessaire, l'expressionsizeof(a)/sizeof(a[0])
peut également être écrite commesizeof(a)/sizeof(*a)
. Cela signifie la même chose, et peu importe comment il est écrit, c'est l' idiome C pour trouver les éléments numériques du tableau.Écueil principal: l'idiome C n'est pas de type sécurisé. Par exemple, le code…
passe un pointeur vers
N_ITEMS
, et par conséquent produit très probablement un mauvais résultat. Compilé comme un exécutable 32 bits dans Windows 7, il produit…int const a[7]
justeint const a[]
.int const a[]
dansint const* a
.N_ITEMS
est donc invoqué avec un pointeur.sizeof(array)
(taille d'un pointeur) vaut alors 4.sizeof(*array)
est équivalent àsizeof(int)
, qui pour un exécutable 32 bits est également 4.Afin de détecter cette erreur au moment de l'exécution, vous pouvez le faire…
La détection des erreurs d'exécution est meilleure que l'absence de détection, mais elle gaspille un peu de temps processeur et peut-être beaucoup plus de temps programmeur. Mieux avec la détection au moment de la compilation! Et si vous êtes heureux de ne pas prendre en charge les tableaux de types locaux avec C ++ 98, vous pouvez le faire:
En compilant cette définition substituée dans le premier programme complet, avec g ++, j'ai eu…
Comment cela fonctionne: le tableau est transmis par référence à
n_items
, et donc il ne se désintègre pas pour pointer vers le premier élément, et la fonction peut simplement renvoyer le nombre d'éléments spécifié par le type.Avec C ++ 11, vous pouvez également l'utiliser pour les tableaux de type local, et c'est l' idiome C ++ de type sécurisé pour trouver le nombre d'éléments d'un tableau.
5.4 Piège C ++ 11 et C ++ 14: Utilisation d'une
constexpr
fonction de taille de tableau.Avec C ++ 11 et versions ultérieures, c'est naturel, mais comme vous le verrez dangereux !, pour remplacer la fonction C ++ 03
avec
où le changement significatif est l'utilisation de
constexpr
, qui permet à cette fonction de produire une constante de temps de compilation .Par exemple, contrairement à la fonction C ++ 03, une telle constante de temps de compilation peut être utilisée pour déclarer un tableau de la même taille qu'un autre:
Mais considérez ce code en utilisant la
constexpr
version:Le piège: à partir de juillet 2015, ce qui précède se compile avec MinGW-64 5.1.0 avec
C ++ 11 C ++ 14 5,19 $ / 2 neuf e tiret-pedantic-errors
, et, teste avec les compilateurs en ligne à gcc.godbolt.org/ , également avec clang 3.0 et clang 3.2, mais pas avec clang 3.3, 3.4. 1, 3.5.0, 3.5.1, 3.6 (rc1) ou 3.7 (expérimental). Et important pour la plate-forme Windows, il ne compile pas avec Visual C ++ 2015. La raison en est une instruction C ++ 11 / C ++ 14 sur l'utilisation des références dansconstexpr
expressions:On peut toujours écrire le plus verbeux
… Mais cela échoue quand
Collection
ne s'agit pas d'un tableau brut.Pour traiter des collections qui peuvent être des non-tableaux, il faut la surcharge d'une
n_items
fonction, mais aussi, pour le temps de compilation, il faut une représentation du temps de compilation de la taille du tableau. Et la solution C ++ 03 classique, qui fonctionne bien également en C ++ 11 et C ++ 14, consiste à laisser la fonction rapporter son résultat non pas comme une valeur mais via son type de résultat de fonction . Par exemple, comme ceci:A propos du choix du type de retour pour
static_n_items
: ce code n'utilise passtd::integral_constant
car avecstd::integral_constant
le résultat est représenté directement comme uneconstexpr
valeur, réintroduisant le problème d'origine. Au lieu d'uneSize_carrier
classe, on peut laisser la fonction retourner directement une référence à un tableau. Cependant, tout le monde ne connaît pas cette syntaxe.À propos de la dénomination: une partie de cette solution
constexpr
-invalid-due-to-reference est de rendre explicite le choix de la constante de temps de compilation.Espérons que le problème oops-there-was-a-reference-impliqué-in-your-
constexpr
sera corrigé avec C ++ 17, mais jusque-là, une macro comme celle-STATIC_N_ITEMS
ci donne la portabilité, par exemple aux compilateurs clang et Visual C ++, en conservant le type sécurité.Connexes: les macros ne respectent pas les étendues, donc pour éviter les collisions de noms, il peut être judicieux d'utiliser un préfixe de nom, par exemple
MYLIB_STATIC_N_ITEMS
.la source
Segmentation fault
... J'ai enfin trouvé / compris après avoir lu vos explications! Veuillez écrire votre section §5.2 :-) Cheerssize_t
qui concerne , cela n'a aucun avantage que je connaisse pour les plates-formes modernes, mais il a un certain nombre de problèmes en raison des règles de conversion de type implicites de C et C ++. Autrement dit,ptrdiff_t
est utilisé très intentionnellement, pour éviter les problèmes avecsize_t
. Il faut cependant être conscient que g ++ a un problème avec la correspondance de la taille du tableau au paramètre de modèle à moins que ce ne soitsize_t
(je ne pense pas que ce problème spécifique au compilateur avec non-size_t
soit important, mais YMMV).size_t
pour désigner la taille des tableaux, non bien sûr que non.Création et initialisation de tableaux
Comme avec tout autre type d'objet C ++, les tableaux peuvent être stockés soit directement dans des variables nommées (alors la taille doit être une constante au moment de la compilation; C ++ ne prend pas en charge les VLA ), ou ils peuvent être stockés de manière anonyme sur le tas et accessibles indirectement via pointeurs (ce n'est qu'alors que la taille peut être calculée au moment de l'exécution).
Tableaux automatiques
Les tableaux automatiques (tableaux vivant "sur la pile") sont créés à chaque fois que le flux de contrôle passe par la définition d'une variable de tableau local non statique:
L'initialisation est effectuée par ordre croissant. Notez que les valeurs initiales dépendent du type d'élément
T
:T
est un POD (commeint
dans l'exemple ci-dessus), aucune initialisation n'a lieu.T
initialise tous les éléments.T
ne fournit aucun constructeur par défaut accessible, le programme ne compile pas.Alternativement, les valeurs initiales peuvent être spécifiées explicitement dans l' initialiseur de tableau , une liste séparée par des virgules entourée de crochets:
Étant donné que dans ce cas, le nombre d'éléments dans l'initialiseur de tableau est égal à la taille du tableau, la spécification manuelle de la taille est redondante. Il peut être déduit automatiquement par le compilateur:
Il est également possible de spécifier la taille et de fournir un initialiseur de tableau plus court:
Dans ce cas, les éléments restants sont initialisés à zéro . Notez que C ++ autorise un initialiseur de tableau vide (tous les éléments sont initialisés à zéro), contrairement à C89 (au moins une valeur est requise). Notez également que les initialiseurs de tableau ne peuvent être utilisés que pour initialiser tableaux; ils ne pourront plus être utilisés ultérieurement dans les travaux.
Tableaux statiques
Les tableaux statiques (tableaux vivant "dans le segment de données") sont des variables de tableau locales définies avec le
static
mot clé et les variables de tableau à la portée de l'espace de noms ("variables globales"):(Notez que les variables à la portée de l'espace de noms sont implicitement statiques. L'ajout du
static
mot - clé à leur définition a une signification complètement différente et obsolète .)Voici comment les tableaux statiques se comportent différemment des tableaux automatiques:
(Rien de ce qui précède n'est spécifique aux tableaux. Ces règles s'appliquent également aux autres types d'objets statiques.)
Membres de données de tableau
Les membres de données de tableau sont créés lorsque leur propre objet est créé. Malheureusement, C ++ 03 ne fournit aucun moyen d'initialiser les tableaux dans la liste d'initialisation des membres , donc l'initialisation doit être simulée avec des affectations:
Alternativement, vous pouvez définir un tableau automatique dans le corps du constructeur et copier les éléments sur:
En C ++ 0x, les tableaux peuvent être initialisés dans la liste des initialiseurs de membres grâce à une initialisation uniforme :
C'est la seule solution qui fonctionne avec les types d'éléments qui n'ont pas de constructeur par défaut.
Tableaux dynamiques
Les tableaux dynamiques n'ont pas de nom, donc le seul moyen d'y accéder est via des pointeurs. Parce qu'ils n'ont pas de noms, je les désignerai désormais comme des "tableaux anonymes".
En C, des tableaux anonymes sont créés via
malloc
et amis. En C ++, les tableaux anonymes sont créés à l'aide de lanew T[size]
syntaxe qui renvoie un pointeur sur le premier élément d'un tableau anonyme:L'illustration ASCII suivante illustre la disposition de la mémoire si la taille est calculée comme 8 au moment de l'exécution:
De toute évidence, les tableaux anonymes nécessitent plus de mémoire que les tableaux nommés en raison du pointeur supplémentaire qui doit être stocké séparément. (Il y a aussi des frais généraux supplémentaires sur la boutique gratuite.)
Notez qu'il n'y a pas de décroissance tableau vers pointeur en cours ici. Bien que l'évaluation
new int[size]
crée en fait un tableau d'entiers, le résultat de l'expressionnew int[size]
est déjà un pointeur vers un seul entier (le premier élément), pas un tableau d'entiers ou un pointeur vers un tableau d'entiers de taille inconnue. Cela serait impossible, car le système de type statique requiert que les tailles de tableau soient des constantes au moment de la compilation. (Par conséquent, je n'ai pas annoté le tableau anonyme avec des informations de type statique dans l'image.)Concernant les valeurs par défaut des éléments, les tableaux anonymes se comportent de manière similaire aux tableaux automatiques. Normalement, les tableaux POD anonymes ne sont pas initialisés, mais il existe une syntaxe spéciale qui déclenche l'initialisation de la valeur:
(Notez la paire de parenthèses de fin juste avant le point-virgule.) Encore une fois, C ++ 0x simplifie les règles et permet de spécifier les valeurs initiales des tableaux anonymes grâce à une initialisation uniforme:
Si vous avez terminé d'utiliser un tableau anonyme, vous devez le renvoyer au système:
Vous devez libérer chaque baie anonyme exactement une fois, puis ne plus la toucher par la suite. Ne pas le libérer du tout entraîne une fuite de mémoire (ou plus généralement, selon le type d'élément, une fuite de ressource), et essayer de le libérer plusieurs fois entraîne un comportement indéfini. L'utilisation du formulaire non-tableau
delete
(oufree
) au lieu dedelete[]
pour libérer le tableau est également un comportement non défini .la source
static
utilisation dans la portée de l'espace de noms a été supprimée dans C ++ 11.new
est un opérateur, il pourrait certainement renvoyer le tableau alloué par référence. Ça ne sert à rien ...new
est beaucoup plus ancien que les références.int a[10]; int (&r)[] = a;