Modern C++:Efficient and Scalable Application Development
上QQ阅读APP看书,第一时间看更新

Looping with Iteration

There are two versions of the for statement, iteration and range-based. The latter was introduced in C++11. The iteration version has the following format:

    for (init_expression; condition; loop_expression) 
loop_statement;

You can provide one or more loop statements, and for more than one statement, you should provide a code block using braces. The purpose of the loop may be served by the loop expression, in which case you may not want a loop statement to be executed; here, you use the null statement, ; which means do nothing.

Within the parentheses are three expressions separated by semicolons. The first expression allows you to declare and initialize a loop variable. This variable is scoped to the for statement, so you can only use it in the for expressions or in the loop statements that follow. If you want more than one loop variable, you can declare them in this expression using the comma operator.

The for statement will loop while the condition expression is true; so if you are using a loop variable, you can use this expression to check the value of the loop variable. The third expression is called at the end of the loop, after the loop statement has been called; following this, the condition expression is called to see if the loop should continue. This final expression is often used to update the value of the loop variable. For example:

    for (int i = 0; i < 10; ++i)   
{
std::cout << i;
}

In this code, the loop variable is i and it is initialized to zero. Next, the condition is checked, and since i will be less than 10, the statement will be executed (printing the value to the console). The next action is the loop expression; ++i, is called, which increments the loop variable, i, and then the condition is checked, and so on. Since the condition is i < 10, this means that this loop will run ten times with a value of i between 0 and 9 (so you will see 0123456789 on the console).

The loop expression can be any expression you like, but often it increments or decrements a value. You do not have to change the loop variable value by 1; for example, you can use i -= 5 as the loop expression to decrease the variable by 5 on each loop. The loop variable can be any type you like; it does not have to be integer, it does not even have to be numeric (for example, it could be a pointer, or an iterator object described in Chapter 5, Using the Standard Library Containers), and the condition and loop expression do not have to use the loop variable. In fact, you do not have to declare a loop variable at all!

If you do not provide a loop condition then the loop will be infinite, unless you provide a check in the loop:

for (int i = 0; ; ++i)  
{
std::cout << i << std::endl;
if (i == 10) break;
}

This uses the break statement introduced earlier with the switch statement. It indicates that execution exits the for loop, and you can also use return, goto, or throw. You will rarely see a statement that finishes using goto; however, you may see the following:

for (;;)  
{
// code
}

In this case, there is no loop variable, no loop expression, and no conditional. This is an everlasting loop, and the code within the loop determines when the loop finishes.

The third expression in the for statement, the loop expression, can be anything you like; the only property is that it is executed at the end of a loop. You may choose to change another variable in this expression, or you can even provide several expressions separated by the comma operator. For example, if you have two functions, one called poll_data that returns true if there is more data available and false when there is no more data, and a function called get_data that returns the next available data item, you could use for as follows (bear in mind; this is a contrived example, to make a point):

for (int i = -1; poll_data(); i = get_data()) 
{
if (i != -1) std::cout << i << std::endl;
}

When poll_data returns a false value, the loop will end. The if statement is needed because the first time the loop is called, get_data has not yet been called. A better version is as follows:

for (; poll_data() ;) 
{
int i = get_data();
std::cout << i << std::endl;
}

Keep this example in mind for the following section.

There is one other keyword that you can use in a for loop. In many cases, your for loop will have many lines of code and at some point, you may decide that the current loop has completed and you want to start the next loop (or, more specifically, execute the loop expression and then test the condition). To do this, you can call continue:

for (float divisor = 0.f; divisor < 10.f; ++divisor)  
{
std::cout << divisor;
if (divisor == 0)
{
std::cout << std::endl;
continue;
}
std::cout << " " << (1 / divisor) << std::endl;
}

In this code, we print the reciprocal of the numbers 0 to 9 (0.f is a 4-byte floating-point literal). The first line in the for loop prints the loop variable, and the next line checks to see if the variable is zero. If it is, it prints a new line and continues, that is, the last line in the for loop is not executed. The reason is that the last line prints the reciprocal and it would be an error to divide any number by zero.

C++11 introduces another way to use the for loop, which is intended to be used with containers. The C++ standard library contains templates for container classes. These classes contain collections of objects, and provide access to those items in a standard way. The standard way is to iterate through collections using an iterator object. More details about how to do this will be given in Chapter 5, Using the Standard Library Containers; the syntax requires an understanding of pointers and iterators, so we will not cover them here. The range-based for loop gives a simple mechanism to access items in a container without explicitly using iterators.

The syntax is simple:

for (for_declaration : expression) loop_statement;

The first thing to point out is that there are only two expressions and they are separated by a colon (:). The first expression is used to declare the loop variable, which is of the type of the items in the collection being iterated through. The second expression gives access to the collection.


In C++ terms, the collections that can be used are those that define a begin and end function that gives access to iterators, and also to stack-based arrays (that the compiler knows the size of).

The Standard Library defines a container object called a vector. The vector template is a class that contains items of the type specified in the angle brackets (<>); in the following code, the vector is initialized in a special way that is new to C++11, called list initialization. This syntax allows you to specify the initial values of the vector in a list between curly braces. The following code creates and initializes a vector, and then uses an iteration for loop to print out all the values:

using namespace std; 
vector<string> beatles = { "John", "Paul", "George", "Ringo" };

for (int i = 0; i < beatles.size(); ++i)
{
cout << beatles.at(i) << endl;
}

Here a using statement is used so that the classes vector and string do not have to be used with fully qualified names.

The vector class has a member function called size (called through the . operator, which means "call this function on this object") that returns the number of items in the vector. Each item is accessed using the at function passing the item's index. The one big problem with this code is that it uses random access, that is, it accesses each item using its index. This is a property of vector, but other Standard Library container types do not have random access. The following uses the range-based for:

vector<string> beatles = { "John", "Paul", "George", "Ringo" }; 

for (string musician : beatles)
{
cout << musician << endl;
}

This syntax works with any of the standard container types and for arrays allocated on the stack:

int birth_years[] = { 1940, 1942, 1943, 1940 }; 

for (int birth_year : birth_years)
{
cout << birth_year << endl;
}

In this case, the compiler knows the size of the array (because the compiler has allocated the array) and so it can determine the range. The range-based for loop will iterate through all the items in the container, but as with the previous version you can leave the for loop using break, return, throw, or goto, and you can indicate that the next loop should be executed using the continue statement.