[C++ Primer Reading Notes] Day 6

Scope

Chapter 3.4

Review

Chapter 3.4 mainly introduces the using of iterator. It can be seen as the pointer to library containers like vectors, and to strings. In Extra Summaries, I also make an analogy between pointers and iterators.

Notes

1. about the iterators

1.1 the role of iterators play

Although we can use subscripts to access the characters of a string or the elements in a vector, there is a more general mechanism—known as iterators—that we can use for the same purpose.

All of the library containers have iterators, but only a few of them support the subscript operator.

This is the reason why it is better and more general to use iterators.

Technically speaking, a string is not a container type, but string supports many of the container
operations. As we’ve seen string, like vector has a subscript operator. Like vectors, strings also have iterators.

The strings and the vectors are very similar.

1.2 the begin member and end member of iterators

Unlike pointers, we do not use the address-of operator to obtain an iterator. Instead, types that have iterators have members that return iterators. In particular, these types have members named begin and end.

The begin member returns an iterator that denotes the first element (or first character)
The iterator returned by end is an iterator positioned “one past the end” of the associated container (or string). This iterator denotes a nonexistent element “off the end” of the container.

The begin member and end member can be used simutaineously when judging whether the container is empty.

If the container is empty, the iterators returned by begin and end are equal—they are both off-the-end iterators.

In terms of the charactor of const or nonconst, the return of begin() and end() are similar to the a pointer except that they have different types.

The type returned by begin and end depends on whether the object on which they operator is const. If the object is const, then begin and end return a const_iterator; if the object is not const, they return iterator.

For convinence, C++ provides another two members that can access const_iterator directly:

it is usually best to use a const type (such as const_iterator) when we need to read but do not need to write to an object. To let us ask specifically for the const_iterator type, the new standard introduced two new functions named cbegin and cend

auto it3 = v.cbegin(); // it3 has type vector<int>::const_iterator

1.3 iterator operators

As with pointers, we can dereference an iterator to obtain the element denoted by an iterator. Also, like pointers, we may dereference only a valid iterator that denotes an element.

string s("some string");
if (s.begin() != s.end()) { // make sure s is not empty
	auto it = s.begin(); // it denotes the first character in s
	*it = toupper(*it); // make that character uppercase
}

Iterators use the increment (++) operator to move from one element to the next. Incrementing an iterator is a logically similar operation to incrementing an integer.

// process characters in s until we run out of characters or we hit a whitespace
for (auto it = s.begin(); it != s.end() && !isspace(*it); ++it)
	*it = toupper(*it); // capitalize the current character

Caution:

Because the iterator returned from end does not denote an element, it may not be incremented or dereferenced

Just like pointers, we have to pay attention to the undefined dereference.

Tips:

C++ programmers use != as a matter of habit.

Instead of using < or <=, it is better to use != in C++, because != is applicable to all kinds of container besides strings and vectors.

1.4 iterator types

as with size_type, the library types that have iterators define types named iterator and const_iterator that represent actual iterator types.

By the way, we have to specify the type of the element that the container contains. For example, we write ""after “vector”:

vector<int>::iterator it;

The difference between iterator and const iterator and the difference between pointer and const pointer can also be analogues.

A const_iterator behaves like a const pointer. Like a const pointer, a const_iterator may read but not write the element it denotes; an object of type iterator can both read and write. If a vector or string is const, we may use only its const_iterator type.

1.5 combining dereference and member access

When we dereference an iterator, we get the object that the iterator denotes. If that object has a class type, we may want to access a member of that object.

(*it).empty()

The parentheses in (*it).empty() are necessary because to make sure the * operator is applied to it first.

To simplify expressions such as this one, the language defines the arrow operator (the -> operator).
That is, it->mem is a synonym for (* it).mem.

1.6 things can’t do with the vector

The length of a vector can be varied, this may cause some problems. Here are some implications we should know.

we cannot add elements to a vector inside a range for loop

any operation, such as push_back, that changes the size of a vector potentially invalidates all iterators into that vector

1.7 iterators arithmetic for string and vector

There are some operators that can be applied to iterators of all kinds of containers

Incrementing an iterator moves the iterator one element at a time. All the library containers have iterators that support increment.
Similarly, we can use == and != to compare two valid iterators into any of the library container types.

Here are some operators that can only be applied to iterators of string and vector, including +,-,+=,-+,>,>=,<,<=.

Iterators for string and vector support additional operations that can move an iterator multiple elements at a time. They also support all the relational operators.

We can also subtract two iterators so long as they refer to elements in, or one off the end of, the same vector or string. The result is the distance between the iterators.
The result type is a signed integral type named difference_type.

Extra Summaries

1. comparisons among strings, vectors and array

1.1 similarities

  • both of them can use subscripts
  • the result of dereference a pointer/iterator is the element

1.2 differences

stringsvectorsarrays
copy initialization is allowedcopy initialization is allowedcopy initialization is not allowed
assignment is allowedassignment is allowedassignment is not allowed
the result of default initialization is an empty stringthe result of default initialization is an empty vectorthe result of default initialization depends on whether the array is defined inside any function
iterator is applicableiterator is applicableiterator is not applicable
can use cout, cin, getline to deal with input and outputhave to loop through elements to printhave to loop through elements to print
the resul of subscripting a string is the reference of the elementthe resul of subscripting a string is the reference of the elementthe resul of subscripting a string is the element (When we subscript an array, we are really subscripting a pointer to an element in that array)

2. range-based for loop

The range-based for loop can be used to access or change the elements of strings, vectors and arrays. There are some rules that need our attention.

2.1 rule1

If we want to change the elements in strings, vectors or arrays in a range-based for loop, we have to define the loop control variable as a reference.

#include <string>
#include <vector>
#include <iostream>
using namespace std;

int main()
{
    // applied to strings
    string str1("angler baby");
    for (auto &c : str1)  // define c as reference
        c = toupper(c);
    cout<<str1<<endl;

	// applied to vectors
    vector<int> vec1(5, 1);
    for (auto &i : vec1)  // define i as reference
    {
        i += 5;
        cout<<i<<endl;
    }
	
	// applied to arrays
	int arr1[5] = {1,2,3,4,5};
    for (auto &i : arr1)
    {
        i += 1;
        cout<<i<<endl;
    }
   
    return 0;
}

2.2 rule2

If we want to access (not change) elements in a two dimension array, we have to define the control variable as a reference in the first loop. Take the following program as an example, even if we don’t need to change the elements in this two dimension array, we have to define i as reference. 1

#include <iostream>
using namespace std;

int main()
{
    int arr1[3][2] = {1,2,3,4,5,6};
    for (auto &i : arr1)  // define i as reference
    {
        for (auto j : i)
        {
            cout<<j<<endl;
        }
    }

    return 0;
}

arr1 in the above program is actually a size-3 array that contain 3 pointers. If we don’t define i as reference, i would be a pointer by default. In this case, we can’t loop i in the second loop unless it is no more a pointer. If we define i as a reference, then i would become a size-2 array where we can use a for loop. The following program can illustrate more details.

#include <iostream>
#include <typeinfo>
using namespace std;

int main()
{
    int arr1[3][2] = {1,2,3,4,5,6};
    
    // test1
    // the first way to define i as a reference
    for (auto &i  : arr1)
   	{
       	for (auto j : i)
       	{
            cout<<j<<endl;
            cout<<"the type of j is: "<<typeid(j).name()<<endl;
        }
       cout<<"the type of i is: "<<typeid(i).name()<<endl;
   	}
    
    // test2
    // the second way to define i as a reference
    for (int (&i)[2]  : arr1)
    {
        for (auto j : i)
        {
            cout<<j<<endl;
            cout<<"the type of j is: "<<typeid(j).name()<<endl;
        }
        cout<<"the type of i is: "<<typeid(i).name()<<endl;
    }

    // test3
    // what if we don't define i as a reerence
    for (auto i : arr1)
    {
        cout<<"the type of i is: "<<typeid(i).name()<<endl;
    }

    return 0;
}

In test1 and test2, we defind i as a reference. The type of i in this cases are “A2_i”, probably means an array. In test 3 where we don’t define i as a reference, the type of i is “Pi”, which probably means pointer. This result varify our claim.

According to this book,

To use a multidimensional array in a range for, the loop control variable for all but the innermost array must be references.

If we write the program like this:

for (auto row : ia)
	for (auto col : row)

our program would not compile. As before, the first for iterates through ia, whose elements are arrays of size 4. Because row is not a reference, when the compiler initializes row it will convert each array element (like any other object of array type) to a pointer to that array’s first element. As a result, in this loop the type of row is
int*. The inner for loop is illegal. Despite our intentions, that loop attempts to iterate over an int*.

3. the analogy between iterators and pointers

We can make an analogy between iterators and pointers.

iteratorspointers
it points to an element in the containerit points to a variable in memory
the result of dereferencing an iterator is the reference of the elementthe result of dereferencing a pointer is the reference of the variable
it has const_iterator typeis has const pointer type
it can use arithmetic operatorsit can use arithmetic operators
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值