217 lines
8.1 KiB
Markdown
217 lines
8.1 KiB
Markdown
|
# Chapter 3. Strings, Vectors, and Arrays
|
||
|
|
||
|
## 3.3 Library vector Type
|
||
|
|
||
|
* **vector** = collection of objs (all have same type)
|
||
|
* **vector** is a **container** (it "contains" other objects). Detail of containers are in Part II
|
||
|
* Include correct header and `using` declaration before using `vector`:
|
||
|
|
||
|
```cpp
|
||
|
#include <vector>
|
||
|
using std::vector;
|
||
|
```
|
||
|
|
||
|
* **vector** is a **class template** (Details are introduced in Chapter 13)
|
||
|
* Template are not functions or classes. But can be used to generating classes or functions.
|
||
|
* Process of creating classes/func from template is **instantiation**
|
||
|
* Specify which class to instantiate by `template_name<> obj_name` (C++11 standard)
|
||
|
|
||
|
```cpp
|
||
|
vector<int> ivec; // ivec holds objects of type int
|
||
|
vector<Sales_item> Sales_vec; // holds Sales_items
|
||
|
vector<vector<string>> file; // vector whose elements are vectors
|
||
|
```
|
||
|
|
||
|
Note:
|
||
|
|
||
|
* `vector` is template, not a type.
|
||
|
* Types generated from `vector` must include element type (e.g. `vector<int>`)
|
||
|
|
||
|
|
||
|
???
|
||
|
|
||
|
## 3.4 Introducing Iterators
|
||
|
|
||
|
Two ways of access containers (i.e. vector):
|
||
|
|
||
|
1. subscripts
|
||
|
2. **iterators**
|
||
|
|
||
|
All containers from library have iterators, but not all of them support subscript operator.
|
||
|
|
||
|
* iterator provide indirect access like pointers
|
||
|
* iterator can be valid/invalid
|
||
|
|
||
|
### 3.4.1 Using Iterators
|
||
|
|
||
|
* Containers have members to return iterator: `begin` & `end`
|
||
|
* `begin` returns an iterator denotes the 1st element:
|
||
|
* `end` return an iterator positioned "one past the end" of container (i.e. it's nonexistent element "off the end")
|
||
|
* iterator returned by `end` often referred to **off-the-end iterator** or **end iterator**
|
||
|
* If container is empty: `begin` `end`'s iterator are same
|
||
|
|
||
|
```cpp
|
||
|
// the compiler determines the type of b and e; see § 2.5.2 (p. 68)
|
||
|
// b denotes the first element and e denotes one past the last element in v
|
||
|
auto b = v.begin(), e = v.end(); // b and e have the same type
|
||
|
```
|
||
|
|
||
|
For best practice:
|
||
|
|
||
|
* Normally, we don't (want to) know the precise type of iterator. We Hence use `auto` to let compiler decide.
|
||
|
|
||
|
#### Iterator Operations
|
||
|
|
||
|
| operator | Description |
|
||
|
| ----------------------------------- | ---------------------------------------------------------------------------------------------------------- |
|
||
|
| `*iter` | Returns a reference to the element denoted by iterator |
|
||
|
| `iter->mem` | Dereference iter and fetches the member named `mem` from the underlying element. Equivalent to (*iter).mem |
|
||
|
| `++iter` | Increments `iter` to refer to next element |
|
||
|
| `--iter` | Decrements `iter` to refer to previous element |
|
||
|
| `iter1 == iter2` ; `iter1 != iter2` | Compares 2 iterators, they are equal if denoting same element or they are end iterator of same container |
|
||
|
|
||
|
* Here `*iter` return a reference is actually deference the iterator (like pointer's operation).
|
||
|
|
||
|
Following code transfer the first char to upper letter:
|
||
|
|
||
|
```cpp
|
||
|
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, dereference it via `*it`
|
||
|
}
|
||
|
```
|
||
|
|
||
|
* We dereference `it` to pass the current char to `toupper` and assign the resulting uppercase letter back to into character denoted by `it`
|
||
|
|
||
|
#### Moving Iterators from One Element to Another
|
||
|
|
||
|
* `++/--` in/decrement operators are used to move iterators from one element to next.
|
||
|
* end-iterator does not denote an element, it cannot be incremented & dereferenced.
|
||
|
|
||
|
```c++
|
||
|
// 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
|
||
|
```
|
||
|
|
||
|
* This code stop at whitespace
|
||
|
|
||
|
**Key Concept: Generic Programming**
|
||
|
|
||
|
* C++ programmers use `!=` as habit
|
||
|
* Only few library types have subscript operator, all library containers have iterators that defined `==` & `!=`. Most of them does not have `<`
|
||
|
|
||
|
#### Iterator Types
|
||
|
|
||
|
* We generally don't need to know the precise type of an iterator (like we use `auto` for vector's `size_type` member)
|
||
|
* Iterators from library types have `iterator` & `const_iterator` type
|
||
|
|
||
|
```cpp
|
||
|
vector<int>::iterator it; // it can read and write vector<int> elements
|
||
|
string::iterator it2; // it2 can read and write characters in a string
|
||
|
vector<int>::const_iterator it3; // it3 can read but not write elements
|
||
|
string::const_iterator it4; // it4 can read but not write characters
|
||
|
```
|
||
|
|
||
|
Terminology confusion: Iterators and Iterator Types
|
||
|
|
||
|
* "iterator" refers to 3 entities:
|
||
|
* 1. *concept* of an iterator
|
||
|
* 2. `iterator` *type* defined by an container
|
||
|
* 3. *object*
|
||
|
* Every container class defines a type named `iterator`; it supports actions
|
||
|
|
||
|
#### `begin` and `end` Operation
|
||
|
|
||
|
If object returned by `begin` & `end` are `const`, then type returned by `begin`/`end` are const
|
||
|
|
||
|
```cpp
|
||
|
vector<int> v;
|
||
|
const vector<int> cv; // cv is a vector with constant integer
|
||
|
auto it1 = v.begin(); // it1 has type vector<int>::iterator
|
||
|
auto it2 = cv.begin(); // it2 has type vector<int>::const_iterator
|
||
|
```
|
||
|
|
||
|
As shown above, the type are varying. C++11 gave **`cbegin`** & **`cend`** functions to specify type
|
||
|
|
||
|
```cpp
|
||
|
auto it3 = v.cbegin(); // it3 has type vector<int>::const_iterator
|
||
|
```
|
||
|
|
||
|
#### Combining Dereference and Member Access
|
||
|
|
||
|
Correct way of dereference iterator and access members:
|
||
|
|
||
|
* **`(xx)` for `(*it)` is required**. It means apply dereference operator to `it` and to apply dot operator to the result of dereferencing `it`.
|
||
|
|
||
|
```cpp
|
||
|
(*it).empty()
|
||
|
```
|
||
|
|
||
|
* Incorrect usage:
|
||
|
* Iterator has no members
|
||
|
|
||
|
```cpp
|
||
|
(*it).empty() // dereferences it and calls the member empty on the resulting object
|
||
|
*it.empty() // error: attempts to fetch the member named empty from it
|
||
|
// but it is an iterator and has no member named empty
|
||
|
```
|
||
|
|
||
|
Simplified dereference & member access:
|
||
|
|
||
|
* **`->` operator**;
|
||
|
|
||
|
```cpp
|
||
|
// print each line in text up to the first blank line
|
||
|
for (auto it = text.cbegin(); it != text.cend() && !it->empty(); ++it)
|
||
|
cout << *it << endl;
|
||
|
```
|
||
|
|
||
|
Currently remember loops that use iterators should not add elements to the container to which the iterator refer
|
||
|
|
||
|
### 3.4.2 Iterator Arithmetic
|
||
|
|
||
|
Operations suppoted by vector and string Iterators
|
||
|
|
||
|
| Operator | Description |
|
||
|
| -------------------------- | ------------------------------------------------------------------------ |
|
||
|
| `iter +/- n` | generate a iterator that many elements forward/backward within container |
|
||
|
| `iter += n` or `iter -= n` | Compound-assignment for iterator addition/subtraction |
|
||
|
| `iter1 - iter2` | yields the number |
|
||
|
| `>`, `>=`, `<`, `<=` | A iterator < other means it appears before |
|
||
|
|
||
|
```cpp
|
||
|
// compute an iterator to the element closest to the midpoint of vi
|
||
|
auto mid= vi.begin() + vi.size() / 2;
|
||
|
```
|
||
|
|
||
|
```cpp
|
||
|
if (it < mid)
|
||
|
// process elements in the first half of vi
|
||
|
```
|
||
|
|
||
|
??? Skip ???
|
||
|
|
||
|
#### Using Iterator Arithmetic
|
||
|
|
||
|
Binary search using iterator arithmetic
|
||
|
|
||
|
```cpp
|
||
|
// text must be sorted
|
||
|
// beg and end will denote the range we're searching
|
||
|
auto beg = text.begin(), end = text.end();
|
||
|
auto mid = text.begin() + (end - beg)/2; // original midpoint
|
||
|
// while there are still elements to look at and we haven't yet found sought
|
||
|
while (mid != end && *mid != sought) {
|
||
|
if (sought < *mid) // is the element we want in the first half?
|
||
|
end = mid; // if so, adjust the range to ignore the second half
|
||
|
else // the element we want is in the second half
|
||
|
beg = mid + 1; // start looking with the element just after mid
|
||
|
mid= beg + (end - beg)/2; // new midpoint
|
||
|
}
|
||
|
```
|
||
|
|
||
|
* At end of `while`, `mid` will be equal to `end` or it will denote the element for which we are looking.
|
||
|
|