C++ Primer 第9章顺序容器

9.1 顺序容器概述

C++ Primer 第9章顺序容器_第1张图片
C++ Primer 第9章顺序容器_第2张图片

确定使用哪种顺序容器

通常使用vector是最好的选择
C++ Primer 第9章顺序容器_第3张图片

9.2 容器库概述(本节所有容器均适用)

C++ Primer 第9章顺序容器_第4张图片

对容器可以保存的元素类型的限制

  • 有些类没有提供默认构造函数,我们可以定义一个这种类型对象的容器,但我们在构造这种容器时不能只传递给它一个数目参数.
    在这里插入图片描述

C++ Primer 第9章顺序容器_第5张图片
C++ Primer 第9章顺序容器_第6张图片

9.2.1 迭代器

  • 与容器一样,迭代器有着公共的接口:如果一个迭代器提供某个操作,那么所有提供相同操作的迭代器对这个操作的实现方式都是相同的.但是有一个例外:forward_list迭代器不支持递减运算符

迭代器范围

  • begin和end first和last(指向尾元素之后的元素)
    C++ Primer 第9章顺序容器_第7张图片

使用左闭合范围蕴含的编程假定

C++ Primer 第9章顺序容器_第8张图片
C++ Primer 第9章顺序容器_第9张图片

9.2.2 容器类型成员

  • 为了索引int的vector中的元素使用迭代器:vector::iterator
  • 为了读取string的list中的元素:应该使用的类型是:list::value_type
  • 为了写入数据,应该使用的类型是;list::reference

9.2.3 begin和end成员

C++ Primer 第9章顺序容器_第10张图片

  • 不以c开头的函数都是被重载过的.也就是说,实际上有两个名为begin的成员,一个是const成员返回const_iterator,另外一个返回的是非常量成员iterator.当我们对一个非常量对象调用这些成元返回普通的.
    在这里插入图片描述

9.2.4 容器定义和初始化

C++ Primer 第9章顺序容器_第11张图片

将一个容器初始化为另外一个容器的拷贝

  • 将一个新容器创建为另外一个容器的拷贝的方法有两种:
    • 可以直接拷贝整个容器(除array)
    • 拷贝有一个迭代器对指定的范围
      在这里插入图片描述
    list<string> ls = {"1","2","3"};
    vector<const char *> vs = {"4","5","6"};

    list<string> list2(vs.begin(),vs.end());

在这里插入图片描述

列表初始化

与顺序容器大小相关的构造函数

C++ Primer 第9章顺序容器_第12张图片

标准库array具有固定大小

C++ Primer 第9章顺序容器_第13张图片
C++ Primer 第9章顺序容器_第14张图片

9.2.5 赋值和swap

C++ Primer 第9章顺序容器_第15张图片

使用assign(仅顺序容器)

C++ Primer 第9章顺序容器_第16张图片
C++ Primer 第9章顺序容器_第17张图片
C++ Primer 第9章顺序容器_第18张图片
C++ Primer 第9章顺序容器_第19张图片

9.2.6 容器大小操作

C++ Primer 第9章顺序容器_第20张图片

9.2.7 关系运算符

  • 除了无序关联容器外的所有容器都支持关系运算符.关系运算符左右两边的运算对象必须是相同的容器,且必须保存相同类型的元素.
  • 运算符的工作方式与string的关系运算类似.

容器的关系运算符使用元素的关系运算符完成比较

只有当其元素类型也定义了相应的比较运算符时,我们才可以使用关系运算符来比较两个容器

9.3 顺序容器操作(顺序容器特有操作)

9.3.1 向顺序容器添加元素

C++ Primer 第9章顺序容器_第21张图片

  • 在一个vector或string的尾部之外的任何位置,或是deque的首尾之外的任何位置添加元素都需要移动元素

使用push_back(除array和forward_list之外)

C++ Primer 第9章顺序容器_第22张图片

使用push_front

  • list,forward_list,deque支持
  • deque和vector提供随机访问元素的能力

在容器的特定位置添加元素(insert)

  • insert(iterator,element)

插入范围内元素

  • 传递的迭代器不能指向添加新元素的目标容器
  • 新标准之下,接受元素个数或范围的insert版本返回指向第一个新加入元素的迭代器

使用insert返回值

C++ Primer 第9章顺序容器_第23张图片

使用emplace操作

C++ Primer 第9章顺序容器_第24张图片
C++ Primer 第9章顺序容器_第25张图片
向一个vector,string,deque插入元素会使现有指向容器的迭代器,引用,指针生效.这时就应该结合insert返回的元素来进行操作

9.3.2 访问元素

C++ Primer 第9章顺序容器_第26张图片
C++ Primer 第9章顺序容器_第27张图片

访问成员函数返回的是引用

C++ Primer 第9章顺序容器_第28张图片

小标操作和安全的随机访问

  • 提供下标运算符的容器(string,vector,deque和arrate都提供下标运算符).
  • 如果我们希望确保下标是合法的,可以使用at成员函数.如果at下标越界,at会抛出一个out_of_range异常

9.3.3 删除元素

C++ Primer 第9章顺序容器_第29张图片

pop_front和pop_back成员函数

  • 与vector和string不支持push_front一样,这些类型也不支持pop_front
  • 不能对一个空容器执行1弹出操作
while(!ilist.empty()){
}

从容器内部删除一个元素(erase)

删除多个元素

slist.clear()
slist.erase(slist.begin(),slist.end())

在这里插入图片描述

int main()
{
    int a[] = {0,1,1,2,3,5,8,13,21,55,89};
    vector<int> vi ;
    list<int> li;
    vi.assign(a,a+11); // 将数组上的元素拷贝到vector中
    li.assign(a,a+11);

    vector<int>::iterator vit = vi.begin();

    while (vit != vi.end()){
        if(*vit %2 == 0){
            vit = vi.erase(vit);
        } else{
            vit++;
        }
    }

    vit = vi.begin();
    while (vit != vi.end()){
        cout<<*vit<<ends;
        vit++;
    }


    return 0;
}

9.3.4 特殊的forward_list操作

C++ Primer 第9章顺序容器_第30张图片

  • 由于forwar_list是一个单向列表,删除或者添加元素需要这个元素的前驱才能操作,但是在一个单链表中没有简单的方法获得一个元素的前驱节点,所以删除和添加元素操作都是通过改变给定元素之后的元素来完成的。
  • 由于这些操作与其他容器上的操作不同,forward_list并未定义insert,emplace,erase而是定义了insert_after,emplace_after,erase_after的操作。
    C++ Primer 第9章顺序容器_第31张图片
  • 当在forward_list中添加或删除元素时,我们必须关注两个迭代器,一个指向我们要处理的元素,另一个执行其前驱。

C++ Primer 第9章顺序容器_第32张图片

int main() {
    int a[] = {0, 1, 1, 2, 3, 5, 8, 13, 21, 55, 89};
    forward_list<int> fi;
    fi.assign(a, a + 11); // 将数组上的元素拷贝到forward_list中

    int x1 = 21, x2 = 2; //查找x1并将x2插入到紧挨x1之后的位置

    forward_list<int>::iterator it = fi.before_begin();//返回第一个元素前面的迭代器
    forward_list<int>::iterator nestIt = fi.begin();//返回首元素

    it = fi.begin();
    while (it != fi.end()) {
        cout << *it << ends;
        it++;
    }
    it = fi.before_begin();
    cout<<endl;
    
    //查找x1
    while (nestIt != fi.end()) {
        if (*nestIt == x1) {
            fi.erase_after(it);  //删除it之后的元素,it在nextIt的前面
            break;
        } else {
            it++;
            nestIt++;
        }
    }

    it = fi.begin();
    while (it != fi.end()) {
        cout << *it << ends;
        it++;
    }

    nestIt = fi.begin();
    //插入x1到x2的位置
    while (nestIt != fi.end()) {
        if (*nestIt == x2) {
            fi.insert_after(nestIt, x1);
            break;
        } else {
            nestIt++;
        }
    }


    cout << endl;
    it = fi.begin();
    //插入x1到x2的位置
    while (it != fi.end()) {
        cout << *it << ends;
        it++;
    }
/*
 * 0 1 1 2 3 5 8 13 21 55 89
0 1 1 2 3 5 8 13 55 89
0 1 1 2 21 3 5 8 13 55 89
 */
    return 0;
}

9.3.5 改变容器大小(resize)

C++ Primer 第9章顺序容器_第33张图片
C++ Primer 第9章顺序容器_第34张图片

  • 对于元素是类类型,则单参数resize版本要求改类型必须提供一个默认构造函数。

9.3.6 容器操作可能是迭代器失效

  • 向容器中添加元素和从容器中删除元素的操作可能会使指向元素的指针,引用或者迭代器失效。而一个失效的指针,引用或迭代器将不再表示任何元素
    C++ Primer 第9章顺序容器_第35张图片
  • 容器是vector或者string的情况下,在向容器添加元素后,如果容器扩容,即容器的存储空间重新分配,则指向容器的迭代器失效
  • 容器是vector或者string的情况下,在向容器添加元素后,如果容器未扩容,则指向插入位置之前的元素的迭代器有效,但指向插入位置之后元素的迭代器将会失效,这是因为插入位置后的元素次序发生变化使得原本指向某元素的迭代器不再指向希望指向的元素。
  • 容器是deque的情况下,插入到任何位置都会导致迭代器。
  • 容器是list和forward_list的情况下,插入到任何位置,指向容器的迭代器仍然有效,
    C++ Primer 第9章顺序容器_第36张图片

编写改变容器的循环程序

C++ Primer 第9章顺序容器_第37张图片

  • insert在给定位置之插入新元素,然后返回指向新元素的迭代器,所以必须加两次,越过插入的新元素和正在处理的元素

不要保存end返回的迭代器

在这里插入图片描述
如果在一个循环中插入,删除deque或vector中的元素,不要缓存end返回的迭代器’
C++ Primer 第9章顺序容器_第38张图片
C++ Primer 第9章顺序容器_第39张图片

第一题:

  • list和forward_list与其他容器的一个不同是,迭代器不支持加减运算,因为链表中的元素并非在内存中联系存储,因此无法通过地址的加减在元素间远距离移动。因此,应多次调用++来实现与迭代器加法相同的效果。
  • list和forward_list支持++不支持+=`
    vector<int> vi = {0,1,2,3,4,5,6,7,8,9};

    auto iter = vi.begin();
    while(iter != vi.end())
    {
        if(*iter % 2)
        {
            iter = vi.insert(iter,*iter);
            iter+=2;
            cout<<1<<endl;
        }
        else
        {
            iter = vi.erase(iter); //删除之后返回我们删除的元素之后的元素
        }
    }
int main() {
    list<int> vi = {0,1,2,3,4,5,6,7,8,9};

    auto iter = vi.begin();
    while(iter != vi.end())
    {
        if(*iter % 2) //奇数
        {
            iter = vi.insert(iter,*iter);
            iter++;
            iter++;
        }
        else
        {
            iter = vi.erase(iter); //删除之后返回我们删除的元素之后的元素
        }
    }

    iter = vi.begin();
    while (iter != vi.end()){
        cout<<*iter<<ends;
        iter++;
    }
    return 0;
}
/删除奇数,复制偶数
int main() {
    
    //对于forward_list删除元素时,由于是单链表结构,需要维护前驱和后继两个迭代器
    forward_list<int> vi = {0,1,2,3,4,5,6,7,8,9};

    auto iter = vi.begin();
    auto prev = vi.before_begin();
    while(iter != vi.end())
    {
        if(*iter % 2) //奇数
        {
            iter = vi.insert_after(iter,*iter); //返回插入的元素
            prev = iter;
            iter++;
        }
        else
        {
            iter = vi.erase_after(prev); //删除之后返回我们删除的元素之后的元素
        }
    }

    iter = vi.begin();
    while (iter != vi.end()){
        cout<<*iter<<ends;
        iter++;
    }
    return 0;
}

第二题

  • 编译器处理形参的顺序可能是不同的,由右向左或者有左向右都有可能。

9.4 vector对象是如何增长的

管理容量的成员函数

C++ Primer 第9章顺序容器_第40张图片
C++ Primer 第9章顺序容器_第41张图片

capacity和size

  • capacity是在不分配内存空间的前提下它最多可以保存多少元素,size是指它已经保存的元素的数目。
  • 实际上只要没有操作需求超过vector的容量,vector就不能重新分配内存空间

9.5 额外的string操作

9.5.1 构造string的其他方法

C++ Primer 第9章顺序容器_第42张图片
C++ Primer 第9章顺序容器_第43张图片

substr操作

在这里插入图片描述

习题

C++ Primer 第9章顺序容器_第44张图片

9.5.2 改变string的其他方法

C++ Primer 第9章顺序容器_第45张图片

append和replace函数

C++ Primer 第9章顺序容器_第46张图片
C++ Primer 第9章顺序容器_第47张图片
C++ Primer 第9章顺序容器_第48张图片

改变string的多种重载函数

在这里插入图片描述

void test(string &s, string& oldVal, string& newVal)
{
	if (!s.size())
		return; //s为空
	string::iterator its = s.begin();
	int j = 0; //记录正在遍历的s的位置
	while (its != s.end()) {

		if (*its == oldVal[0] && j < s.size())
		{
			cout << "找到首元素" << endl;
			string::iterator oldIt = oldVal.begin();
			while (oldIt !=oldVal.end() && *its == *oldIt && j< s.size())
			{
				oldIt++;
				its++;
			}
			if (oldIt == oldVal.end())
			{ //s中有oldval并找到其位置
				cout << "找到了" << endl;
				s.erase(j,oldVal.size());
				s.insert(j, newVal);
				break;
			}
		
		}
		else
		{
			its++;
			j++;
		}
	
	}
}
int main(int argc, char** argv)
{
	string s = "newtho";
	string old = "tho";
	string news = "news";
	test(s, old, news);
	cout << s << endl;   //newsnew
	return 0;
}

9.5.3 string搜索操作

C++ Primer 第9章顺序容器_第49张图片
C++ Primer 第9章顺序容器_第50张图片

指定在哪里开始搜索

在字符串中循环的搜索字符串出现的所有位置


void test(string &s, string& findStr)
{
	string::size_type pos = 0;

	while ((pos = s.find_first_of(findStr,pos)) != string::npos)
	{
		cout << "pos = " << pos<<"Element is " << s[pos]<< endl;
		++pos;
	}
}

s.find_first_of(str)查找的是str中的任意一个字符在s首次出现的位置

逆向搜索

rfind

习题

for-each语句遍历string会乱码

string::size_type pos = 0;
	string  str = "qwertyuiopasdfghjklzxcvbnm";
	string::size_type len = str.size();
	 for(int i =0;i< len;i++)
	 {	
		 str += toupper(str[i]);
		 cout << str[i] << "  " << toupper(str[i]) << " " << str << endl;
		 
	 }
	 cout << str << endl;

9.5.4 compare函数

C++ Primer 第9章顺序容器_第51张图片

9.5.5 数值转换

在这里插入图片描述
C++ Primer 第9章顺序容器_第52张图片

9.5 容器适配器

C++ Primer 第9章顺序容器_第53张图片

定义一个适配器

C++ Primer 第9章顺序容器_第54张图片

栈适配器

C++ Primer 第9章顺序容器_第55张图片

队列适配器

C++ Primer 第9章顺序容器_第56张图片
C++ Primer 第9章顺序容器_第57张图片

你可能感兴趣的:(c++,java,开发语言)