!!!Chapter 9 Sequential Containers (9.1 ~ 9.3)

A container holds a collection of objects of a specified type. The library vector type is a sequential container. The order of elements in a sequential container is independent of the value of the elements. The order is determined by the order in which elements are added to the container.

The library defines three kinds of sequential containers: vector, list and deque (called "deck").

The library also provides three sequential container adaptors. Effectively, an adaptor adapts an underlying container type by defining a new interface in terms of the operations provided by the original type. The sequential container adaptors are stack, queue, and priority_queue.

The containers vary as to which operations they provide, but if two containers provide the same operation, then the interface will be the same for both container types.

There are different sets of operations:

1. Some operations are supported by all container types.

2. Other operations are common to only the sequential or only the associative containers.

3. Still others are common to only a subset of either the sequential or associative containers.

9.1 Defining a Sequential Container

To define a container object, we must include its associated header file:

#include <vector>
#include <list>
#include <deque>

To define a particular kind of container, we name the container followed by angle brackets that enclose the type of the elements the container will hold:

vector<string>     svec;
list<int>          ilist;
deque<Sales_item>  items;

Each container defines a default constructor that creates an empty container of the specified type. In most programs, using the default constructor gives the best run-time performance and makes using the container easier.

9.1.1 Initializing Container Elements

Container Constructors
C<T> c;create an empty container with default constructor. Valid for all containers
C c(c2);create c as a copy of container c2; c and c2 must be the same container type. Valid for all containers
C c(b,e);create c with a copy of the elements from the range denoted by iterators b and e.
Valid for all containers
include b, exclude e.
C c(n,t);create c with n elements, each with value t. Sequential container only
c c(n);create c with n value-initialized elements. Sequential container only

1. Initializing a container as a copy of another container

When we copy one container into another, the types must match exactly: The container type and element type must be the same.

2. Initializing as a copy of a range of elements

When we use iterators, there is no requirement that the container types be identical. The element types in the containers can differ as long as they are compatible.

Here the iterators mark the first and one past the last element to be copied:

//initialize list with vector
list<string> slist(svec.begin(), svec.end());

Since pointers are iterators, we can initialize a container from a pair of pointers into a built-in array. P 308

3. Allocating and initializing a specified number of elements

a. provide both number of elements and value of element.

b. only provide number of elements: library will generate value-initialized element:

for built-in type, the initial value is 0.

for class, the initial value is determined by default constructor.

if don't not have default constructor, then must specify the value.

9.1.2 Constraints on Types that a Container Can Hold

There are two constraints that element types must meet:

1. The element type must support assignment.

2. We must be able to copy objects of the element type.

For built-in and compound types, only reference does not meet the requirement. Reference do not support assignment

For library types, IO library types do not support copy operations.

Containers of Containers

We can define containers with elements that are themselves containers.

//note spacing: use"> >" when specifying a container element type
vector< vector<string> > lines;    //vector of vectors
vector< vector<string>> lines;     //error: >>treats as shift operator 

9.2 Iterators and Iterator Ranges

Each of the library container types has several companion vector iterators.

All the iterators have a common interface: If an iterator provides an operation, then the operation is supported in the same way for each iterator that supplies that operation.

Standard Container Iterator Operations
*iterreturn a reference to the element referred to by the iterator
iter->memdereference and fetch the member. Equivalent to (*iter).mem
++iter/iter++Increment iter
--iter/iter--decrement iter
iter1 == iter2
iter1 != iter2
compare two iterators for equality

Iterators on vector and deque support additional operations

There are two important sets of operations that only vector and deque support

1. iterator arithmetic

2. the use of relational operators (in addition to == and !=)

The reason is only vector and deque offer fast, random access to their elements

Operations Supported by vector/deque
iter + n
iter - n
adding(subtracting) an integral to yield another iterator
iter += n
iter -= n
Compound-assignment version
iter1 - iter2The iterators must refer to elements in the same container or one past the end of the container
>, >=, <, <=Relational operators. The iterators must refer to elements in the same container or one past the end of the container

9.2.1 Iterator Ranges

An iterator range is denoted by the first iterator and the last iterator. The last iterator points to one past the last element.

This element range is called a left-inclusive interval.  [first, last)

Programming implications of Using left-inclusive ranges

1. When first equals last, the range is empty

2. When first is not equal to last, there is at least one element in the range, and first refers to the first element in that range. Moreover, we can advance first by increasing it some number of times untilfirst == last.

The above property means we can safely write loops to process a range of elements by testing the iterators:

while (first != last)
{
   //do something
   ++first;
}

Here the condition handles the case where the range is empty, there is no need for a special case to handle an empty range.

9.2.2 Some Container Operations Invalidate Iterators

Some container operations change the internal state of a container or cause the elements in the container to be moved. Such operations invalidate all iterators that refer to the elements that are moved and may invalidate other iterators as well.

9.3 Sequential Container Operations

Each sequential container let us:
1. Add elements to the container
2. Delete elements from the container
3. Determine the size of the container
4. Fetch the first and last elements from the container, if any

9.3.1 Container Typedefs

Each container defines several types:
Container Defined Typedefs
size_typeUnsigned integral type to hold size of container
iteratorType of iterator for this type
const_iteratorConst iterator
reverse_iteratorIterator that addresses elements in the reverse order
const_reverse_iteratorConst reverse iterator
difference_typesigned integral type to hold the difference between two iterators
value_typeelement type
referenceElement's lvalue type; synonym for value_type&
const_referenceElement's const lvalue type
Briefly, a reverse iterator goes backward through a container and inverts the iterator operations: ++ on a reverse iterator yields the previous element in the container.

9.3.2 begin and end Members

Container begin and end Operations
c.begin()Yields an iterator referring to the first element in c
c.end()Yields an iterator referring to the one past the last element in c
c.rbegin()Yields a reverse iterator referring to the last element in c
c.rend()Yields a reverse iterator referring one past the first element in c
There are two different versions of each of these operations: const or nonconst.
The return type depends on whether the container is const.

9.3.3 Adding Elements to a Sequential Container

Every sequential container supports push_back, which appends an element to the back of the container.
In addition to push_back, the list and deque containers support push_front.
for(size_t ix = 0; ix != 4; ++ix)
ilist.push_front(ix);               //the result is: 3,2,1,0
When we add an element to container, we do so by copying the element value into the container. So changes to the element in the container have no effect on the value that was copied, and vice verse.
Add elements to a Sequential Container
c.push_back(t)add element t at the end of c, returns void 
c.push_front(t)add t at the front of c, return void. Only for list or deque
c.insert(p,t)insert t before the element referred by iterator p.
return an iterator referring to the element that was added.
c.insert(p,n,t)insert n elements with value t before the element referred to by iterator p.
return void
c.insert(p,b,e)insert elements in the range denoted by iterators b and e before the element referred to by p.return void

Adding Elements at a Specified Point in the Container

insert allows us to insert elements at any particular point in the container. There are three versions of insert:

1. takes an iterator and an element value.The value is inserted before the position referred by the iterator. This version returns an iterator referring to the newly inserted element. We could use the return value to repeatedly insert elements:

//equivalent to call slist.push_front(spouse)
slist.insert(slist.begin(), spouse);
//continue insert elements in the front:
list<string> lst;
list<string>::iterator iter = lst.begin();
while(cin >> word)
  iter = lst.insert(iter, word);  

So we can insert element in the front of vector.(vector does not support push_front)
2. adds a specified number of identical elements at an indicated position.
//inserts ten elements at the end of svec
svec.insert(svec.end(), 10, "Anna");
3. adds a range of elements denoted by an iterator pair into the container(left inclusive):
string sarray[4] = {"a", "b", "c", "d"};
slist.insert(slist.end(), sarray, sarray+4);
Inserting Elements Can Invalidate Iterators

Avoid Storing the Iterator Returned from end P 321

After insert/push, some or all of the iterators may be invalid. It is safest to assume that all iterators are invalid.

9.3.4 Relational Operators

To compare two containers, the containers must be the same kind of container and must hold elements of the same type. So vector<int> can only compare with vector<int>, not vector<double> or list<int>.

The operators work similarly to the string relationals:

1. If both containers are the same size and all the elements are equal, then the two containers are equal; otherwise, they are unequal.

2. If the containers have different sizes but every element of the shorter one is equal to the corresponding element of the longer one, then the shorter one is considered to be less than the other

3. If neither container is an initial subsequence of the other, then the comparison depends on comparing the first unequal elements.

Relational Operators Use their Element's Relational Operator

We can use a relational operator to compare two containers only if the operator is also defined for the element types.
So we could compare two vector<int>, but we could not compare two vector<Sales_item>.

9.3.5 Container Size Operations

Sequential Container Size Operations
c.size()return the number of elements in the container
c.max_size()return maximum number of elements c can contain
return type is c::size_type
c.empty()returns a bool that is true if size is zero and false otherwise.
c.resize(n)resize c so that it can has n elements. If N <c.size(), the excess elements are discarded. If new elements must be added, they are value initialized
c.resize(n,t)resize c to have n elements. Any elements added have value t.

9.3.6 Accessing Elements

Operations to Access Elements in Sequential Container
c.back()return reference to last element in c.
undifined if c is empty
c.front()return reference to the first element in c.
undifined if c is empty
c[n]return a reference to the element indexed by n.
undifined if n < 0 or n > c.size()
Valid only for vector and deque.
c.at(n)return reference to the element indexed by n. If index is out of range,
throws out_of_range exception
Valid only for vector and deque.
Two ways to return references bound to the first/last elements:
if(!ilist.empty())
{
  //val1 and val2 refer to the same element
  list<int>::reference val1 = *ilist.begin();
  list<int>::reference val2 = ilist.front();

  //val3 and val4 refer to the same element
  list<int>::reference val3 = *--ilist.end();
  list<int>::reference val4 = ilist.back();
}

9.3.7 Erasing Elements

Operations to Remove Elements from a Sequential Container
c.erase(p)Removes element referred to by the iterator p.
Returns an iterator referring to the element afterthe one deleted, or an off-the-end iterator if p is the last element.
Undefined if p is an off-the-end iterator
c.erase(b,e)Removes the range of elements denoted by the iterator b and e.
Returns an iterator referring after the last one in the range that was deleted,
or an off-the-end iterator if e is an off-the-end iterator.
c.clear()Removes all the elements in c. Return void
c.pop_back()Removes the last element in c. Return void. Undefined if c is empty
c.pop_front()Removes the first element in c. Return void. Undefined if c is empty.
Valid only for list or deque.

Removing the First or Last Element

A common use of pop_front is to use it together with front to process a container as a stack:
while(!ilist.empty())
{
  process(ilist.front());    //do something
  ilist.pop_front();         //then delete the first element
}
Removing an Element From within the Container
We often use erase together with find algorithm (need to include algorithm header)
string searchValue("Quasimodo");
list<string>::iterator iter = 
            find(slist.begin(), slist.end(), searchValue);
if (iter != slist.end())
  slist.erase(iter);
Removing All the elements in a Container
//two ways to delete all elements
slist.clear();
slist.erase(slist.begin(), slist.end());

9.3.8 Assignment and swap

Sequential Container Assignment Operations
c1 = c2Deletes elements in c1 and copies elements from c2 into c1.
c1 and c2 must be the same type
c1.swap(c2)Swaps contents.
Execution time is much faster than copying elements from c2 to c1.
(Of course, only modify a pointer!)
c.assign(b,e)Replace the elements in c by those in the range denoted by iterators b and e.
The iterators b and e must not refer to elements in c.
c.assign(n,t)Replace the elements in c by n elements with value t.

Except for swap, other assignment operations can be expressed in terms of eraseand insert operations.
c1 = c2;
//equivalent to:
c1.erase(c1.begin(), c1.end());
c1.insert(c1.begin(), c2.begin(), c2.end());

Assignment invalidate all iterators into the left-hand container.

swap does not invalidate iterators. After swap, iterators continue to refer to the same elements, although those elements are now in a different container.

Using assign

The assignment operator(=) can be used to assign one container to another only if the container and element type are the same.
The assign operation (the function) can be used to assign elements of a different but compatible element type/or a different container. (from vector<char*> to list<string>)

Using swap to Avoid the Cost of Deleting Elements

To use swap: The operands must be the same kind of container, and they must hold values of the same type.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值