string类和标准模板库
- string类
- 构造字符串
string类的构造函数
构造函数 | 描述 |
string(const char *) | 将string对象初始化为s指向的NBTS |
string(size_type n, char c) | 创建一个包含n个元素的string对象,其中每个元素都被初始化为字符c |
string(const string & str) | 将一个string对象初始化为string对象str(复制构造函数) |
string() | 创建一个默认的string对象,长度为0(默认构造函数) |
string(const char *s, size_type n) | 将string对象初始化为s指向的NBTS的前n个字符,即使 超过了NBTS结尾 |
template<class Iter> string(Iter begin, Iter end) | 将一个string对象初始化为区间[begin,end)内的字符,其中begin和end的行为就像指针,用于指定位置,范围包括begin在内,但不包括end |
string(const string & str, string size_type pos=0,size_type n=npos) | 将一个string对象初始化为对象str中从位置pos开始到结尾的字符,或从位置pos开始的第n个字符 |
string(string && str) noexcept | C++11新增,它将一个string对象初始化为string对象str,并可能修改str(移动构造函数) |
string(initializer_list<char> i1) | C++11新增,它将一个string对象初始化为初始化列表i1中的字符 |
*NBTS:以空字符结束的字符串——传统的C字符串。
- string类输入
对于类,很有帮助的另一点是,知道有哪些输入方式可用。 对于C-风格字符串,有3种方式:
char info[100];
cin>>info; //读取一个单词
cin.getline(info,100); //读取一行,丢弃\n
cin.get(info,100); //读取一行,将\n保存在队列中
对于string对象,有两种方式:
string stuff;
cin>>stuff; //读取一个单词
getline(cin,stuff); //读取一行,丢弃\n
两个版本的getline()都有一个可选参数,用于指定使用哪个字符来确定输入的边界:
cin.getline(info,100,’:’); //读到:,丢弃:
getline(stuff,’:’); //读到:,丢弃:
在功能在,它们之间的主要区别在于,string版本的getline()将自动调整目标string对象的大小,使之刚好能够存储输入的字符:
char fname[10];
string lname;
cin>>fname; //如果输入字符>9个将产生问题
cin>>lname; //能读取非常非常长的单词
cin.getline(fname,10); //可能会截断输入
getline(cin,fname); //不会截断
自动调整大小的功能让string版本的getline()不需要指定读取多少个字符的数值参数。
- 标准模板库
STL提供了一组表示容器、迭代器、函数对象和算法的模板。 容器是一个与数组类似的单元,可以存储若干个值。 STL容器是同质的,即存储的值的类型相同;算法是完成特定任务的处方;迭代器能够用来遍历容器的对象,与能够遍历数组的指针类似,是广义指针;函数对象是类似于函数的对象,可以是类对象或函数指针。 STL使得能够构造各种容器(包括数组、队列和链表)和执行各种操作(包括搜索、排序和随机排列)。
- 模板类vector
//vect1.cpp -- 介绍vector模板
#include<iostream>
#include<string>
#include<vector>
const int NUM = 5;
int main()
{
using std::vector;
using std::string;
using std::cin;
using std::cout;
vector<int>ratings(NUM);
vector<string>title(NUM);
cout << "You will do exactly as told. You will enter\n"
<< NUM << " book titles and your rating (0-10).\n";
int i;
for (int i = 0; i < NUM; i++)
{
cout << "Enter title #" << i + 1 << ": ";
getline(cin,title[i]);
cout << "Enter your rating (0-10): ";
cin>>ratings[i];
cin.get();
}
cout << "Thank you. You entered the following:\n"
<< "Rating\tBook\n";
for (i = 0; i < NUM; i++)
{
cout << ratings[i] << "\t" << title[i] << std::endl;
}
system("pause");
return 0;
}
程序运行结果:
You will do exactly as told. You will enter
5 book titles and your rating (0-10).
Enter title #1: TAN
Enter your rating (0-10): 8
Enter title #2: FAT
Enter your rating (0-10): 8
Enter title #3: YOUN
Enter your rating (0-10): 9
Enter title #4: NHUH
Enter your rating (0-10): 7
Enter title #5: BGFD
Enter your rating (0-10): 6
Thank you. You entered the following:
Rating Book
8 TAN
8 FAT
9 YOUN
7 NHUH
6 BGFD
- 可对矢量执行的操作
所有的STL容器都提供了一些基本方法,其中包括:
size(): 返回容器中元素数目
swap(): 交换两个容器的内容
begin(): 返回一个指向容器中第一个元素的迭代器
end(): 返回超过容器尾的迭代器
迭代器是一个广义指针。 事实上,它可以是指针,也可以是一个可对其执行类似指针的操作——如解除引用(如operator*())和递增(如operator++())——对象。 通过将指针广义化为迭代器,让STL能够为各种不同的容器类提供统一的接口。 每个容器类都定义了一个合适的迭代器,该迭代器的类型是一个名为iterator的typedef,其作用域为整个类。 例如,要为vector的double类型规范声明一个迭代器,可以这样做:
vector<double>::iterator pd; //pd是一个迭代器
假设scores是一个vector<double>对象:
vector<double>scores;
则可以使用迭代器pd执行这样的操作:
pd = scores.begin(); //让pd指针指向第一个元素
*pd=2.3; //对pd解除引用以及给第一个元素赋值
++pd; //让pd指向下一个元素
end()成员函数标识超过结尾的位置。 如果将迭代器设置为容器的第一个元素,并不断递增,则最终它将到达容器结尾,从而遍历整个容器的内容。
for(pd = scores.begin(); pd!=scores.end();pd++)
cout<<*pd<<endl;
所有容器都包含上述方法。 vector模板类也包含一些只有某些STL容器才有的方法。 push_back()是一个方便的方法,它将元素添加到矢量末尾。 这样做时,它将负责内存管理,增加矢量的长度,使之能够容纳新的成员。
vector<double> scores; //创建一个空的vector
double temp;
while(cin>>temp && temp>=0)
scores.push_back(temp);
cout<<”You entered “<<scores.size()<<” scores.\n”;
每次循环都给scores对象增加一个元素。
erase()方法删除矢量中给定区间的元素。 它接受两个迭代器参数,这些参数定义了要删除的区间。 第一个迭代器指向区间的起始处,第二个迭代器位于区间终止处的后一个位置。 例如,下述代码删除第一个和第二个元素:
scores.erase(scores.begin(),scores.begin()+2);
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());
//vect2.cpp -- 方法和迭代器
#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); //使用复制构造函数
if (num > 3)
{
//去掉2个元素
books.erase(books.begin() + 1, books.begin() + 3);
cout << "After erasure:\n";
for (pr = books.begin(); pr != books.end(); pr++)
ShowReview(*pr);
//插入一个元素
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;
//去除输入行余下元素
while (std::cin.get() != '\n')
continue;
return true;
}
void ShowReview(const Review & rr)
{
std::cout << rr.rating << "\t" << rr.title << std::endl;
}
程序运行结果:
Enter book title (quit to quit): Pride and Prejudice
Enter book rating: 8
Enter book title (quit to quit): War and Peace
Enter book rating: 9
Enter book title (quit to quit): Advanced Math
Enter book rating: 6
Enter book title (quit to quit): C++
Enter book rating: 7
Enter book title (quit to quit): quit
Thank you. You entered the following:
Rating Book
8 Pride and Prejudice
9 War and Peace
6 Advanced Math
7 C++
Reprising:
Rating Book
8 Pride and Prejudice
9 War and Peace
6 Advanced Math
7 C++
After erasure:
8 Pride and Prejudice
7 C++
After insertion:
9 War and Peace
8 Pride and Prejudice
7 C++
Swapping oldlist with books:
8 Pride and Prejudice
9 War and Peace
6 Advanced Math
7 C++
- 对矢量可执行的其他操作
以下介绍3个具有代表性的STL函数:for_each()、random_shuffle()和sort()。
for_each()函数可用于很多容器类,它接受3个参数。 前两个是定义容器中区间的迭代器,最后一个是指向函数的指针(更普遍地说,最后一个参数是一个函数对象)。 for_each()函数将被指向地函数应用于容器区间中地各个元素。 被指向地函数不能修改容器元素地值。 可以用for_each()函数来代替for循环。 例如:
vector<Review>::iterator pr;
for(pr = books.begin();pr!=books.end();pr++)
ShowReview(*pr);
可替换为:
for_each(books.begin(),books.end(),ShowReview);
random_shuffle()函数接受两个指定区间地迭代器参数,并随即排列该区间中地元素。 例如,下面地语句随机排列books矢量中所有元素:
random_shuffle(books.begin(),books.end());
与可用于任何容器类地for_each()不同,该函数要求容器类允许随机访问,vector类可以做到这一点。
sort()函数也要求容器支持随机访问。 该函数有两个版本,第一个版本接受两个定义区间地迭代器参数,并使用为存储在容器中的类型元素定义的<运算符,对区间中的元素进行操作。 例如,下面的语句按升序对coolstuff的内容进行排序,排序时使用内置的<运算符对值进行比较:
vector<int> coolstuff;
…
sort(coolstuff.begin(),coolstuff.end());
如果容器元素是用户定义的对象,要使用sort(),则必须定义能够处理该类型对象的operator<()函数。 例如,如果为Review提供了成员或非成员函数operator<(),则可以对包含Review对象的矢量进行排序。
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;
}
有了这样的函数后,就可以对包含Review对象(如books)的矢量进行排序了:
sort(books.begin(),books.end());
上述版本的operator<()函数是按title成员的字母顺序排序。 如果title成员相同,则按照rating排序。 然而,如果想按降序或是按rating(而不是title)排序,该如何办呢?可以使用另一种格式的sort()。 它接受三个参数,前两个参数也是指定区间的迭代器,最后一个参数是指向要使用的函数的指针(函数对象),而不是用于比较的operator<()。
bool WorseThan(const Review & r1, const Review &r2)
{
if (r1.rating < r2.rating)
return true;
else
return false;
}
有了这个函数后,就可以使用下面的语句将包含Review对象的books矢量按rating升序排列:
sort(books.begin(),books.end(),WorseThan);
//vect3.cpp -- 使用STL函数
#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);
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);
}
else
cout << "No entries.";
cout << "Bye.\n";
cin.get();
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;
}
程序运行结果:
Enter book title (quit to quit): Python
Enter book rating: 8
Enter book title (quit to quit): English
Enter book rating: 7
Enter book title (quit to quit): Math
Enter book rating: 8
Enter book title (quit to quit): Science
Enter book rating: 7
Enter book title (quit to quit): quit
Thank you. You entered the following 4 ratings:
Rating Book
8 Python
7 English
8 Math
7 Science
Sorted by title:
Rating Book
7 English
8 Math
8 Python
7 Science
Sorted by rating:
Rating Book
7 English
7 Science
8 Math
8 Python
After shuffling:
Rating Book
7 English
7 Science
8 Python
8 Math
Bye.
- 泛型编程
STL是一种泛型编程(generic programing)。 面向对象编程关注的是编程的数据方面,而泛型编程关注的是算法。 它们之间的共同点是抽象和创建可重用代码。
泛型编程旨在编写独立于数据类型的代码。
- 为何使用迭代器
迭代器使算法独立于使用的容器类型。
首先看一个在double数组中搜索特定值的函数:
double *find_ar(double * ar, int n, const double & val)
{
for (int i = 0; i < n; i++)
if (ar[i] == val)
return &ar[i];
return 0; //在C++11中,返回nullptr;
}
该函数使用下标来遍历数组。 可以用模板将这种算法推广到包含==运算符的、任意类型的数组。 尽管如此,这种算法仍然与一种特定的数据结构(数组)关联在一起。
下面来看搜索另一种数据结构——链表的情况。
struct Node
{
double item;
Node * p_next;
};
假设有一个指向链表第一个节点的指针,每个节点的p_next指针都指向下一个节点,链表最后一个节点的p_next指针被设置为0,则可以这样编写find_ll()函数:
Node * find(Node * head, const double & val)
{
Node * start;
for (start = head; start != 0; start = start->p_next)
if (start->item == val)
return start;
return 0;
}
同样,也可以使用模板将这种算法推广到支持==运算符的任何数据类型的算法。 然而,这种算法也是与特定的数据结构——链表关联在一起。
从实现细节上看,这两个find函数算法是不同的:一个使用数组索引来遍历元素,另一个则将start重置为start->p_next。 但从广义上说,这两种算法是相同的:将值依次与容器中的每个值进行比较,直到找到匹配的为止。
泛型编程旨在使用同一个find函数来处理数组、链表或任何其他容器类型。 即函数不仅独立于容器中存储的数据类型,而且独立于容器本身的数据结构。 模板提供了存储在容器中的数据类型的通用表示,因此还需要遍历容器中的值的通用表示,迭代器正是这样的通用表示。
可以这样重新编写find_arr()函数:
typedef double * iterator;
iterator find_ar(iterator ar, int n, const double & val)
{
for (int i = 0; i < n; i++, ar++)
if (*ar == val)
return ar;
return 0;
}
然后可以修改函数参数,使之接受两个指示区间的指针参数,其中的一个指向数组的起始位置,另一个指向数组的结尾:同时函数可以通过返回尾指针,来指出没有找到要找的值。
iterator find_ar(iterator begin,iterator end, const double & val)
{
iterator ar;
for (ar = begin; ar != end; ar++)
if (*ar == val)
return ar;
return end; //表示val没有找到
}
使用容器类时,无需知道其迭代器是如何实现的,也无需知道超尾是如何实现的,而只需知道它有迭代器,其begin()返回一个指向第一个元素的迭代器,end()返回一个指向超尾位置的迭代器即可。 例如,要打印vector<double>对象中的值,则可以这样做:
vector<double>::iterator pr;
for(pr = scores.begin(); pr!=scores.end();pr++)
cout<<*pr<<endl;
其中,下面的代码将pr的类型声明为vector<double>类的迭代器:
vector<double>class:
vector<double>::iterator pr;
- 迭代器类型
STL定义了5种迭代器: 输入迭代器、输出迭代器、正向迭代器、双向迭代器和随机访问迭代器。 例如,find()原型与下面类似:
输入迭代器:
输入迭代器可被程序用来读取容器中的信息。
输出迭代器:
简而言之,对于单通行、只读算法,可以使用输入迭代器;而对于单通行、只写算法,则可以使用输出迭代器。
正向迭代器:
正向迭代器既可以使得能够读取和修改数据,也可以使得只能读取数据:
int * pirw; //读写迭代器
const int * pir; //只读迭代器
双向迭代器:
双向迭代器具有正向迭代器的所有特性,同时支持两种(前缀和后缀)递减运算符。
随机访问迭代器:
随机访问迭代器具有双向迭代器的所有特性,同时添加了支持随机访问的操作(如指针增加运算)和用于对元素进行排序的关系运算符。
- 概念、改进和模型:
将指针用作迭代器:
迭代器是广义指针,而指针满足所有的迭代器要求。 迭代器是STL算法的接口,而指针是迭代器,因此STL算法可以使用指针来对基于指针的非STL容器进行操作。 例如,可将STL算法用于数组。 假设Receipts是一个double数组,并要按升序对它进行排序:
const int SIZE=10;
double Receipts[SIZE];
STL sort()函数接受指向容器第一个元素的迭代器和指向超尾的迭代器作为参数。 &Receipts[0](或Receipts)是第一个元素的地址,&Receipts[SIZE](或Receipts+SIZE)是数组最后一个元素后面的元素的地址。 因此,下面的函数调用对数组进行排序:
sort(Receipts, Receipts+SIZE);
copy()可以将数据从一个容器复制到另一个容器中。
int casts[10]={6,7,2,9,4,11,8,7,10,5};
vector<int> dice[10];
copy(casts,casts+10,dice.begin()); //将数组复制到矢量
现在,假设要将信息复制到显示器上。 如果有一个表示输出流的迭代器,则可以使用copy()。 STL为这种迭代器提供了ostream_iterator模板。
#include <iterator>
…
ostream_iterator<int, char> out_iter(cout,” ”);
out_iter迭代器现在是一个接口,让您能够使用cout来显示信息。 第一个模板参数(int)指出了被发送给输出流的数据类型;第二个模板参数(char)指出了输出流使用的字符类型。 构造函数的第一个参数(cout)指出了要使用的输出流;最后一个字符串参数是在发送给输出流的每个数据项后显示的分隔符。
可以这样使用迭代器:
*out_iter++=15; //工作效果如同cout<<15<<” “;
对于常规指针,这意味着将15赋给指针指向的位置,然后将指针加1。 但对于该ostream_iterator,这意味着将15和由空格组成的字符串发送到cout管理的输出流中,并为下一个输出操作做好了准备。 可以将copy()用于迭代器,如下所示:
copy(dice.begin(),dice.end(),out_iter); //将矢量复制到输出流
这意味着将dice容器的整个区间复制到输出流中,即显示容器的内容。
也可以不创建命名的迭代器,而直接构建一个匿名迭代器。 即可以这样使用适配器:
copy(dice.begin(),dice.end(),ostream_iterator<int,char>(cout,” ”));
iterator头文件还定义了一个istream_iterator模板,使istream输入可用作迭代器接口。 它是一个输入迭代器概念的模型,可以使用两个istream_iterator对象来定义copy()的输入范围:
copy(istream_iterator<int,char>(cin),istream_iterator<int,char>,dice.begin());
与ostream_iterator相似,istream_iterator也使用两个模板参数。 第一个参数指出要读取的数据类型,第二个参数指出输出流使用的字符类型。 使用构造函数参数cin意味着使用由cin管理的输入流,省略构造函数表示输入失败,因此上述代码从输入流中读取,直到文件结尾、类型不匹配或出现其他输入故障为止。
其他有用的迭代器:
反向打印容器的内容:vector类有一个名为rbegin()的成员函数和一个名为rend()的成员函数,前者返回一个指向超尾的反向迭代器,后者返回一个指向第一个元素的反向迭代器。
copy(dice.rbegin(),dice.rend(),out_iter); //反向显示
//copyit.cpp -- copy()和迭代器
#include<iostream>
#include<iterator>
#include<vector>
int main()
{
using namespace std;
int casts[10] = { 6,7,2,9,4,11,8,7,10,5 };
vector<int>dice(10);
//从数组向矢量进行复制
copy(casts, casts + 10, dice.begin());
cout << "Let the dice be cast!\n";
//创建一个ostream迭代器
ostream_iterator<int, char>out_iter(cout, " ");
//将矢量复制到output
copy(dice.begin(), dice.end(), out_iter);
cout << endl;
cout << "Implicit use of reverse iterator.\n";
copy(dice.rbegin(), dice.rend(), out_iter);
cout << endl;
cout << "Explicit use of reverse iterator.\n";
vector<int>::reverse_iterator ri;
for (ri = dice.rbegin(); ri != dice.rend(); ++ri)
cout << *ri << ' ';
cout << endl;
cin.get();
return 0;
}
程序运行结果:
Let the dice be cast!
6 7 2 9 4 11 8 7 10 5
Implicit use of reverse iterator.
5 10 7 8 11 4 9 2 7 6
Explicit use of reverse iterator.
5 10 7 8 11 4 9 2 7 6
另外三种迭代器(back_insert_iterator, front_insert_iterator和insert_iterator)通过将复制转换为插入。 插入将添加新的元素,而不会覆盖已有的数据,并使用自动内存分配来确保能够容纳新的信息。 back_insert_iterator将元素插入到容器尾部,而front_insert_iterator将元素插入到容器的前端。 最后,insert_iterator将元素插入到insert_iterator构造函数的参数指定的位置前面。
这些迭代器将容器类型作为模板参数,将实际的容器标识符作为构造函数参数。 也就是说,要为名为dice的vector<int>容器创建一个back_insert_iterator,可以这样做:
back_insert_iterator<vector<int>> back_iter(dice);
必须声明容器类型的原因是,迭代器必须使用合适的容器方法。
声明front_insert_iterator的方式与此相同。 对于insert_iterator声明,还需一个指示插入位置的构造函数参数:
insert_iterator<vector<int>> insert_iter(dice,dice.begin());
//inserts.cpp
#include<iostream>
#include<string>
#include<iterator>
#include<vector>
#include<algorithm>
void output(const std::string &s) { std::cout << s << " "; }
int main()
{
using namespace std;
string s1[4] = { "fine","fish","fashion","fate" };
string s2[2] = { "busy","bats" };
string s3[2] = { "silly","singers" };
vector<string>words(4);
copy(s1, s1 + 4, words.begin());
for_each(words.begin(), words.end(), output);
cout << endl;
//构造匿名back_insert_iterator对象
copy(s2, s2 + 2, back_insert_iterator<vector<string>>(words));
for_each(words.begin(), words.end(), output);
cout << endl;
//构造匿名insert_iterator对象
copy(s3, s3 + 2, insert_iterator<vector<string>>(words, words.begin()));
for_each(words.begin(), words.end(), output);
cout << endl;
cin.get();
return 0;
}
程序运行结果:
fine fish fashion fate
fine fish fashion fate busy bats
silly singers fine fish fashion fate busy bats
- 容器种类
vector:
该模板是在vector头文件中声明的。 简单地说,vector是数组的一种类表示,它提供了自动内存管理功能,可以动态地改变vector对象地长度,并随着元素的添加和删除而增大和缩小。 它提供了对元素的随机访问。 在尾部添加和删除元素的时间是固定的,但在头部或中间插入和删除元素的复杂度为线性时间。
除序列外,vector还是可反转容器(reversible container)概念的模型。 这增加了两个类方法:rbegin()和rend(),前者返回一个指向反转序列的第一个元素的迭代器,后者返回反转序列的超尾迭代器。 因此,如果dice是一个vector<int>容器,而Show(int)是显示一个整数的函数,则下面的代码将首先正向显示dice的内容,然后反向显示:
for_each(dice.begin(),dice.end(),Show); //正向显示
cout<<endl;
for_each(dice.rbegin(),dice.rend(),Show); //反向显示
cout<<endl;
vector模板是最简单的序列类型,除非其他类型的特殊优点能够更好地满足程序的要求,否则应默认使用这种类型。
deque:
deque模板类(在deque头文件中声明)表示双端队列(double-ended queue),通常被简称为deque。 在STL中,其实现类似于vector容器,支持随机访问。 主要区别在于,从deque对象的开始位置插入和删除元素的时间是固定的,而不像vector中那样是线性时间的。 所以,如果多数操作发生在序列的起始和结尾处,则应考虑使用deque数据结构。
list:
list模板类(在list头文件中声明)表示双向链表。 除了第一个和最后一个元素外,每个元素都与前后的元素相链接,这意味着可以双向遍历链表。 list和vector之间的关键区别在于,list在链表任意位置进行插入和删除的时间都是固定的。 因此,vector强调的是通过随机访问进行快速访问,而list强调的是元素的快速插入和删除。
list成员函数:
函数 | 说明 |
void merge(list<T,Alloc>&x) | 将链表x与调用链表合并。 两个链表必须已经排序。 合并后的经过排序的链表保存在调用链表中,x为空。 这个函数的复杂度为线性时间 |
void remove(const T & val) | 从链表中删除val的所有实例。 这个函数的复杂度为线性时间 |
void sort() | 使用<运算符对链表进行排序;N个元素的时间复杂度为NlogN |
void splice(iterator pos,list<T,Alloc>x | 将链表x的内容插入到pos的前面,x将为空。 这个函数的复杂度为固定时间。 |
void unique() | 将连续的相同元素压缩为单个元素。 这个函数的复杂度为线性时间 |
//list.cpp
#include<iostream>
#include<list>
#include<iterator>
#include<algorithm>
void outint(int n) { std::cout << n << " "; }
int main()
{
using namespace std;
list<int>one(5, 2); //含有5个2的链表
int stuff[5] = { 1,2,4,8,6 };
list<int>two;
two.insert(two.begin(), stuff, stuff + 5);
int more[6] = { 6,4,2,4,6,5 };
list<int>three(two);
three.insert(three.end(), more, more + 6);
cout << "List one:";
for_each(one.begin(), one.end(), outint);
cout << endl << "List two: ";
for_each(two.begin(), two.end(), outint);
cout << endl << "List three: ";
for_each(three.begin(), three.end(), outint);
three.remove(2);
cout << endl << "List three minus 2s: ";
for_each(three.begin(), three.end(), outint);
three.splice(three.begin(), one);
cout << endl << "List three after splice: ";
for_each(three.begin(), three.end(), outint);
cout << endl << "List one: ";
for_each(one.begin(), one.end(), outint);
three.unique();
cout << endl << "List three after unique: ";
for_each(three.begin(), three.end(), outint);
three.sort();
three.unique();
cout << endl << "List three after sort & unique: ";
for_each(three.begin(), three.end(), outint);
two.sort();
three.merge(two);
cout << endl << "Sorted two merged into three: ";
for_each(three.begin(), three.end(), outint);
cout << endl;
cin.get();
return 0;
}
程序运行结果:
List one:2 2 2 2 2
List two: 1 2 4 8 6
List three: 1 2 4 8 6 6 4 2 4 6 5
List three minus 2s: 1 4 8 6 6 4 4 6 5
List three after splice: 2 2 2 2 2 1 4 8 6 6 4 4 6 5
List one:
List three after unique: 2 1 4 8 6 4 6 5
List three after sort & unique: 1 2 4 5 6 8
Sorted two merged into three: 1 1 2 2 4 4 5 6 6 8 8
forward_list(C++ 11):
C++11新增了容器类forward_list,它实现了单链表。
queue:
queue模板类(在头文件queue中声明)是一个适配器类。 由前所述,ostream_iterator模板就是一个适配器,让输出流能够使用迭代器接口。 同样,queue模板让底层类(默认为deque)展示典型的队列接口。
queue的操作:
方法 | 说明 |
bool empty()const | 如果队列为空,则返回true;否则返回false |
size_type size()const | 返回队列中元素的数目 |
T& front() | 返回指向队首元素的引用 |
T & back() | 返回指向队尾元素的引用 |
void push(const T &x) | 在队尾插入x |
void pop() | 删除队首元素 |
priority_queue:
priority queue模板类(在queue文件中声明)是另一个适配器,它支持的操作与queue相同。 两者之间的主要区别在于,在priority queue中,最大的元素被移到队首。
stack:
与queue类似,stack(在头文件stack中声明)也是一个适配器。
stack的操作:
方法 | 说明 |
bool empty()const | 如果栈为空,则返回true;否则返回false |
size_type size()const | 返回栈中元素的数目 |
T& top() | 返回指向栈顶元素的引用 |
void push(const T &x) | 在栈顶部插入x |
void pop() | 删除栈顶元素 |
array:
它并非STL容器,因为其长度是固定的。 可将很多STL算法用于array对象,如copy()和for_each()。
- 函数对象
很多STL算法都使用函数对象——也叫函数符(functor)。 函数符是可以以函数方式与()结合使用的任意对象。 这包括函数名、指向函数的指针和重载了()运算符的对象(即定义了函数operator()()的类)。 例如:
class Linear
{
private:
double slope;
double y0;
public:
Linear(double s1_=1,double y_=0):slope(s1_),y0(y_){}
double operator()(double x) { return y0 + slope * x; }
};
这样,重载的()运算符将使得能够像函数那样使用Linear对象:
Linear f1;
Linear f2(2.5, 10.0);
double y1 = f1(12.5);
double y2 = f2(0.4);
其中y1将使用表达式0+1*12.5来计算,y2将使用表达式10.0+2.5*0.4来计算。 在表达式y0+slope*x中,y0和slope的值来自对象的构造函数,而x值来自operator()()参数。