前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【笔记】《C++Primer》—— 第二部分:C++标准库

【笔记】《C++Primer》—— 第二部分:C++标准库

作者头像
ZifengHuang
发布2020-07-29 16:21:44
5760
发布2020-07-29 16:21:44
举报
文章被收录于专栏:未竟东方白未竟东方白

这篇是第二部分的总结,基本上就是回看了之前的5篇笔记并且重新翻翻书梳理了一下,内容基本都是从前面的章节复制来的,长度较长,不熟悉的话看起来可能不会很轻松。

下篇开始就到了第三部分,是关于面向对象和类设计工具的内容,是C++的核心部分,难度应该也会比前面更大。

8 IO库

  • IO库的头文件中的类名都是分写入型(改i),读取型(改o),读写型(不加),还额外对应了一组为了支持宽字符wchar_t类型而设的宽字符型(加w)
  • IO流都不能进行拷贝或赋值,形参或返回类型自然也就不能设置为IO类型,传递操作都要使用引用
  • 对IO流的读写会改变其状态,因此传递和返回的引用也不该是const的
  • 流的条件状态位有[流].iostate,[流].badbit,[流].failbit,[流].eofbit,[流].goodbit
  • 每个输出流都有自己的缓冲区,又是我们需要缓冲刷新来立即输出一些数据,以下情况会立即刷新:程序正常结束,缓冲区满,endl,ends或flush操作符(附加换行,附加一个空字符,什么都不附加),设置unitbuf,读写被关联的流
  • unitbuf是流内部的一种状态调用函数,用nounitbuf来复位,会使得此流进入立即输出的状态,cerr就设置了这个位
  • fstream构造时可以直接提供文件名,也可以用open函数让空文件流对象打开文件。如果open失败,则failbit会被置位,所以open后用if检查一下是好习惯
  • fstream被析构时,close会自动调用
  • 为了打开其他文件需要close再open,直接open会损坏流
  • 每个流都有自己的文件模式,此参数在构造或open时在文件名后附加。分为in(输入),out(输出),app(追加,每次写入都定位到尾部),ate(打开后定位到尾部),trunc(截断,清空当前文件内容后操作),binary(二进制模式)
  • string流的构造参数是一个string,用str()函数可以返回其里面的string的拷贝,如果是str(string s),则会将s拷贝进去返回void
  • 用getline配合其第二个参数很方便地进行逐行读取,具体回看8.3

9 顺序容器

  • 顺序容器的储存顺序不依赖于元素的值,而是与元素加入的位置相关
  • 新标准库的容器性能非常好,都是精心优化的,我们无需自己实现容器来处理自己的数据
  • 大多数情况下我们都可以用vector来进行数据处理,需要在中间插入数据时用list,只需要在某个阶段进行中间插入,则可以先用list再转存到vector
  • 当不清楚该用什么容器时,先用迭代器代替下标操作,避免随机访问且增加灵活性
  • 用begin和end可以得到容器的头尾迭代器,注意begin指向第一个元素,end指向最后一个元素后面的位置。这让我们可以用begin==end来确定容器是否为空,当不等时容器至少有一个元素
  • 常用的遍历容器方法:while(begin!=end) ++begin;
  • assign(分配)函数可以将目标元素替换到当前容器中,会直接将当前整个容器改为目标内容
  • push_back和emplace_back都可以向容器尾加入元素,区别是push_back是用拷贝构造实现的,emplace_back是直接使用参数(因此参数需与元素的构造函数匹配)进行了内部构造,emplace_back效率稍微高一点
  • insert可以向目标迭代器之前插入元素,但要注意对vector,string尾外,deque首尾外加元素效率低下
  • at函数比直接用下标安全很多
  • erase函数用于删去容器中的元素,目标是迭代器所指的元素或两个迭代器之间的左闭范围,返回值是被删元素之后元素的迭代器,以便连续删除
  • capacity是vector的容量变量(区分于元素量size),可以用reserve指定下一次分配时所需分配的容量,用shrink_to_fit来将capacity减少只size的大小(不一定被实现)
  • find函数可以搜索指定字符串,搜索成功时返回字符串第一次出现时的第一个匹配位置的下标,搜索失败时返回称为string::npos的string::size_type的-1,npos是一个unsigned成员,因此-1代表任何string的最大可能大小,因此用int或其他类型来保存返回值并不合适
  • find_first_of函数返回对给定字符串中任意一个匹配字符的第一个匹配位置,相应的也有find_last_of,find_first_not_of等等
  • to_string函数可以将各式的数值类型转换为string。而类似的有stoi(string to int),stod(string to double)等一系列string转数值的函数
  • 注意容器适配器(如stack)需要先top得到栈顶元素后才pop

10 泛型算法

  • 大多数的标准库算法都定义在头文件algorithm中,有些数值类的在numeric
  • 写容器算法需要确保被写入的容器长度至少和需要写入的量一样长,为了规避这个风险可以用插入迭代器back_inserter解决
  • sort通过混合排序算法进行排序,默认使得序列从小到大排序,需要实现<。stable_sort内部采用稳定的排序算法
  • unique将重复的元素移动到容器尾,除了list外不会删除那些被移走的元素,返回的迭代器指向新的容器尾(最后一个不重复的元素的位置),可以用erase来删除剩余元素
  • 很多算法需要比较容器中的元素,有时候默认的运算符实现并不适合我们,可以通过在参数输入新的可调用对象(如函数)来自定义默认行为,这个参数称为“谓词”。谓词是一个可调用的表达式,标准库中的谓词分接受一个参数的一元谓词和接受两个参数的二元谓词
  • lambda有时被叫做匿名函数,是C++四种可调用对象之一(函数,函数指针,lambda,重载了调用运算符的类),它可以理解为一个未命名的内联函数,特点是可以高效地运算并调用函数体外的一些局部变量
  • lambda的格式如下,其中参数列表和返回类型是可以忽略的: [ 捕获列表 ] ( 参数列表 ) -> 返回类型 { 函数体 }
  • lambda特别的成分是捕获列表,在捕获列表中可以写入一些lambda所在函数的局部变量,然后用逗号分隔
  • lambda有值捕获,引用捕获,隐式捕获三种类型,其中两种隐式捕获不能简单混用,详见10.3
  • 当lambda函数体中存在不止一句return时,编译器将假定返回类型为void,此时要通过第六章讲到的尾置返回来指定所需的返回类型
  • 若要用普通函数来代替lambda捕获变量的特性,可以用标准库头文件functional中的bind函数来处理
  • bind函数接收一个可调用对象然后生成一个适配的新的可调用对象,第一个参数是需要适配的可调用对象,后续参数是需要传递给这个调用对象的参数,返回值是适配后的可调用对象。其中传递给调用对象的参数中,可以用placeholder空间(此空间包括在std中)的_1,_2…占位符来标记,参数填入了_1代表生成的对象的第一个参数会被映射到这个位置,_2同理
  • 如果想要给bind传递引用,需要用ref函数或其常量版本cref将所需引用的对象再包装一下,这对于iostream很有用。这个函数同样处于functional中
  • 标准库头文件iterator中定义了四种基础迭代器,对他们的赋值操作将有不同的效果,通过给算法传递不同的迭代器可以改变算法的效果,分别是:
  • 迭代器在标准库中大致分为五个抽象类别(并不对应某个具体的类,而是对类进行了分类),详见10.5
  • 知道了迭代器的分类后就可想而知算法大概会需要什么迭代器才能运行(迭代器错误时会报错并产生相应的错误提示),除了forwardl_list外的容器都提供双向迭代器甚至更高级的迭代器
  • 标准库中能传递比较谓词的算法通常都是重载的同名函数,谓词是最后一个参数,有些算法有一个xxx_if版本的函数,其接受的参数变为谓词,有拷贝版本的函数通常会增加指定目标拷贝的位置的新的参数并改名为xxx_copy
  • 链表类型list和forward_list由于实现方式的特别而拥有一些专有成员函数代替标准库算法,这些函数通常来说性能比标准库的通用函数更好,应尽可能使用
  • 链表类型还额外定义了splice(捻接)算法,将两个链表连接在一起

11 关联容器

  • 关联容器和顺序容器有根本的不同,关联容器中的元素是按照关键保存和访问的,而不是顺序容器中的按照容器位置来保存和访问
  • 标准库中最主要的两个关联容器就是map和set。map是键值对,set是关键字集。标准库中的关联容器分为无序集合和有序集合,集合中分为map和set,然后map和set都有允许重复关键字的版本
  • 关联容器的迭代器都是双向的
  • 关联容器进行初始化时可以用空构造,用迭代器范围进行拷贝构造或C11以后支持的列表初始化。进行列表初始化的时候要注意map需要采用内部花括号进行pair的构造。map中所存放的元素实际是pair类型的元素对,pair类型是存于头文件utility的标准库类型
  • 通常不对关联容器使用泛型算法,实际应用中我们使用容器自带的一些算法进行处理,例如关联容器自带的find
  • 用成员函数insert或emplace来向关联容器插入元素,使用方法和顺序容器类似
  • 我们可以用下标或at函数来访问容器的元素,参数是关键字,但是和顺序容器不同的是当关键字不在map中时,map会创建一个元素并插入进去,然后进行值初始化。相比之下如果用at来访问数据,则有参数检查,当关键字不在map中时会抛出out_of_range异常
  • 由于下标操作会创建新的值,所以我们只能对非const的map进行下标操作
  • 如果想要访问元素,对于不可重复关键字的容器直接用find即可,但是如果是可重复元素的容器,用equal_range是最直接方便的方法
  • 无序关联容器是C11才加入的新标准容器,本质是一个哈希桶,也就是用哈希函数和==运算符来组织元素,用来方便我们对一组没有明显顺序关系的元素提供一个可以在平均时间内进行检索的容器,很多时候用无序容器性能更好
  • 无序容器将哈希值相同的元素储存在同一个桶中,在桶中再采用顺序查找,然后在元素增多时看情况重整桶的元素以此来保持平均性能,因此自然也就有一批围绕着桶展开的成员函数可供操控。其中rehash能提高容器的性能但重组的时间代价很大
  • 无序容器使用哈希函数来生成每个元素的哈希值,标准库为每个内置类型(包括指针)提供了hash模板,因此我们可以直接指定内置类型的无序容器

12 动态内存

  • 动态内存(自由空间,堆空间)用来储存程序运行期间分配的对象,生存期由程序控制,我们必须显式销毁它,在C++中由new进行分配,由delete进行释放。由于人工管理动态内存的分配和销毁是繁琐易错的,所以用智能指针辅助
  • 允许多个指针指向同个对象的shared_ptr,指针独占对象的unique_ptr,还有一个伴随的弱引用指针weak_ptr
  • 最方便的使用动态内存的方式是调用make_shared函数,它使用参数args初始化类型为T的对象并返回指向这个对象的智能指针,当我们想要用new的时候可以用这个函数来替代
  • 由于智能指针内有引用计数,所以可以让多个智能指针指向同个对象共享数据,并以此管理内存的释放
  • 注意不要把智能指针和内置指针混用,让智能指针和内置指针都指向同一块内存容易导致引用问题,我们将无法确切得知合适这个对象应该被销毁
  • 类似的也不要用智能指针的get函数提取内部的指针出来构造别的智能指针,因为这样引用计数无法传递,get函数是用来适配一些无法传入智能指针的函数而出现的
  • 如果要给智能指针调用新的的删除器函数,需要在构造指针时第二个参数传入一个可调用对象,且此对象的参数必须是一个该类型元素的指针
  • unique_ptr的一个特点是没有make_shared函数之类的函数可以使用,我们必须用内置指针来初始化它
  • 我们不可以对unique_ptr进行拷贝和赋值,但是我们可以用release和reset函数来转移它的所有权
  • 我们同样可以像shared_ptr那样自定义指针的删除器,但是我们必须类似指定关联容器的比较器一样在模板尖括号中指出删除器的类型
  • 弱指针的是一种不会影响对象生存期的指针,一般用来引用和标识,特点就是对对象的weak_ptr指向不会增加shared_ptr的引用计数
  • 弱指针必须用shared_ptr来赋值或初始化,且使用时必须使用lock函数的返回值来解引用
  • 用new和方括号可以申请一大块连续内存用于初始化一个动态数组,返回值是指向这个数组第一个元素的指针,不能对其使用begin等用在数组上的迭代器操作,也无法使用范围for语句,释放动态数组我们要用delete[]的形式
  • 指针型的动态数组一样可以由unique_ptr来管理,我们也一样可用下标访问其中元素
  • 但是shared_ptr不直接支持管理动态数组,当用shared_ptr管理时我们需要提供自己的删除器且不能用下标访问元素而是需要用get得到内置指针来访问
  • 处于灵活性的考虑,有时候我们希望能得到一块连续内存但先不初始化它,此时我们可以用allocator类来处理,而且大多数时候我们用它分配动态数组可以得到更高的效率并更好管理
  • allocator分配的内存是未构造的,因此我们需要用construct函数来构造其中的元素,用destroy来析构元素
  • 当需要批量构造元素到这段内存中时,我们可以使用uninitialized系列算法来填充,使用起来类似于copy函数。其中的uninitialized_copy函数会返回指向最后一个构造的元素的指针
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-04-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 未竟东方白 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档