vector
STL提供了一组表示容器、迭代器、函数对象和算法的模板。容器是一个与数组类似的单元,可以存储若干个值。STL容器是同质的,即存储的值的类型相同;算法是完成特定任务的厨房,迭代器能够用来遍历容器的对象,与能够遍历数组的指针类似,是广义指针;函数对象是类似于函数的对象,可以是类对象或函数指针。
STL不是面向对象的编程,而是一种不同的编程模式——泛型编程(generic programming)。
创建vector模板对象,使用通常的<type>表示法来指出要使用的类型。另外,vector模板使用动态内存分配,因此可以用初始化参数来指出需要多少矢量。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//vect1.cpp -- introducing the vector template #include <iostream> #include <string> #include <vector> const int NUM=5; using namespace std; int main() { using std::vector; using std::string; using std::cin; using std::cout; vector<int> ratings(NUM); vector<string> titles(NUM); cout<<"You will do exactly as told. You will enter\n" <<NUM<<" book titles and your ratings (0-10).\n"; int i; for(i=0;i<NUM;i++) { cout<<"Enter title #"<<i+1<<": "; getline(cin,titles[i]); cout<<"Enter your rating (0-10): "; cin>>ratings[i]; cin.get(); } cout<<"Thank you. You entered the following:\n" <<"Rating\t Book\n"; for(i=0;i<NUM;i++) { cout<<ratings[i]<<"\t"<<titles[i]<<endl; } return 0; }
所有的STL容器都提供了一些基本方法,其中包括size()——返回容器中元素数目、swap()——交换两个容器的内容、begin()——返回一个指向容器中第一个元素的迭代器、end()——返回一个表示超过容器尾的迭代器。
迭代器:广义指针。它是指针,也是可对其执行类似指针的操作的对象。通过将指针广义化为迭代器,让STL能够为各种不同的容器类提供统一的接口。迭代器是一个名为iterator的typedef,其作用域为整个类。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
vector<double>::iterator pd; vector<double> scores; pd=scores.begin(); *pd++; ++pd; vector<double>::iterator pd=scores.begin(); auto pd=scores.begin();
迭代器的行为就像指针,还有上面例子中的C++11自动类型推断很有用。
超尾:迭代器指向容器最后一个元素后面的那个元素。这与C-风格字符串最后一个字符后面的空字符类似,只是空字符是一个值,而“超过结尾“是一个指向元素的指针(迭代器)。end()成员函数标识超过结尾的位置。push_back()是一个方便的方法,它将元素添加到矢量末尾。这样做时,它将负责内存管理,增加矢量的长度,是指能够容纳新的成员。因此在编写或运行程序时,无需了解元素的数目。只要能够取得足够的内存,程序就可以根据需要增加长度。
erase()方法删除矢量中给定区间的元素。它接受两个迭代器参数,这些参数定义了要删除的区间。了解STL如何使用两个迭代器来定义区间至关重要。第一个迭代器指向区间的起始处,第二个迭代器位于区间终止处的后一个位置。例如,下述代码删除第一个和第二个元素,即删除begin()和begin()+1指向的元素。
scores.erase(scores.begin(),scores.begin()+2);
如果it1和it2是迭代器,则STL文档使用[p1,p2)来表示(不包括p2)的区间。
insert()方法的功能与erase()相反。它接受3个迭代器参数,第一个参数指定了新元素的插入位置,第二个和第三个地带其参数定义了被插入区间,该区间通常是另一个容器对象的一部分。例如,下面的代码将矢量new_v中除第一个元素外的所有元素插入到old_v矢量的第一个元素前面:
vector<int> old_v; vector<int> new_v; old_v.insert(old_v.begin(),new_v.begin()+1,new_v.end());
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//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(); cout<<num<<std::endl; 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);//复制 if(num>3) { books.erase(books.begin()+1,books.begin()+3);//删除第二个和第三个 cout<<"After erasure:\n"; for(pr=books.begin();pr!=books.end();pr++) ShowReview(*pr); books.insert(pr=books.begin(),oldlist.begin()+1,oldlist.begin()+2);//元素插入books第一个元素的前面,将oldllist中第二个元素插入。 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"; 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; while(std::cin.get()!='\n') continue; return true; } void ShowReview(const Review & rr) { std::cout<<rr.rating<<"\t"<<rr.title<<std::endl; }
STL从更广泛的角度定义了非成员(non-member)函数来执行这些操作,即不是为每个容器定义find()成员函数,而是定义了一个适用于所有容器类的非成员函数find()。STL有时也会定义一个成员函数。这是因为对有些操作来说,类特定算法的效率比通用算法高,因此,vector的成员函数swap()的效率比非成员函数swap()高,但非成员函数让您能够交换两个类型不同的容器的内容。
下面来看3个具有代表性的STL函数:for_each()、random_shuffle()和sort()。for_each()函数可用于很多容器类,它接受3个参数。前两个是定义容器中区间的迭代器,最后一个是指向函数的指针。for_each()函数将被指向的函数用于容器区间中的各个元素。被指向的函数不能修改容器元素的值。可以用for_each()函数来代替for循环。
for_each(books.begin(),books.end(),ShowReview);
这样可避免显式地使用迭代器变量。
循环和关系表达式中说过,基于范围的for循环是为用于STL而设计的。
double prices[5]={1,2,3,4,5}; for(double x:prices) cout<<x<<std::endl;
在这种for循环中,括号内的代码声明一个类型与容器存储的内容相同的变量,然后指出了容器的名称。接下来,循环体使用指定的变量依次访问容器的每个元素。
for_each(books.begin(),books.end(),ShowReview);
可将其替换为下述给予范围的for循环:
for(auto x:books) ShowReview(x);
根据book的类型(vector<Review>),编译器将推断出x的类型为Review,而循环将依次将books中的每个Review对象传递给ShowReview()。
不同于for_each(),基于范围的for循环可修改容器的内容,诀窍是指定一个引用参数。
void InflateReview(Review &r){ r.rating++; } for(auto &x:books) InflateReview(x);
Random_shuffle()函数接受两个指定区间的迭代器参数,并随机排列该区间中的元素。
random_shuffle(books.begin(),books.end());
与可用于任何容器类的for_each不同,该函数要求容器类允许随机访问,vector可以做到这一点。
sort()函数也要求容器支持随机访问。该函数有两个版本,第一个版本接受两个定义区间的迭代器参数,并使用为存储在容器中的支持类型元素定义的<运算符,对区间中的元素进行操作。如果容器元素是用户自定义的对象,则要使用sort(),必须定义能够处理该类型对象的operator<()函数。
注:与operator<相比,WorseThan()函数执行的对Review对象进行排序的工作不那么完整,如果两个对象的title相同,operator<()函数将按rating进行排序,而WorseThan()将他们视为完全相同。第一种视为全排序 (total ordering),第二种排序称为完整弱排序(strict weak ordering)。全排序顺序必然相同,完整弱排序中等价不相同。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <string> #include <vector> #include <algorithm> struct Review{ std::string title; int rating; }; bool operator<(const Review &r1, const Review &r2); bool worseThan(const Review &r1, const Review &r2); bool FillReview(Review & rr); void ShowReview(const Review &rr); void InflateReview(Review & r) { r.rating++; } int main() { using namespace std; vector<Review> books; Review temp; while(FillReview(temp)) books.push_back(temp); if(books.size()>0) { cout<<"Thank you. You entered the following " << books.size()<<" ratings:\n" <<"Rating\tBook\n"; for_each(books.begin(),books.end(),ShowReview); sort(books.begin(), books.end()); cout<<"Sorted by title:\nRating\tBook\n"; for_each(books.begin(),books.end(),ShowReview); sort(books.begin(),books.end(),worseThan); cout<<"Sorted by rating:\nRating\tBook\n"; for_each(books.begin(),books.end(),ShowReview); random_shuffle(books.begin(),books.end()); cout<<"After shuffling:\nRating\tBook\n"; for_each(books.begin(),books.end(),ShowReview); for (auto &x : books) InflateReview(x); cout<<"After InflateReview:\nRating\tBook\n"; for (auto x:books) ShowReview(x); } else cout<<"No entries. "; cout<<"Bye.\n"; return 0; } bool operator<(const Review & r1, const Review & r2) { if(r1.title<r2.title) return true; else if(r1.title==r2.title&&r1.rating<r2.rating) return true; else return false; } bool worseThan(const Review & r1, const Review & r2) { if(r1.rating<r2.rating) return true; else return false; } 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; while(std::cin.get()!='\n') continue; return true; } void ShowReview(const Review & rr) { std::cout<<rr.rating<<"\t"<<rr.title<<std::endl; }