顺序容器
顺序容器基本操作:http://blog.csdn.net/fancynece/article/details/79193881
我们已经对容器的公有操作和顺序容器的操作有了一定的了解,接下来,我们要进一步学习顺序容器。
一、vector、string对象的增长
通常情况下,我们不必关心一个标准库类型是如何实现的,但是对于vector和string来说, 部分实现渗透到了接口中。
当我们向vector和string中添加元素时,若容器没有空间容纳新元素,那么不可能随意找到一个位置存放(因为元素必须连续存储)。此时,容器必须分配新的内存空间,将原来的元素拷贝并添加新元素,再释放原有空间。若每次添加元素都要进行分配内存,将会十分低效。
为了提高效率,我们规定,向容器中添加元素时,如果容器没有空间容纳新元素,那么会分配给它大于需求的内存,留作备用。
标准库给我们提供了可以管理容量的成员函数。
. | |
---|---|
c.capacity( ) | 容器在不扩张内存的情况下,可以容纳多少元素 |
c.reserve(n) | 将容器预先分配的内存设置为n |
c.shrink_to_fit( ) | 将capacity减少到与size同大小 |
其中,capacity和reserve只适用于vector、string,shrink_to_fit适用于vector、string、deque。
对于c.reserve(n);
当 需求大小 > 当前容量 时,reserve会分配至少和需求一样大的容量(可能更大)。而当 需求大小 <= 当前容量 时,reserve什么也不做。
也就是说,reserve不会减少容器的内存空间。与此不同的是,之前了解到的resize()函数,仅仅改变容器的元素, 不会改变容器的内存空间。
capacity和size
capacity是 在不重新分配内存时,容器可以容纳元素的个数。
size是 容器当前已保存的元素个数。
vector<int> ve;
//size为0,capacity依赖于具体实现
cout << "size:" << ve.size()
<< "capacity:" << ve.capacity() << endl;
for (vector<int>::size_type i = 0; i != 14; ++i)
ve.push_back(i);
//size为14,capacity依赖于具体实现
cout << "size:" << ve.size()
<< "capacity:" << ve.capacity() << endl;
//预分配30个内存空间
ve.reserve(30);
cout << "size:" << ve.size()
<< "capacity:" << ve.capacity() << endl;
//用完这些空间
while (ve.size() != ve.capacity())
ve.push_back(1);
cout << "size:" << ve.size()
<< "capacity:" << ve.capacity() << endl;
//再添加元素
ve.push_back(2);
cout << "size:" << ve.size()
<< "capacity:" << ve.capacity() << endl;
//归还内存
ve.shrink_to_fit();
cout << "size:" << ve.size()
<< "capacity:" << ve.capacity() << endl;
运行结果如下图所示:
二、额外的string操作
在之前的学习中http://blog.csdn.net/fancynece/article/details/79084054,
我们已经对string有了初步了解,而作为容器的一种,string除了顺序容器所提供的操作之外,还有许多额外的操作,一般与字符数组、下标有关。
1. 构造string的其他方法
. | |
---|---|
string s(cp,n); | cp为数组,s为cp的前n个字符 |
string s(s1,pos); | s1为字符串,s为字符串s1自pos开始的字符 |
string s(s1,pos,n); | s1为字符串,s为字符串s1自pos开始的n个字符 |
const char *cp = "Hello,world!";
char noNull[] = { 'H','i' ,'!'};
string s1(cp); //Hello,world!
string s2(noNull, 2); //Hi
string s3(cp + 6, 5); //world
string s4(s1, 6, 5); //world
string s5(s1, 6); //world!
substr操作
. | |
---|---|
s.substr(pos,n); | 返回一个字符串,由字符串s自pos开始的n个字符构成,pos默认为0,n默认为s.size( ) - pos |
//s1为"Hello,world!"
cout << s1.substr(0, 5) << endl; //Hello
cout << s1.substr(6) << endl; //world!
cout << s1.substr() << endl; //Hello,world!
2. 改变string的其他方法
. | |
---|---|
s.insert(pos,args); | 在pos之前插入args,pos可以是下标、迭代器。下标返回s的引用,迭代器返回第一个插入字符的迭代器 |
s.erase(pos,n); | 删除从pos开始的n个元素,若省略n,则删除pos开始的所有字符。返回s的引用 |
s.assign(args); | 将s替换为args |
s.append(args); | 将args追加到s |
s.replace(b,e,args); | 删除迭代器b和e范围的字符,替换为args |
s.replace(pos,n,args); | 删除pos开始的n个字符,替换为args |
其中,args可以是下列形式之一。
- str (字符串)
- str,pos,n (字符串从pos开始的n个字符)
- cp,n (数组cp的前n个字符)
- cp (数组cp)
- n,c (n个字符c)
- b,e (迭代器b和e范围内的字符)
- 初始化列表
我们可以看出,assign和append操作无须给出位置参数,因为assign总是全部替换,而append总是在末尾追加。而需要给出位置参数的函数,可以是迭代器,也可以是下标。
归根结底,string的操作特殊在 它可以接受字符数组为参数,可以接受下标为参数,此前顺序容器中必须由一对迭代器表示的范围,也可由起始位置和长度来表示。
const char c[] = { 'l','o','l','i','t','a',',' };
string temp = "My sunshine";
string s;
s.insert(0, "My love,"); //下标
s.insert(s.size(),c,7); //下标
s.append(temp,0);
cout << s << endl;
s.erase(0, 3);
cout << s << endl;
s.replace(12, 6, 4, 'o');
cout << s << endl;
3. string搜索操作
string提供了6个不同的搜索函数,每个函数都有4个重载版本。若找到则返回匹配发生位置的下标(string::size_type类型),若找不到则返回string::npos。
. | |
---|---|
s.find(args) | 查找s中args第一次出现的位置 |
s.rfind(args) | 查找s中args最后一次出现的位置 |
s.find_frist_of(args) | 在s中查找args中任何一个字符第一次出现的位置 |
s.find_last_of(args) | 在s中查找args中任何一个字符最后一次出现的位置 |
s.find_frist_not_of(args) | 在s中查找第一个不在args中的字符 |
s.find_last_not_of(args) | 在s中查找最后一个不在args中的字符 |
其中,args可以是下列之一。
- c,pos (从位置pos开始查找字符c,pos默认为0)
- s2,pos (从位置pos查找字符串s2,pos默认为0)
- cp,pos (从位置pos查找字符数组cp,pos默认为0)
- cp,pos,n (从位置pos查找字符数组的前个字符,pos和n无默认值)
string name("fancynaomi");
cout << name.find("fancy") << endl;
cout << name.find('n', 4) << endl;
string ch("suorn");
cout << name.find_first_of(ch) << endl; //name中第一个在ch中的为n
cout << name.find_last_of(ch) << endl; //name中最后一个在ch中的为o
cout << name.find_first_not_of(ch) << endl; //name中第一个不在ch中的为f
cout << name.find_last_not_of(ch) << endl; //name中最后一个不在ch中的为i
查找一个字符串中的所有子串。
string ct = "My name is fancy.My bro is cute.";
string::size_type st = 0;
while ((st = ct.find("is", st)) && st != string::npos) {
cout << "在" << st << "处发现" << "is" << endl;
++st;
}
4. compare函数
除了关系运算符之外,标准库string还提供了一组compare函数,根据 s 是等于、大于、小于参数指定的字符串,返回0、正数、负数。
可以比较两个字符串、一个字符串和一个字符数组,并且都可以比较一部分字符。
s.compare重载的几种形式。
- s.compare(s1): 比较s和s1
- s.compare(pos,n,s1): 将 s中从pos开始的n个字符 与s2 比较
- s.compare(pos,n,s1,pos1,n1): 将 s从pos开始的n个字符 与 s1从pos1开始的n1个字符 比较
- s.compare(cp): 比较s和字符数组cp
- s.compare(pos,n,cp): 将 s中从pos开始的n个字符 与cp 比较
- s.compare(pos,n,cp,n1): 将 s中从pos开始的n个字符 与cp的前n1个字符 比较
5. 数值转换
新标准引入了多个函数,实现数值数据和string之间的转换。string可以和任何算术类型进行转换。当string不能转换为一个数值时,会抛出异常。
. | |
---|---|
to_string(value); | value为任意算术类型 |
stoi(s,p,b); | 将字符串s转换为int类型。p是size_t的指针,保存s中第一个非数值字符的下标,默认不保存。b是转换所用的基数。相同的,也可以转换为long、unsigned long、long long、unsigned long long。 |
stof(s,p); | 将字符串s转换为float类型。p是size_t的指针,保存s中第一个非数值字符的下标,默认不保存。相同的,也可以转换为double、long double。 |
三、容器适配器
除了顺序容器外,标准库还定义了三个顺序容器适配器:stack、queue、priority_queue(优先队列)。
什么是适配器?适配器是一种机制,能使某种事物的行为看起来像另一种事物。容器、迭代器、函数都有适配器。
例如,stack适配器接受一个顺序容器,并使其操作起来像一个stack一样。
容器适配器支持的操作如下。
. | |
---|---|
size_type | |
value_type | 元素类型 |
container_type | 底层容器类型 |
A a; | 创建空适配器 |
A a(b); | 创建适配器,带有容器b的拷贝 |
== != > >= < <= | 返回底层容器的比较结果 |
a.empty() | 判空 |
a.size() | 返回a中元素数目 |
swap(a,b) | 交换a和b,类型必须相同 |
1. 定义适配器
默认情况下,stack和queue是基于deque实现的,默认容器类型为deque。而priority_queue是基于vector实现的,默认容器类型为vector。但是可以显示地改变容器类型。
- 默认构造函数
stack<int> stk;
其中stk可以接受一个deque<int>
类型的对象。 - 将顺序容器作为第二个类型参数,重载默认容器类型。
vector<string> vs(5, "st");
stack<string, vector<string>> stk(vs);//stack现在为vector的适配器
所有的适配器都要求容器可以添加、删除元素,因此适配器不能构造在array、forward_list上。(array不可增删元素,forward_list不可增删尾元素)。
stack只要求push_back、pop_back、back操作,可以使用除array和forward_list外的所有容器。
queue要求push_back、pop_front、back、front操作,因此只能使用deque和list。
priority_queue要求push_back、pop_back、front、随机访问操作,因此只能使用deque、vector。
2. 栈适配器
. | |
---|---|
s.pop(); | 删除栈顶元素,不返回该元素的值 |
s.top(); | 返回栈顶元素的值,不删除元素 |
s.push(item); | 压入元素 |
s.empalce(args); | 压入由args构造的元素 |
3. 队列适配器
queue和priority_queue定义在queue头文件中。
. | |
---|---|
q.pop( ); | 删除queue首元素/priority_queue优先级最高的元素,不返回元素 |
q.front( ); | 返回首元素/尾元素,不删除元素 |
q.back( ); | 只适用于queue |
q.top( ); | 只适用于priority_queue,返回优先级最高的元素,不删除元素 |
q.push(item); | 压入元素 |
q.emplace(args); | 压入由args构造的元素 |