C++primer(第五版)第九章 顺序容器(容器的运用及其部分习题解答,C++11特性总结,重点章节内容较多)

顺序容器:为程序员提供了控制元素存储和访问顺序的能力。(无序容器)

          1.顺序容器的概述

          A.顺序容器的类型

          vector:可变大小数组。支持快速随机访问。在尾部之外的位置插入或删除元素可能很慢。

         deque:双端队列。支持快速随机访问。在头尾位置插入/删除速度很快。

          list:双向链表。只支持双向顺序访问。在list中任何位置进行插入/删除操作速度都很快。

         forword_list:单向链表。只支持单向顺序访问。在链表的任何位置进行插入/删除操作速度都很快。(无size())

         array:固定大小数组。支持快速随机访问。不能添加和删除元素。

string:与vector相似的容器,但专门用于保存字符。随机访问快。在尾部插入/删除速度快。

根据以上顺序容器的类型介绍,从而习题9.1答案就很简单了:list(字母序是任何位置插入)、deque(头部尾部插入/删除)、vector(可变容器)。

 2.容器库

            A.迭代器

           迭代器中beigin是指容器首元素,end是指容器的最后一个元素的后一个位置;

           B.容器的拷贝

           容器的拷贝有两种操作方法:一是直接拷贝整个容器,二是拷贝一个迭代器对指定的元素范围。前者需要两个容器的类型和元素类型必须匹配;后者不要求容器类型相同元素类型也可不同,但需要元素转换。

例子:

list<string> authors ={"Milton","Shakespeare","Austen"};

vector<const char*> atricles={"a","an","the"};

list<string> list2(authors);  
 //正确,类型与元素都匹配;

forward_list<string> words(articles.begin(), articles.end());// 正确, 元素转换

 C.赋值和swap

           赋值相关运算会导致指向左边容器内部的迭代器、引用和指针失效。而swap操作容器的内容交换不会导致指向容器的迭代器、引用和指针失效并且高效(array、string类型的除外)。

           D.容器大小比较

           容器大小的比较与前面章节讲的字符串string的比较一样,首先是字符大小比较(出现第一个不同字符大的容器大,都相同则容器大小相同),然后是容器长度(元素前部分都相同,即一个容器的元素都在另一个容器的前半部分,元素多容器大)。

 3.顺序容器的操作

           顺序容器与关联容器的不同之处在于两者组织元素的方式。例如:元素的存储、访问、添加和删除。

           A.容器元素添加的操作

           
push_back:将元素添加到容器的尾部(array和forward_list除外)

           
push_front:将元素添加到容器的头部(list、forward_list 和deque容器支持)

           
insert:将元素添加到容器的特定位置(insert(a,b)将b元素插入到a元素指定位置的前面)

           
插入单个元素例子:

vector<string> svec;

              list<string> slist;

              slist.insert(slist.begin(),"hello!"); //在list链表首元素之前插入hello!这个元素;

 svec.insert(svec.begin(),"hello!");//在vector首元素之前插入hello!这个元素;

插入范围内元素例子:

svec.insert(svec.end(),10,"Anna");//将10个Anna元素插入到svec的末尾;

注意:end表示的是svec最有一个元素后一个位置,从而也就是插入到末尾;

            习题9.18、9.19、9.20程序解答:

#include <iostream>

#include<deque>

#include<list>

using namespace std;

int main()

{

deque<string> string1={"wangjin","wangjin1","wangjing2"};

for(auto it=string1.begin();it!=string1.end();++it)     //打印deque容器;

cout<<*it<<endl;

list<string> string2(string1.begin(),string1.end());

for(auto it1=string2.begin();it1!=string2.end();++it1)  //打印list容器;

cout<<*it1<<endl;

//上面是9.18和9.19习题程序;

list<int> oe={1,2,3,4,5,6,7,8,9,0};

deque<int> odd,even;

for(auto it2=oe.begin();it2!=oe.end();++it2)

{

if((*it2)%2==0)

even.insert(even.end(),*it2);

else

odd.insert(odd.end(),*it2);

}

cout<<"deque偶数元素为:"<<endl;

for(auto it3=even.begin();it3!=even.end();++it3)  //打印deque偶数容器;

{

cout<<*it3<<",";

}

cout<<endl<<"deque奇数元素为:"<<endl;

for(auto it4=odd.begin();it4!=odd.end();++it4) //打印deque奇数容器;

{

cout<<*it4<<",";

}

cout<<endl;  //上面为练习9.20习题

return 0;

}

显示结果:

 B.emplace操作

            C++11引入的新的三个标准:emplace_front、emplace和emplace_back,这些操作是直接构造,而不是拷贝元素;分别对应push_front、push和push_back;

例子:

c.emplace_back("978-56",25,15.99);//直接构造对象,压入容器;

            c.push_back(Sales_data("978-56",25,15.99));//创建临时对象,再压入容器;

            emplace函数在容器中直接构造元素。传递给emplace函数的参数必须与元素类型的构造函数相匹配。

 C.删除元素

            删除元素的操作:

         
   c.pop_back():删除c中尾元素。c为空,则未定义。

             c.pop_front():删除c中首元素。c为空,则未定义。

             c.erase(p)   :删除迭代器p所指定的元素,返回一个指向被删元素之后的迭代器;若p是尾元素,则返回尾后迭代器。

             c.erase(b,e):删除迭代器b和e所指定范围内的元素。返回一个指向被删元素之后的迭代器;

 c.clear():删除c中的所有元素。返回void;

              注意:迭代器删除元素,都是返回删除元素的后一个位置的迭代器;

           有关特殊的forward_list操作:

            学过链表就能很快理解它删除和添加元素对整个链表的影响,书本中做了很详细的讲解,引用了before_begin()、insert_after();

           D.改变容器的大小

         
  resize(a):a表示整个容器数据的大小,它可以通过a的大小来增大和缩小容器;

             c.resize(n):调整c的大小为n个元素。若n<c.size,则多出的元素被丢弃。

           
 c.resize(n,t):调整c的大小为n个元素。任何新添加的元
素的初始化值为t。

 
 例子:

           
list<int> ilist(10,42) ; //10个int:每个的值为42;即list初始化有10个42的容器;

         
  ilist.resize(15);   //本来有10个元素,现在扩大了容器:增加了5个为0的元素;

            ilist.resize(25,-1);
  //继续扩大容器:增加了(25-10)10个为-1的元素;

         
  ilist.resize(5)  ;  //从ilist末尾删除20个元素,最终容器只有:5个为42的元素;

            E.不要保存end返回的迭代器

              原因:当我们删除/添加vector或string的元素后,或在deque中首元素之外的任何位置添加/删除元素后,原来end返回的迭代器总是会失效。因此,添加或删除元素的循环程序必须反复调用end,而不是在循环之前保存end返回的迭代器,一直当做容器末尾使用。

              4.vector对象是如何增长的

             结果:容器通过预留空间作为备用,可用来保存更多的新元素。标准库采用了这种可以减少容器空间重新分配次数的策略。

              A.管理容量的成员函数

                容器大小管理操作:

  c.shrink_to_fit()
:将capacity()减少为与size()相同大小;

                  它的适用范围:vector、string和deque;

             
c.capacity()      :不重新分配内存空间的话,c可以保存多少元素;

              c.reserve()
       :分配至少能容纳n个元素的内存空间;

                  它们两个的适用范围:vector、string;

             
  注意:reserve并不改变容器中元素的数量,它仅影响vector预先分配多大的内存。

            B.capacity和size

 它们的区别:capacity>=size;capacity-size=预留空间的大小;根据这两个总结的公式就能够更好的理解它们。  

             例子习题9.39和9.38的题目程序: 

#include <iostream>

#include<vector>

#include<string>

using namespace std;

int main()

{

vector<string> svec;

svec.reserve(1024);

cout << "capacity1:" <<svec.capacity()<< endl;  //之前的capacity;

string word={"0"};

for(auto it=0;it<256;it++)   //将it=256,512,1000,1024分别运行程序

svec.push_back(word);

svec.resize(svec.size()+svec.size()/2);

cout << "capacity2:" <<svec.capacity()<< endl; //插入数据后的capacity;

return 0;

}             

            显示结果:

    分别为(插入256,512,1000,1024)对应的capacity:1024,1024,2000,2048;

          5.额外的string操作

           A.构造string的其他方法

           
string s(cp,n)  : s是cp指向数组中前n个字符的拷贝。数组长度须不小于n;

           
string s(s2,pos2):s是string s2从下标pos2开始的字符的拷贝。

           
string s(s2,pos2,len2):s是string s2从下标pos2开始len2个字符的拷贝。若pos2>s2.size(),构造函数的行为未定义。不管len2多大,构造函数最多拷贝s2.size()-pos2个字符。

             
例子:

          const char* cp="Hello world!!!" ;

          char nonull[]={"H","i"};

          string s1(cp)  ;//拷贝cp中的字符直到遇到空字符;s1=="Hello world!!!";

          string s2(cp+6,5);// 从cp[6]开始拷贝5个字符;s2="world";

          string s3(s1,6,20);//只拷贝到s1的末尾,s3="world!!!";

          string s4(s1,16);//s1的下标小于16,从而抛出一个out_of_range异常;

           B.substr操作

         s.substr(pos,n):返回一个string,包含s中从pos开始的n个字符的拷贝。pos的默认值为0,n的默认值为s.size()-pos,即拷贝从pos开始的所有字符;

           与上面string的操作相似,就不在举例说明;

           C.append和replace函数

           s.append(args) :  将args追加到s。返回一个指向s的引用。

           s.replace(range,args): 删除s中范围range内的字符,替换为args指定的字符。range或者是一个下标和一个长度,或者是一个对指向s的迭代器。返回一个指向s的引用。

           例子:

           string s("C++ primer"),s2=s;//将s和s2初始化为"C++ primer";

           s.insert(s.size(),"4th Ed.");

           s2.append(" 4th Ed.");//等价s.insert(s.size(),"4th Ed.");s2==s,s=="C++ primer 4th Ed."

          replace操作是调用erase和insert的一种简单形式:

           s.erase(11,3);      //s=="C++ primer  Ed."

           s.insert(11,"5th");
 //s=="C++ primer 5th Ed."

          s2.replace(11,3,"5th");
//s==s2;等价于上面两个语句;

            D.string 搜素操作

            s.find(args)  :查找s中args第一次出现的位置;

           
s.rfind(args) :查找s中args最后一次出现的位置;

           
s.find_first_of(args):在s中查找args中任何一个字符第一次出现的位置;

            s.find_last_of(args):在s中查找args中任何一个字符最后一次出现的位置;

            s.find_first_not_of(args):在s中查找第一个不在args中的字符;

           
s.find_last_not_of(args):在s中查找最后一个不在args中的字符;

            例子:

            string river("Mississippi");

            auto first_pos=river.find("is"); //返回1

            auto last_pos=river.rfind("is");// 返回4

            E.数值转换

            string和数值之间的转换:

            to_string(val)  :一组重载函数,返回数值val的string表示。val可以是任何算术类型。

           stoi(l,ul,ll,ull)(s,p,b):返回s的起始字串的数值,(红色标记的)返回值类型分别是int,long,unsigned long,long long,unsigned long long。b表示转换所用的基数,默认值为10。p是size_t指针,用来保存s中第一个非数值字符的下标,p默认为0,即函数不保存下标。

           stof(d,ld)(s,p):  返回s的起始子串的数值,返回类型分别是float,double,long double。p参数的作用与整数转换函数中一样;

          由于我用的codeblacks不支持stoi从而,我编写的程序编译不通过;下次换个支持C++11特性的软件试试;

          总结C++11特性:

               1.forward_list和array

             
 array是固定大小的数组,因此,它不支持添加/删除以及改变容器大小的操作。而forward_list的设计目标是达到与最好的手写的单向链表数据结构相当的性能。它没有size操作,因为保存和计算需要额外的开销。前面也做了介绍。

                2.cbegin和cend

                他们和begin,end类似,只不过前面加了const ;它们返回const迭代器;当不需要写访问时,就用cbegin和end比较好。

                3.列表初始化

               容器列表的初始化在前面的程序中都用到了,前面章节也讲解过;

                 list<string> aythors={"Mislton","Shakespeare"};

                4.容器的非成员版本swap

               本章节对swap和赋值进行了比较分析,swap操作将容器的内容交换不会导致指向容器的迭代器、引用和指针失效。这是它的一个很大的优点。

                 5.insert函数

                 本章做了重点讲解,它在新的标准下,接受元素个数或范围的insert版本返回指向第一个新加入元素的迭代器。

                 6.emplace操作

               
 本章也做了详细的讲解,通过前面的例子能够更好的理解和运用它。

                 7.shrink_to_fit

               
 管理容量成员的函数,它在新标准下,它可要求deque、vector和string退回不需要的内存空间。(简单来讲,就是将预留空间去掉和size()大小相同)

                8.string的数值转换

               
上面有详细的讲解,本想通过程序更好的理解它,我用的codeblacks不支持stoi等函数,只能下次尝试其他支持c++11特性的软件。

时间: 2024-10-19 02:04:38

C++primer(第五版)第九章 顺序容器(容器的运用及其部分习题解答,C++11特性总结,重点章节内容较多)的相关文章

C++ Primer(第五版) 第九章:顺序容器

练习9.1:考察使用哪种顺序容器 (a)list,当需要在容器中任意位置执行插入/删除操作时,用list最好 (b)deque,当需要在头部插入/删除元素,不需要在容器中间任意位置插入/删除元素时,用deque最好 (c)vector,当不需要在头部/任意位置插入/删除元素的情况下,用vector最好 练习9.2:考察对容器可以保存的元素类型的限制 list<deque<int>>lst1; list<deque<int> >lst2;     //在编译器

c++ primer 第五版第九章

9.01 对于下面的程序任务,vector, deque和list哪种容器最为合适?解释你选择的理由.如果没有哪一种容器优于其它容器,也请解释理由. 读取固定数量的单词,将它们按字典序插入到容器中.我们将在下一章看到,关联容器更适合这个问题. 读取未知数量的单词,总是将新单词插入到末尾.删除操作在头部进行. 从一个文件中读取未知数量的整数.将这些整数排序,然后打印到标准输出. 使用list,需要在中间插入,用list效率更高. 使用deque.只在头尾进行操作,deque效率更高. 使用vect

C++ Primer(第五版) 第二章 基本内置类型

容易忘记的部分: 2.1:C++提供的几种字符串类型有哪些及其用途? 基本的字符类型char,一个char的类型和一个机器字节一样 其他字符类型用于拓展字符集,如wchar_t.char16_t.char32_t wchar_t类型确保可以存放机器最大拓展字符集中的任意一个字符 char16_t和char32_t则为Unicode字符集服务 2.2:如何选择所使用的类型 当数值不为负数时,使用无符号类型(unsigned) 一般常用int和long long执行整数的运算 算术表达式中不使用ch

C++ Primer 第五版:第1 章

*****C++ Primer 第五版第1章学习笔记***** *****实验代码在Red Hat 6.6或VS 2013中调试***** *****文章内容依据当前知识撰写,存在认识的局限性***** 1.1 编写一个简单的C++程序 函数:依据我个人理解,C/C++的函数是一个能够完成一个功能的模块. 完整函数的组成: ①返回类型:不一定有返回值,故不一定有返回类型 ②函数名:根据名字标识完成特定功能的模块,必须存在 ③形参列表:可能没有参数传入,不一定存在 ④函数体:一个完整的函数应该是有

C++Primer第五版习题解答---第一章

C++Primer第五版习题解答---第一章 ps:答案是个人在学习过程中书写,可能存在错漏之处,仅作参考. 作者:cosefy Date: 2022/1/7 第一章:开始 练习1.3 #include<iostream> int main() { std::cout << "hello, world" << std::endl; return 0; } 练习1.4: #include<iostream> int main() { int

C++ Primer 第五版:第2章

*****C++ Primer 第五版第2章学习笔记***** *****实验代码在Red Hat 6.6或VS 2013中调试***** *****文章内容依据当前知识撰写,存在认识的局限性***** 今天学习C++ Primer 的第2章,还没有看完,先写一点看书的心得和笔记. 对象类型决定对象包含的数据和能参与的运算操作,同时还决定了存储空间大小.算术表达式中不要使用char,因为不同机器实现不一样,导致结果不同.单精度计算不一定比双精度快. C++类型不匹配时,是自动进行类型的转换. C

Java 线程第三版 第九章 Thread调度 读书笔记

一.Thread调度的概述 import java.util.*; import java.text.*; public class Task implements Runnable { long n; String id; private long fib(long n) { if (n == 0) return 0L; if (n == 1) return 1L; return fib(n - 1) + fib(n - 2); } public Task(long n, String id)

C++ Primer(第五版)学习笔记_9_标准模板库_multimap多重映照容器

C++ Primer(第五版)学习笔记_9_标准模板库_multimap多重映照容器 多重映照容器multimap与map结构基本相同,但由于重复键值存在,所以multimap的元素插入.删除.查找都与map的方法不相同. 1.multimap对象创建.元素插入 插入元素时,需要使用insert()方法和类似pair<string,double>("Jack", 300.5)的元素结构.可以看到,重复的元素是按照插入的先后顺序排序的. #include <iostre

C++ Primer(第五版)学习笔记_1_标准模板库--快速入门

C++ Primer(第五版)学习笔记_1_标准模板库--快速入门 欢迎大家阅读参考,如有错误或疑问请留言纠正,谢谢 标准模板库(STL)提供三种类型的组件:容器.迭代器和算法,他们都支持泛型程序设计标准. 容器主要有两类:顺序容器和关联容器.顺序容器(vector.list.deque和string等)是一系列元素的有序集合.关联容器(set.multiset.map和multimap)包含查找元素的键值. 迭代器的作用是遍历容器. STL算法库包含四类算法:排序算法.不可变序算法.变序性算法