В C ++ 11 вы можете использовать диапазон for
, который действует как foreach
другие языки. Он работает даже с простыми массивами C:
int numbers[] = { 1, 2, 3, 4, 5 };
for (int& n : numbers) {
n *= 2;
}
Как он узнает, когда остановиться? Работает ли он только со статическими массивами, которые были объявлены в той же области, в for
которой используется? Как бы вы использовали это for
с динамическими массивами?
for
. Но в тот момент, когда этот массив распадается на указатель, информация о размере теряется.numbers
этоsizeof(numbers)/sizeof(int)
, например.Ответы:
Он работает для любого выражения, тип которого является массивом. Например:
int (*arraypointer)[4] = new int[1][4]{{1, 2, 3, 4}}; for(int &n : *arraypointer) n *= 2; delete [] arraypointer;
Для более подробного объяснения, если тип выражения, переданного справа от,
:
является типом массива, то цикл повторяется отptr
доptr + size
(ptr
указывая на первый элемент массива,size
являющийся количеством элементов массива).Это контрастирует с пользовательскими типами, которые работают путем поиска
begin
и вend
качестве членов, если вы передаете объект класса или (если нет членов, вызываемых таким образом) функции, не являющиеся членами. Эти функции будут давать начальный и конечный итераторы (указывающие непосредственно после последнего элемента и начала последовательности соответственно).Этот вопрос проясняет, почему существует такая разница.
источник
begin
`end. It just happens that
std :: begin,`std::end
использующий функции-члены, и будет использоваться, если лучшее совпадение недоступно.Я думаю, что самая важная часть этого вопроса заключается в том, как C ++ знает, каков размер массива (по крайней мере, я хотел знать это, когда нашел этот вопрос).
C ++ знает размер массива, потому что это часть определения массива - это тип переменной. Компилятор должен знать тип.
Поскольку C ++ 11
std::extent
можно использовать для получения размера массива:int size1{ std::extent< char[5] >::value }; std::cout << "Array size: " << size1 << std::endl;
Конечно, в этом нет особого смысла, потому что вы должны явно указать размер в первой строке, который затем вы получите во второй строке. Но вы также можете использовать,
decltype
и тогда будет интереснее:char v[] { 'A', 'B', 'C', 'D' }; int size2{ std::extent< decltype(v) >::value }; std::cout << "Array size: " << size2 << std::endl;
источник
Согласно последнему рабочему проекту C ++ (n3376), оператор range for эквивалентен следующему:
{ auto && __range = range-init; for (auto __begin = begin-expr, __end = end-expr; __begin != __end; ++__begin) { for-range-declaration = *__begin; statement } }
Таким образом, он знает, как остановить так же, как и обычный
for
цикл с использованием итераторов.Я думаю, вы можете искать что-то вроде следующего, чтобы предоставить способ использования вышеуказанного синтаксиса с массивами, которые состоят только из указателя и размера (динамические массивы):
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; };
Затем этот шаблон класса можно использовать для создания диапазона, по которому вы можете выполнять итерацию, используя новый синтаксис диапазона . Я использую это для просмотра всех объектов анимации в сцене, которая импортируется с использованием библиотеки, которая возвращает только указатель на массив и размер как отдельные значения.
for ( auto pAnimation : Range<aiAnimation*>(pScene->mAnimations, pScene->mNumAnimations) ) { // Do something with each pAnimation instance here }
Этот синтаксис, на мой взгляд, намного понятнее, чем тот, который вы бы использовали
std::for_each
или простойfor
цикл.источник
Он знает, когда остановиться, потому что знает границы статических массивов.
Я не уверен, что вы имеете в виду под «динамическими массивами», в любом случае, если не выполнять итерацию по статическим массивам, неофициально, компилятор просматривает имена
begin
иend
в области класса объекта, который вы повторяете, или просматривает доbegin(range)
иend(range)
с помощью аргумента-зависимого поиска и используют их в качестве итераторов.Дополнительные сведения см. В стандарте C ++ 11 (или его общедоступной версии), «6.5.4 Оператор на основе диапазона
for
», стр. 145источник
new[]
. В этом случае у вас есть только указатель без указания размера, поэтому нет возможностиfor
работать с ним на основе диапазона .Это должно читаться как " Скажите, что делает ранжированный (с массивами)? "
Я отвечу, предполагая, что - возьмите следующий пример с использованием вложенных массивов:
int ia[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}}; for (auto &pl : ia)
Текстовая версия:
ia
представляет собой массив массивов («вложенный массив»), содержащий[3]
массивы, каждый из которых содержит[4]
значения. В приведенном выше примереia
выполняется цикл с помощью его основного 'range' ([3]
), и, следовательно,[3]
время цикла . Каждый контур производит один изia
«S[3]
первичных значений , начиная с первой и заканчивая последней - массив , содержащий[4]
значения.pl
равно{1,2,3,4}
- массивpl
равно{5,6,7,8}
- массивpl
равно{9,10,11,12}
- массивПрежде чем мы объясним процесс, вот несколько простых напоминаний о массивах:
pl
должен быть ссылкой, потому что мы не можем копировать массивыn
это число, о котором идет речь, тоia[n]
это то же самое, что и*(ia+n)
(Мы разыменуем адрес, которыйn
записи вперед) иia+n
совпадает с&ia[n]
(Мы получаем адрес этой записи в массиве).Вот что происходит:
pl
устанавливается в качестве ссылки наia[n]
, сn
сравнявшись текущее значение счетчика цикла , начиная с 0. Итак,pl
находитсяia[0]
на первом раунде, на втором онia[1]
, и так далее. Он получает значение через итерацию.ia+n
пока меньшеend(ia)
.... И это все.
На самом деле это просто упрощенный способ написать это :
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];
Если ваш массив не вложен, этот процесс становится немного проще, поскольку ссылка не требуется, потому что повторяемое значение не является массивом, а скорее `` нормальным '' значением:
int ib[3] = {1,2,3}; // short for (auto pl : ib) cout << pl; // long for (int n = 0; n != 3; ++n) cout << ib[n];
Дополнительная информация
Что, если бы мы не хотели использовать
auto
ключевое слово при созданииpl
? Как бы это выглядело?В следующем примере
pl
относится кarray of four integers
. Каждому циклуpl
присваивается значениеia[n]
:int ia[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}}; for (int (&pl)[4] : ia)
И ... Вот как это работает, с дополнительной информацией, чтобы избавиться от путаницы. Это просто «сокращенный»
for
цикл, который автоматически засчитывается для вас, но не имеет возможности получить текущий цикл без выполнения этого вручную.источник
Пример кода, демонстрирующий разницу между массивами в стеке и массивами в куче
/** * 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 }
источник