c++ primer plus 第16章string 类和标准模板库, 16.3.2 可对矢量执行的操作
c++ primer plus 第16章string 类和标准模板库, 16.3.2 可对矢量执行的操作
16.3.2 可对矢量执行的操作
除分配存储空间外,vector 模板还可以完成哪些任务呢?所有的STL容器都提供了一些基本方法,其中包括 size()–返回容器中元素数目、swap()–交换两个容器的内容、begin()–返回一个指向容器中第一个元素的迭代器、end()–返回一个表示超过容器尾的迭代器。什么是迭代器?它是一个广义指针。事实上,它可以是指针,也可以是一个可对其执行类似指针的操作–如解除引用(如 operator*())和递增(如 operator++( ))–的对象。稍后将知道,通过将指针广义化为迭代器,让 STL能够为各种不同的容器类(包括那些简单指针无法处理的类)提供统一的接口。每个容器类都定义了一个合适的迭代器,该迭代器的类型是一个名为 iterator 的typedef,其作用域为整个类。例如,要为vector的double类型规范声明一个迭代器,可以这样做:
vector<double>::iterator pd;//pd an iterator
假设 scores 是一个 vector对象:
vector<double>scores;
则可以使用迭代器 pd 执行这样的操作:
pd = scores.begin();//have pd point to the first element
*pd =22.3;//dereference pd and assign value to first element
++pd;//make pd point to the next element
正如您看到的,迭代器的行为就像指针。顺便说一句,还有一个C++11自动类型推断很有用的地方。例如,可以不这样做:
vector<double>::iterator pd=scores.begin();
而这样做:
auto pd =scores.begin();//C++11 automatic type deduction回到前面的示例。什么是超过结尾(past-the-end)呢?它是一种迭代器,指向容器最后一个元素后面的那个元素。这与 C-风格字符串最后一个字符后面的空字符类似,只是空字符是一个值,而“超过结尾’是一个指向元素的指针(迭代器)。end()成员函数标识超过结尾的位置。如果将迭代器设置为容器的第一个元素,并不断地递增,则最终它将到达容器结尾,从而遍历整个容器的内容。因此,如果scores和pd的定义与前面的示例中相同,则可以用下面的代码来显示容器的内容:
for(pd=scores.begin();pd != scores.end(); pd++)
cout << *pd << endl;
所有容器都包含刚才讨论的那些方法。vector模板类也包含一些只有某些STL容器才有的方法。push back()是一个方便的方法,它将元素添加到矢量末尾。这样做时,它将负责内存管理,增加矢量的长度,使之能够容纳新的成员。这意味着可以编写这样的代码:
vector<double>scores;//create an empty vector
double temp;
while(cin>>temp &&temp >=0)
scores.push back(temp);
cout <<"You entered"<<scores.size()<<" scores.\n";
每次循环都给 scores 对象增加一个元素。在编写或运行程序时,无需了解元素的数目。只要能够取得足够的内存,程序就可以根据需要增加 scores 的长度。
erase()方法删除矢量中给定区间的元素。它接受两个迭代器参数,这些参数定义了要删除的区间。了解 STL 如何使用两个迭代器来定义区间至关重要。第一个迭代器指向区间的起始处,第二个迭代器位于区间终止处的后一个位置。例如,下述代码删除第一个和第二个元素,即删除begin()和 begin()+1指向的元素(由于 vector 提供了随机访问功能,因此 vector 类迭代器定义了诸如 begin()+2等操作):
scores.erase(scores.begin(),scores.begin()+2);
如果 it1 和 it2 是迭代器,则 STL 文档使用[p1,p2)来表示从p1到p2(不包括 p2)的区间。因此,区间[begin(),end()]将包括集合的所有内容(参见图 16.3),而区间[p1,p1)为空。[)表示法并不是 C++的组成部分,因此不能在代码中使用,而只能出现在文档中。
注意:区间[it1.it2)由迭代器 it1 和 it2 指定,其范围为 it1 到 it2(不包括 it2)。
insert()方法的功能与 erase()相反。它接受3个迭代器参数,第一个参数指定了新元素的插入位置,第二个和第三个迭代器参数定义了被插入区间,该区间通常是另一个容器对象的一部分。例如,下面的代码将矢量newv中除第一个元素外的所有元素插入到oldv矢量的第一个元素前面:
vector<int>old v;
vector<int>new v;
old v.insert(old v.begin(),new v.begin()+ 1,new v.end());
顺便说一句,对于这种情况,拥有超尾元素是非常方便的,因为这使得在矢量尾部附加元素非常简单。
下面的代码将新元素插入到 old.end()前面,即矢量最后一个元素的后面。
old v.insert(old v.end(),new v.begin()+ 1,new v.end());
程序清单16.8演示了 size()、begin()、end()、push back()、erase()和insert( )的用法。为简化数据处理,将程序清单16.7中的rating和title组合成了一个Review结构,并使用 FilReview()和 ShowReview()函数来输入和输出 Review 对象。
程序清单16.8 vect2.cpp
// vect2.cpp -- methods and iterators
#include <iostream>
#include <string>
#include <vector>
struct Review {
std::string title;
int rating;
};
bool FillReview(Review & rr);
void ShowReview(const Review & rr);
int main()
{
using std::cout;
using std::vector;
vector<Review> books;
Review temp;
while (FillReview(temp))
books.push_back(temp);
int num = books.size();
if (num > 0)
{
cout << "Thank you. You entered the following:\n"
<< "Rating\tBook\n";
for (int i = 0; i < num; i++)
ShowReview(books[i]);
cout << "Reprising:\n"
<< "Rating\tBook\n";
vector<Review>::iterator pr;
for (pr = books.begin(); pr != books.end(); pr++)
ShowReview(*pr);
vector <Review> oldlist(books); // copy constructor used
if (num > 3)
{
// remove 2 items
books.erase(books.begin() + 1, books.begin() + 3);
cout << "After erasure:\n";
for (pr = books.begin(); pr != books.end(); pr++)
ShowReview(*pr);
// insert 1 item
books.insert(books.begin(), oldlist.begin() + 1,
oldlist.begin() + 2);
cout << "After insertion:\n";
for (pr = books.begin(); pr != books.end(); pr++)
ShowReview(*pr);
}
books.swap(oldlist);
cout << "Swapping oldlist with books:\n";
for (pr = books.begin(); pr != books.end(); pr++)
ShowReview(*pr);
}
else
cout << "Nothing entered, nothing gained.\n";
// std::cin.get();
return 0;
}
bool FillReview(Review & rr)
{
std::cout << "Enter book title (quit to quit): ";
std::getline(std::cin,rr.title);
if (rr.title == "quit")
return false;
std::cout << "Enter book rating: ";
std::cin >> rr.rating;
if (!std::cin)
return false;
// get rid of rest of input line
while (std::cin.get() != '\n')
continue;
return true;
}
void ShowReview(const Review & rr)
{
std::cout << rr.rating << "\t" << rr.title << std::endl;
}