En C ++ 11, vous pouvez utiliser un basé sur une plage for
, qui agit comme le foreach
d'autres langages. Cela fonctionne même avec des tableaux C simples:
int numbers[] = { 1, 2, 3, 4, 5 };
for (int& n : numbers) {
n *= 2;
}
Comment sait-il quand s'arrêter? Cela fonctionne-t-il uniquement avec les tableaux statiques qui ont été déclarés dans la même étendue que celle for
utilisée? Comment utiliseriez-vous cela for
avec des tableaux dynamiques?
for
. Mais au moment où ce tableau se désintègre en un pointeur, les informations de taille sont perdues.numbers
estsizeof(numbers)/sizeof(int)
, par exemple.Réponses:
Cela fonctionne pour toute expression dont le type est un tableau. Par exemple:
int (*arraypointer)[4] = new int[1][4]{{1, 2, 3, 4}}; for(int &n : *arraypointer) n *= 2; delete [] arraypointer;
Pour une explication plus détaillée, si le type de l'expression passée à droite de
:
est un type tableau, la boucle itère deptr
àptr + size
(ptr
pointant vers le premier élément du tableau,size
étant le nombre d'éléments du tableau).Cela contraste avec les types définis par l'utilisateur, qui fonctionnent en recherchant
begin
et enend
tant que membres si vous passez un objet de classe ou (s'il n'y a pas de membres appelés de cette façon) des fonctions non membres. Ces fonctions donneront les itérateurs de début et de fin (pointant directement après le dernier élément et le début de la séquence respectivement).Cette question clarifie pourquoi cette différence existe.
la source
begin
`end. It just happens that
std :: begin`std::end
use les fonctions membres, et sera utilisé si une meilleure correspondance n'est pas disponible.Je pense que la partie la plus importante de cette question est de savoir comment C ++ sait quelle est la taille d'un tableau (au moins je voulais le savoir quand j'ai trouvé cette question).
C ++ connaît la taille d'un tableau, car il fait partie de la définition du tableau - c'est le type de la variable. Un compilateur doit connaître le type.
Puisque C ++ 11
std::extent
peut être utilisé pour obtenir la taille d'un tableau:int size1{ std::extent< char[5] >::value }; std::cout << "Array size: " << size1 << std::endl;
Bien sûr, cela n'a pas beaucoup de sens, car vous devez fournir explicitement la taille dans la première ligne, que vous obtenez ensuite dans la deuxième ligne. Mais vous pouvez également utiliser
decltype
et cela devient plus intéressant:char v[] { 'A', 'B', 'C', 'D' }; int size2{ std::extent< decltype(v) >::value }; std::cout << "Array size: " << size2 << std::endl;
la source
Selon le dernier projet de travail C ++ (n3376), l'instruction range for est équivalente à ce qui suit:
{ auto && __range = range-init; for (auto __begin = begin-expr, __end = end-expr; __begin != __end; ++__begin) { for-range-declaration = *__begin; statement } }
Donc, il sait comment arrêter de la même manière un régulier
for
boucle utilisant des itérateurs.Je pense que vous cherchez peut-être quelque chose comme ce qui suit pour fournir un moyen d'utiliser la syntaxe ci-dessus avec des tableaux qui se composent uniquement d'un pointeur et d'une taille (tableaux dynamiques):
template <typename T> class Range { public: Range(T* collection, size_t size) : mCollection(collection), mSize(size) { } T* begin() { return &mCollection[0]; } T* end () { return &mCollection[mSize]; } private: T* mCollection; size_t mSize; };
Ce modèle de classe peut alors être utilisé pour créer une gamme, sur laquelle vous pouvez itérer en utilisant la nouvelle distance pour la syntaxe. J'utilise ceci pour parcourir tous les objets d'animation dans une scène qui est importée à l'aide d'une bibliothèque qui ne renvoie qu'un pointeur vers un tableau et une taille en tant que valeurs séparées.
for ( auto pAnimation : Range<aiAnimation*>(pScene->mAnimations, pScene->mNumAnimations) ) { // Do something with each pAnimation instance here }
Cette syntaxe est, à mon avis, beaucoup plus claire que ce que vous obtiendriez en utilisant
std::for_each
unefor
boucle simple .la source
Il sait quand s'arrêter car il connaît les limites des tableaux statiques.
Je ne sais pas ce que vous entendez par "tableaux dynamiques", dans tous les cas, si ce n'est pas itérer sur des tableaux statiques, de manière informelle, le compilateur recherche les noms
begin
etend
dans la portée de la classe de l'objet sur lequel vous itérez, ou regarde pourbegin(range)
etend(range)
les utiliser recherche et les utilisations en fonction de l' argument comme itérateurs.Pour plus d'informations, dans la norme C ++ 11 (ou son projet public), "6.5.4 L'
for
instruction basée sur la plage ", p. 145la source
new[]
. Dans ce cas, vous n'avez qu'un pointeur sans indication de la taille, il n'y a donc aucun moyen pour le baséfor
sur la plage de fonctionner avec.Est-ce que cela se lit comme suit: " Dites-moi ce que fait une distance pour (avec des tableaux)? "
Je répondrai en supposant que - Prenons l'exemple suivant en utilisant des tableaux imbriqués:
int ia[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}}; for (auto &pl : ia)
Version texte:
ia
est un tableau de tableaux ("tableau imbriqué"), contenant des[3]
tableaux, chacun contenant des[4]
valeurs. L'exemple ci-dessus effectue une boucleia
par sa «plage» principale ([3]
), et donc boucle des[3]
temps. Chaque boucle produit l' un desia
des »[3]
valeurs primaires à partir de la première et se terminant par le dernier - un tableau contenant des[4]
valeurs.pl
égale{1,2,3,4}
- Un tableaupl
égale{5,6,7,8}
- Un tableaupl
equals{9,10,11,12}
- Un tableauAvant d'expliquer le processus, voici quelques rappels amicaux sur les tableaux:
pl
doit être une référence car nous ne pouvons pas copier de tableauxn
est le nombre en question, alorsia[n]
est le même que*(ia+n)
(Nous déréférencer l'adresse qui estn
entrée forward), etia+n
est identique à&ia[n]
(Nous obtenons l'adresse de cette entrée dans le tableau).Voici ce qui se passe:
pl
est défini comme une référence àia[n]
, avecn
égalité du nombre de boucles en cours à partir de 0. Donc,pl
c'estia[0]
au premier tour, au second c'estia[1]
, et ainsi de suite. Il récupère la valeur via une itération.ia+n
est inférieur àend(ia)
.... Et c'est à peu près tout.
C'est vraiment juste une façon simplifiée d'écrire ceci :
int ia[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}}; for (int n = 0; n != 3; ++n) auto &pl = ia[n];
Si votre tableau n'est pas imbriqué, alors ce processus devient un peu plus simple en ce qu'une référence n'est pas nécessaire, car la valeur itérée n'est pas un tableau mais plutôt une valeur `` normale '':
int ib[3] = {1,2,3}; // short for (auto pl : ib) cout << pl; // long for (int n = 0; n != 3; ++n) cout << ib[n];
Quelques informations complémentaires
Et si nous ne voulions pas utiliser le
auto
mot - clé lors de la créationpl
? À quoi cela ressemblerait-il?Dans l'exemple suivant,
pl
fait référence à un fichierarray of four integers
. Sur chaque bouclepl
est donnée la valeuria[n]
:int ia[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}}; for (int (&pl)[4] : ia)
Et ... c'est comme ça que ça marche, avec des informations supplémentaires pour balayer toute confusion. C'est juste une
for
boucle «abrégée» qui compte automatiquement pour vous, mais qui manque d'un moyen de récupérer la boucle actuelle sans le faire manuellement.la source
Quelques exemples de code pour démontrer la différence entre les tableaux sur Stack et les tableaux sur Heap
/** * Question: Can we use range based for built-in arrays * Answer: Maybe * 1) Yes, when array is on the Stack * 2) No, when array is the Heap * 3) Yes, When the array is on the Stack, * but the array elements are on the HEAP */ void testStackHeapArrays() { int Size = 5; Square StackSquares[Size]; // 5 Square's on Stack int StackInts[Size]; // 5 int's on Stack // auto is Square, passed as constant reference for (const auto &Sq : StackSquares) cout << "StackSquare has length " << Sq.getLength() << endl; // auto is int, passed as constant reference // the int values are whatever is in memory!!! for (const auto &I : StackInts) cout << "StackInts value is " << I << endl; // Better version would be: auto HeapSquares = new Square[Size]; Square *HeapSquares = new Square[Size]; // 5 Square's on Heap int *HeapInts = new int[Size]; // 5 int's on Heap // does not compile, // *HeapSquares is a pointer to the start of a memory location, // compiler cannot know how many Square's it has // for (auto &Sq : HeapSquares) // cout << "HeapSquare has length " << Sq.getLength() << endl; // does not compile, same reason as above // for (const auto &I : HeapInts) // cout << "HeapInts value is " << I << endl; // Create 3 Square objects on the Heap // Create an array of size-3 on the Stack with Square pointers // size of array is known to compiler Square *HeapSquares2[]{new Square(23), new Square(57), new Square(99)}; // auto is Square*, passed as constant reference for (const auto &Sq : HeapSquares2) cout << "HeapSquare2 has length " << Sq->getLength() << endl; // Create 3 int objects on the Heap // Create an array of size-3 on the Stack with int pointers // size of array is known to compiler int *HeapInts2[]{new int(23), new int(57), new int(99)}; // auto is int*, passed as constant reference for (const auto &I : HeapInts2) cout << "HeapInts2 has value " << *I << endl; delete[] HeapSquares; delete[] HeapInts; for (const auto &Sq : HeapSquares2) delete Sq; for (const auto &I : HeapInts2) delete I; // cannot delete HeapSquares2 or HeapInts2 since those arrays are on Stack }
la source