十六:IO操作
IO对象无拷贝和赋值,只能引用传递,并且不能是const
函数good() 所有错误位都没置位的情况下返回true;
fail() 一般用作流使用的条件(failbit,badbit)
eof()和bad()只表示特定的错误。
例如:将failbit和badbit复位,但eofbit不变
cin.clear(cin.rdstate() &~cin.failbit & ~cin.badbit);
endl 换行并刷新缓冲区。
要每次输出都刷新缓冲区,用unitbuf cout< cout< x.tie(&o); 将流x关联到流o ostringstream允许保存字符串,最后输出,他在#include 相对于stirng,他能够使用>>操作东西。 #include #include usingnamespace std; int main() { ostringstream formatted; formatted<< " "<<"Hello" < cout<< formatted.str(); return 0; } 十七:容器 ----------------支持快速随机访问----------------- array固定大小。 vector string deque ----------不支持随机访问-------------- list forward_list ----------容器适配器------------- stack,queue – 基于deque priority_queue – 基于 vector array 可以进行拷贝和赋值,内置数组不行。 数组不能初始化给array,花括号也不能赋值给array对象。 array对象的赋值,两边的类型必须完全一样。 swap 交换array时间与元素个数成正比。 swap 交换其他容器,其实只交换两个容器的数据结构,常数时间内就可以完成。 每个容器都支持== 和 != ,但是<=等等不一定。 insert 插入到迭代器指定位置之前(1:迭代器可能指向尾部不存在的元素位置,2:开始位置插入的功能很有用),forward_list有自己特色的insert(insert_after) emplace_front/emplace/emplace_back (调用对应的构造函数创建对象) 功能对应 push_front/insert/push_back front/back(除forward_list) 返回首尾元素的引用。 capacity和reserve只适用vector和string reserve不改变容器元素的数量,仅影响预先分配多大的空间。 resize只改变元素数量,不改变容量。 十八:泛型算法 1:头文件:numeric algorithm 2:迭代器另算法不依赖于容器,但算法依赖于元素类型的操作。 如:find用元素的==完成比较。 3:算法使用的是迭代器,不能执行容器操作,所以 算法永远不会改变底层容器的大小(不会删除和添加元素) 因为插入迭代器,默认在赋值时调用了容器算法:push_back(),所以他可以改变容器大小。 4:插入迭代器 (1)作为目的位置,可以保证算法有足够的元素空间。 如:fill_n(back_inserter(vec),10,0); (2)赋值,会调用push_back将元素添加到容器: 如:vector auto it = back_inserter(vec); *it = 42;//实际调用了push_back; 5:谓词:一个可调用的表达式,返回能用做条件的值 bool isShorter(const string&s1,const string &s2) { returns1.size() < s2.size(); } sort(vec.begin(),vec.end(),isShorter); //stable_sort 可以保持等长元素间的字典序。 6:lambda表达式 1) 可调用对象:(1)函数 (2)函数指针(3)重载了函数调用运算符的类 (4)lambda表达式 可以理解为:未命名的内联函数(与函数不同的是,他可以定义在函数内部) []()->return type {} [捕获列表](参数列表)->返回类型{函数体} ///捕获列表和函数体不能省略。 2) lambda根据函数体默认推断返回类型,如果函数体包含任何单一return语句之外的内容,且未指定返回类型,则默认返回void,要明确返回类型,必须指定返回类型。 lambda不能有默认参数。 find_if 只接受一个一元谓词。 一:)捕获列表弥补了参数数量的问题。 二:)参数数量的问题可以用bind函数解决 捕获列表只用于局部非static变量,lambda可以直接使用局部static变量和它所在函数之外的声明的名字。 lambda数据成员(捕获列表)在lambda对象创建时被初始化。而不是调用时。 3) 捕获可以是值捕获或者引用捕获。 可以让编译器根据lambda体中的代码推断我们要使用那些变量,为了指示编译器推断捕获列表,应在捕获列表中写一个&或=; 可以混合使用,如[=,&os],默认都是值捕获,os采用引用捕获。混合使用时,捕获列表的第一个元素必须是&或= 4) 可变lambda 要改变捕获变量的值,必须在参数列表首加个mutable,因此,可变lambda能省略参数列表。 如: auto f = [v1]()mutable{++v1;}; 5)指定lambda返回类型(必须使用尾置返回类型) transform(vi.begin(),vi.end(),vi.begin(),[](inti)->int{ if(i<0) return –i;else return i;});//函数体不是一个单独return ,并且不是void返回,所以必须指定返回类型。 7:for_each接受可调用对象 for_each(vec.begin(),vec.end(),[](conststring *s){coust< 8:bind函数适配器 接受一个可调用对象,生成一个新的可调用对象来“适应”原对象的参数列表。 1) find_if(vec.begin(),vec.end(),[sz](conststring &a){return a.size()>=sz;}); find_if(vec.begin(),vec.end(),bind(check_size,_1,sz));//check_size是一个函数,它比较_1字符串长度和sz的大小 如:auto checkfun = bind(check_size,_1,6); string s = “hello”; bool b1 = checkfun(s); _n定义在placeholders的命名空间中,而placeholders定义在std中,也定义在functional头文件中。 using namespace std::placeholders 2) bind 可以重新安排参数顺序: 如: auto g = bind(f,a,b,_2,c,_1); g(X,Y);相当于调用f(a,b,Y,c,X); 这样其实可以灵活运用参数顺序的不同,重排 sort(vec.begin(),vec.end(),isShorter);//正序 sort(vec.begin(),vec.end(),bind(isShorter,_2,_1));//逆序 反向迭代器也可以实现逆序 sort(vec.rbegin(),vec.rend()); 3)bind绑定引用参数 ostream &print(ostream &os,conststring &s,char c){ return os< for_each(vec.begin(),vec.end(),[&os,c](conststring &s){.....}); 引用捕获的代替: for_each(vec.begin(),vec.end(),bing(print,ref(os),_1,‘ ’)); cref 生成一个保存const引用的类。 (1)查找:find 需要 == find(vec.cbegin(),vec.cend(),val); find(ia+1,ia+4,val); 子范围查找 (2)求和:accumulate需要 + accumulate(vec.cbegin(),vec.cend(),0);//0是初始值 (可以累加string,拼接) (3)判断相等:equal 需要== 假定第二个序列和第一个一样长。char*竟也可以用==比较 equal(vec.cbegin(),vec.cend(),list1.cbegin()); (4)值覆盖:fill_n fill_n(vec.begin(),vec.size(),0); (5)拷贝 :目的序列至少与输入序列一样多 int a1[]={1,2,3}; int a2[sizeof(a1)/sizeof(*a1)]; auto ret =copy(begin(a1),end(a1),a2);//ret指向a2尾元素之后的位置 (6)替换: replace(ilst.begin(),ilst.end(),0,42);//把0替换为42 如果希望原序列不变: replace_copy(ilst.cbegin(),ilst.cend(),back_inserter(vec),0,42); (7)排序:sort 需要 < sort(vec.begin(),vec.end()); 对排完序的可以去重一下:unique只能处理排好序的 auto it = unique(vec.begin(), vec.end());//it 指向不重复区域’后一个位置’的迭代器 进而可以删除 vec.erase(it,vec.end()); (8)转化 transform(vi.begin(),vi.end(),vi.begin(),[](inti){return i<0?-i:i;}); //目的位置迭代器和输入开始的迭代器相同。 (9)删除 remove_if(v1.begin(),v1.end(),[](inti){return i%2;});//删除奇数元素 //将偶数元素从v1拷贝到v2,v1不变 remove_copy_if(v1.begin(),v1.end(),back_inserter(v2),[](inti){return i%2}); (10)通用版本的sort需要随机访问迭代器,所以不能用于list和forward_list。 (11)取两个set的交集:set_intersection(s1.begin(),s1.end(),s2.begin(),s2.end(),s3) 十九:迭代器 1:插入迭代器 赋值时,调用容器操作来操作 back_inserter 使用push_back front_inserter 使用 push_front会将插入元素的顺序颠倒过来。 inserter 使用insert :*t =val,等价于 it = c.insert(it,val); ++it;//递增it,使它指向原来的元素(所以,可以用inserter连续顺序插入) *it,++it,it++不会做任何操作,都返回it 2:iostream迭代器 1) istream读取的类型必须定义了>>运算符 istream_iterator istream_iterator vector ++in,in++ 使用元素类型定义的>>,从输入流中读取下一个值。 2) ostream 需要类型具有<<输出运算符。 创建ostream_iterator时,可以提供(可选)第二个参数,每个输出元素后都会打印此字符串。 *out,++out,out++ 不做任何操作,都返回out osteam_iterator for(auto e:vec) *out_iter++= e;//赋值语句实际上将元素写到cout 等同于out_iter = e; 可以调用copy打印vec中元素,比循环简单: copy(vec.begin(),vec.end(),out_iter); 3:反向迭代器 ++it指向前一个元素 rbegin/crbegin rend/crend 指向容器尾元素和首元素前一个位置的指针。 sort逆序排序 我们只能从既支持++又支持—的迭代器来定义反向迭代器,流不支持递减,所以不能从forward_list和流迭代器创建反向迭代器。 通过调用reverse_iterator的base成员把反向迭代器转换为普通迭代器 |rit.base |end() -------------------------------- | |rbegin rit rit和rit.base指向不同元素,rbegin和end也是不同元素 迭代器类别:(根据支持的操作不同) 1)输入迭代器 2)输出迭代器 3)前向迭代器 4)双向迭代器 5)随机访问迭代器 链表特有的操作会改变容器,如merge,splice(元素合并) 二十:关联容器 map set 有序保存: map/multimap set/multiset 无序 unordered_map/unordered_multimap unordered_set/unordered_multiset 1: 关联容器,除了与顺序容器相同的操作外 1)有独有的操作,如count(val) 返回容器中val的个数 2)类型别名 3)无序容器提供一些调整哈希性能的操作。 key_type mapped_type(只适用于map) value_type,对于set与key_type相同,set中的关键字也是const的 对于map,为pair<const key_type,mapped_type>,我们不能改变元素的关键字,所以关键字类型是const的。 2:map 插入 对于不包含重复关键字的容器,insert和emplace 返回一个pair. pair的first是一个迭代器,指向具有给定关键字的元素。 pair的second是一个bool值,指出插入成功还是已经在容器中。 不能对multi版本的map执行下标运算。 set也不能执行下标运算。 因为下标运算符可能插入一个新的元素,所以,只能对非const的map使用下标运算符。 3: lower_bound(val)返回第一个与查找元素匹配的元素,如果没有,则指向第一个关键字大于val的元素。 upper_bound(val)返回指向最后一个匹配val的之后的元素。 这两个可以作为一个迭代器范围。 equal_range(val)返回一个迭代器pair。 若关键字存在:第一个迭代器指向第一个与关键字匹配的元素,第二个迭代器指向最后一个匹配元素之后的位置。 若关键字不存在:两个迭代器指向关键字可以插入的位置。 for(auto pos =authors.equal_range(val);pos.first != pos.second;++pos.first) cout< 4:无序容器 unordered_...... 不使用比较运算符组织元素,而是使用一个哈希函数hash 在存储上组织为一组桶。 性能依赖于哈希函数的质量和桶的数量和大小。 标准库为内置类型(包括指针)提供了hash模板。我们可以直接定义关键字是内置类型(包括指针)、string还是智能指针类型的无序容器。 为了使用自定义的类型定义无序容器,我们需要提供代替==运算符和哈希值计算函数。 size_t hasher(const Sales_data&sd) { returnhash } bool eqOp(const Sales_data&lhs,const Sales_data &rhs) { returnlhs.isbn() == rhs.isbn(); } using SD_multiset = unordered_multiset SD_multiset bookstore(32,hasher,eqOp); 如果我们定义了==运算符,则可以只重载hash函数 unordered_set 无论有序还是无序容器,具有相同关键字的元素都是相邻存储的。 二十一:动态内存 智能指针(模板) 1:shared_ptr 允许多个指针指向同一对象 例:shared_ptr (1) 对于内置类型加不加()有区别,如果是自定义类型,则没什么区别,都是调用的默认构造函数。 int *p1 = new int;//默认初始化,*p1未定义。 int *p2 = new int();//值初始化为0,*p2值为0; (2)括号包围的初始化器,可以用auto推断要分配的类型 auto p1 = new auto(obj);//括号中仅有单一初始化器是才可以; (3)可以对数组元素进行值初始化,但是auto不能推断数组。 int *p1 = new int[10];//10个未初始化int int *p1 = new int[10]();//10个值初始化为0的int 对于数组,不能在括号中给出初始化器,即,不能用auto做推断。 (4)delete执行两个动作:1:销毁对象 2:释放内存 const 对象不能被改变,但是可以被销毁(delete) const int *p1 = new const int(10); delete p1; (5)接受指针参数的智能指针是explicit的,所以,不能将内置指针隐式转化为智能指针,必须使用直接初始化。 shared_ptr p1 = new int(10);//错误,函数返回也类似 shared_ptr p2(new int(10));//正确 (6)不要混用普通指针和智能指针,因为原则上,如果我们把内存的管理责任交个智能指针,那么就不应该用内置指针来访问了。因为可能已经释放了,用内置指针访问会出错。 (7)不能使用get初始化另一个智能指针,或为智能指针赋值。 使用get返回指针的代码,不能delete此指针。因为用get返回的指针初始化的智能指针与原来的是独立的,互相不知道对方的引用计数。 (8)使用异常处理程序能够在异常发生后另程序流程继续。确保资源被正确释放的方法是使用智能指针。 (9)可以在初始化shared_ptr时,指定删除器。 shared_ptr 2:unique_ptr 独占使用所指对象 (1) unique_ptr是独占,所以,不支持拷贝和赋值 unique_ptr unique_ptr unique_ptr (2) release或reset将指针所有权从一个unique_ptr转移给另一个unique_ptr (3) 不能拷贝unique_ptr有一个例外,我们可以拷贝将要销毁的unique_ptr,如函数要返回时。(编译器执行特殊的拷贝,移动赋值) (4) 与重载关联容器(无序)类似,可以在尖括号类型后提供删除器。 unique_ptr 3:weak_ptr 若引用,不增加引用计数,指向shared_ptr对象 不能使用weak_ptr直接访问对象,必须调用lock。lock返回一个指向共享对象的shared_ptr. 4:动态数组(并不是数组类型) (1)new分配并初始化一个对象数组。 未得到数组类型指针,而是数组元素类型的指针。 (2)可以对数组中元素进行值初始化,方法是在大小后跟一对空括号。 delete []p,释放动态数组,元素按照逆序销毁。 (3)标准库提供了一个管理new分配数组的unique_ptr版本。必须在对象类型后加上空方括号。 unique_ptr up.release();//自动用delete[]销毁 因为up指向一个数组,而不是单个对象,所以不能用点或者箭头运算符,但是可以使用下标运算符。up[i] = i; (4)shared_ptr不支持管理动态数组,若希望管理,必须提供自己的删除器。 shared_ptr sp.reset();//他会调用delete[] shared_ptr 没有定义下标运算符,并且不支持指针的算术运算,我们这样做: *(sp.get()+i) = i; 5:若想让内存分配与对象构造分离,需要使用allocator替代new new对于没有默认构造函数的类有限制。 智能指针,也只是一个指针,我们要注意分配空间(new,make_shared等)