在C++11中,您可以使用基于范围的 for
循环,它起到了其他语言中 foreach
的作用。即使是普通的 C 数组,也可以使用这种循环:
int numbers[] = { 1, 2, 3, 4, 5 };
for (int& n : numbers) {
n *= 2;
}
它如何知道何时停止?它只能与在for
中使用相同作用域内已声明的静态数组一起工作吗?如何在动态数组中使用这个for
?
它适用于任何类型为数组的表达式。例如:
int (*arraypointer)[4] = new int[1][4]{{1, 2, 3, 4}};
for(int &n : *arraypointer)
n *= 2;
delete [] arraypointer;
如果在range-based for语句中,冒号右侧表达式的类型是数组类型,那么循环将从ptr
到ptr + size
进行迭代(其中ptr
指向数组的第一个元素,size
为数组元素数量)。与之相反,如果传递类对象或(如果没有以这种方式称呼的成员)非成员函数,则用户定义类型通过查找成员begin
和end
来工作。这些函数将产生begin和end迭代器(分别指向最后一个元素直接后面和序列的开头)。这个问题解释了为什么存在这种差异。begin
`end。只是恰好
std::begin\
std::end`使用成员函数,并且如果没有更好的匹配,则会被使用。 - Dennis Zickefoose我认为这个问题最重要的部分是,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;
{
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;
};
这个类模板可以用来创建一个范围,通过新的ranged for语法进行迭代。我使用它来遍历场景中所有动画对象,该场景是使用只返回指向数组和大小的指针的库导入的。
for ( auto pAnimation : Range<aiAnimation*>(pScene->mAnimations, pScene->mNumAnimations) )
{
// Do something with each pAnimation instance here
}
在我看来,这种语法比使用std::for_each
或纯粹的for
循环更清晰明了。
new[]
创建的数组被称为“动态数组”。在这种情况下,你只有一个指针而没有大小的指示,所以无法使用范围式的for
循环。 - Mike Seymourrange-based for循环如何处理普通数组?
这是否可以理解为“告诉我range-based for循环(对于数组)的作用?”
我将回答并假设您已经知道以下使用嵌套数组的示例:
int ia[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
for (auto &pl : ia)
ia
是一个包含[3]
个数组的二维数组,每个数组包含[4]
个值。上述示例通过其主要的“范围”([3]
)循��遍历ia
,因此循环[3]
次。每次循环都会产生ia
的[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];
一些额外信息
如果我们在创建pl
时不想使用auto
关键字,那么会是什么样子呢?
在下面的示例中,pl
指的是一个四个整数的数组
。在每次循环中,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
}
for
来访问。但是当数组衰变为指针时,大小信息就会丢失。 - JohannesDnumbers
数组中元素的数量为sizeof(numbers)/sizeof(int)
。 - JohannesD