No C ++ 11, você pode usar um baseado em intervalo for
, que atua como o foreach
de outras linguagens. Funciona mesmo com arrays C simples:
int numbers[] = { 1, 2, 3, 4, 5 };
for (int& n : numbers) {
n *= 2;
}
Como sabe quando parar? Ele funciona apenas com arrays estáticos que foram declarados no mesmo escopo em que for
são usados? Como você usaria isso for
com matrizes dinâmicas?
for
. Mas no momento em que o array se transforma em um ponteiro, as informações de tamanho são perdidas.numbers
ésizeof(numbers)/sizeof(int)
, por exemplo.Respostas:
Ele funciona para qualquer expressão cujo tipo seja uma matriz. Por exemplo:
int (*arraypointer)[4] = new int[1][4]{{1, 2, 3, 4}}; for(int &n : *arraypointer) n *= 2; delete [] arraypointer;
Para uma explicação mais detalhada, se o tipo da expressão passada à direita de
:
for um tipo de array, o loop itera deptr
paraptr + size
(ptr
apontando para o primeiro elemento do array,size
sendo a contagem de elementos do array).Isso contrasta com os tipos definidos pelo usuário, que funcionam procurando
begin
eend
como membros se você passar um objeto de classe ou (se não houver nenhum membro chamado dessa forma) funções não membros. Essas funções produzirão os iteradores de início e fim (apontando para diretamente após o último elemento e o início da sequência, respectivamente).Essa pergunta esclarece por que essa diferença existe.
fonte
begin
ʻend. It just happens that
std :: begin`std::end
use as funções de membro, e será usado se uma combinação melhor não estiver disponível.Acho que a parte mais importante dessa questão é como C ++ sabe qual é o tamanho de um array (pelo menos eu queria saber quando encontrei essa questão).
C ++ sabe o tamanho de um array, porque é parte da definição do array - é o tipo da variável. Um compilador precisa saber o tipo.
Uma vez que C ++ 11
std::extent
pode ser usado para obter o tamanho de uma matriz:int size1{ std::extent< char[5] >::value }; std::cout << "Array size: " << size1 << std::endl;
Claro, isso não faz muito sentido, porque você tem que fornecer explicitamente o tamanho na primeira linha, que você obtém na segunda linha. Mas você também pode usar
decltype
e fica mais interessante:char v[] { 'A', 'B', 'C', 'D' }; int size2{ std::extent< decltype(v) >::value }; std::cout << "Array size: " << size2 << std::endl;
fonte
De acordo com o C ++ Working Draft mais recente (n3376), a instrução range for é equivalente ao seguinte:
{ auto && __range = range-init; for (auto __begin = begin-expr, __end = end-expr; __begin != __end; ++__begin) { for-range-declaration = *__begin; statement } }
Portanto, ele sabe como parar da mesma forma que um
for
loop regular que usa iteradores.Acho que você pode estar procurando algo como o seguinte para fornecer uma maneira de usar a sintaxe acima com matrizes que consistem em apenas um ponteiro e tamanho (matrizes dinâmicas):
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; };
Este modelo de classe pode então ser usado para criar um intervalo, sobre o qual você pode iterar usando o novo intervalo para sintaxe. Estou usando isso para percorrer todos os objetos de animação em uma cena que é importada usando uma biblioteca que retorna apenas um ponteiro para uma matriz e um tamanho como valores separados.
for ( auto pAnimation : Range<aiAnimation*>(pScene->mAnimations, pScene->mNumAnimations) ) { // Do something with each pAnimation instance here }
Essa sintaxe é, na minha opinião, muito mais clara do que você usaria
std::for_each
ou umfor
loop simples .fonte
Ele sabe quando parar porque conhece os limites dos arrays estáticos.
Não tenho certeza do que você quer dizer com "matrizes dinâmicas", em qualquer caso, se não iterar sobre matrizes estáticas, informalmente, o compilador procura os nomes
begin
eend
no escopo da classe do objeto que você itera ou olha -se parabegin(range)
eend(range)
usando de pesquisa e os usa como iterators dependente de argumento.Para obter mais informações, no padrão C ++ 11 (ou rascunho público), "6.5.4 A
for
declaração baseada em intervalo ", pg.145fonte
new[]
. Nesse caso, você tem apenas um ponteiro sem indicação do tamanho, então não há como o baseadofor
em intervalo trabalhar com ele.Isso deve ser lido como " Diga-me o que faz um ranged-for (com matrizes)? "
Responderei supondo que - tome o seguinte exemplo usando matrizes aninhadas:
int ia[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}}; for (auto &pl : ia)
Versão do texto:
ia
é uma matriz de matrizes ("matriz aninhada"), contendo[3]
matrizes, cada uma contendo[4]
valores. O exemplo acima faz um loopia
por seu 'intervalo' primário ([3]
) e, portanto, faz um loop nos[3]
tempos. Cada circuito produz um dosia
's[3]
valores primários a partir do primeiro e terminando com o último - Uma matriz contendo[4]
os valores.pl
igual a{1,2,3,4}
- uma matrizpl
igual a{5,6,7,8}
- uma matrizpl
igual a{9,10,11,12}
- uma matrizAntes de explicarmos o processo, aqui estão alguns lembretes amigáveis sobre matrizes:
pl
devo ser uma referência porque não podemos copiar matrizesn
for o número em questão, entãoia[n]
é o mesmo que*(ia+n)
(Estamos desreferenciando o endereço dasn
entradas forward) eia+n
é o mesmo que&ia[n]
(estamos obtendo o endereço dessa entrada na matriz).Aqui está o que está acontecendo:
pl
é definido como uma referência aia[n]
,n
igualando a contagem do loop atual a partir de 0. Então,pl
estáia[0]
na primeira rodada, na segunda éia[1]
, e assim por diante. Ele recupera o valor por meio de iteração.ia+n
for menor queend(ia)
.... E é isso.
Na verdade, é apenas uma maneira simplificada de escrever isso :
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];
Se a sua matriz não estiver aninhada, esse processo se torna um pouco mais simples , pois não é necessária uma referência , porque o valor iterado não é uma matriz, mas um valor 'normal':
int ib[3] = {1,2,3}; // short for (auto pl : ib) cout << pl; // long for (int n = 0; n != 3; ++n) cout << ib[n];
Algumas informações adicionais
E se não quisermos usar a
auto
palavra - chave ao criarpl
? Qual seria a aparência disso?No exemplo a seguir,
pl
refere-se a umarray of four integers
. Em cada looppl
é dado o valoria[n]
:int ia[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}}; for (int (&pl)[4] : ia)
E ... É assim que funciona, com informações adicionais para afastar qualquer confusão. É apenas um
for
loop 'abreviado' que conta automaticamente para você, mas não tem uma maneira de recuperar o loop atual sem fazer isso manualmente.fonte
Alguns exemplos de código para demonstrar a diferença entre matrizes em Stack e matrizes em 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 }
fonte