primer类到容器这部分

  1 构造函数的初始化列表的初始化顺序是按照声明先后初始化的
  2 如:
  3 class type{
  4     int i;
  5     int j;
  6 public:
  7     type(int val) : j(val), i(j);//看上去是先初始化啊j,实际是先使用未初始化的j去初始化I,再用val初始化j
  8      
  9 }; 
 10 
 11 委托构造函数
 12 
 13 class type{
 14     int x, y;
 15     type(int a, int b) : x(a), y(b){}//普通的构造函数 
 16     type() : type(0,0){}//默认委托构造 
 17     type(int c) : type(c, c){}//委托构造函数 
 18 }; 
 19 上面两个委托构造都是委托了普通构造函数
 20 也就说默认构造函数变成了type(0,0);
 21 type(c)变成了type(c,c);
 22 委托构造可以成链,不能成环;
 23 
 24 explicit关键字只能加在构造函数前,说明不能进行隐式转换;
 25 只有当个参数的构造函数或者只有一个参数不是默认形参的情况下使用;
 26 说的直白一点隐式转换类似于 int a = 1.3//double转 int 或者int a = 'x'; char 转 int 
 27 如:
 28 class type{
 29     int b;
 30     explicit type(int a) : b(a) {}
 31 }; 
 32 
 33 int main()
 34 {
 35     
 36     type(1.2);//这样是可以的,这样被视为构造函数之间的参数隐式转换而不是类型隐式转换;
 37     type obj = 1.2;//这样是不行的,这样视为double转 type
 38     如果没加explicit时,type obj = 1.2又是对的;
 39     因为不加explicit时可以重载的=号生成临时变量temp然后赋值给Obj,但是加了explicit后就禁用了重载的=号; 
 40     return 0;
 41 }
 42 
 43 例2
 44 class type{
 45     public:
 46     int b;
 47     type(int a) : b(a) {cout << b << "\n";}
 48     void print(type a){cout << "ok";}
 49 };
 50 int main()
 51 {    
 52     double d = 2.8;
 53     type t(d);//这种针对于其他函数 
 54     t.print(d);// 如果有explicit则编译不过,去掉则可以,本质还是中间有一步是type a = d的转变过不了; 
 55     return 0;
 56 }  
 57 
 58 
 59 
 60 IO
 61 每个输出流都有缓冲区,哦你过来保存程序读写的数据
 62 如 cout << "geagea ";
 63 文本串可能立马打印出来,也可能被操作系统保存再缓冲区中,随后再打印
 64 导致缓冲区刷新的原因很多:
 65 1.程序正常结束,main的return;
 66 2.缓冲区满时,需要刷新才能写入缓冲区;
 67 3.使用操作符如endl来显示刷新缓冲区;
 68 这里要说的时endl与\n的区别
 69 endl会先把\n放到输出流的缓冲区中;然后把缓冲区的内容刷新到设备,然后返回输出流
 70 所以一般比赛一般用\n代替endl;
 71 4.一个输出流可能被关联到另一个流,当读写被关联到的流时,缓冲区会被刷新
 72 例如cin,cerr都关联到cout,因此读cin或写cerr都会导致cout的缓冲区被刷新
 73 这也就是比赛为什么要ios::std::sync_with_stdio(false);cin.tie(0);//取消c的io,取消cin的关联
 74 
 75 还有两个类似的操作符可以刷新缓冲区
 76 flush和ends;
 77 cout << "hi" << endl;//输出一个hi并换行,然后刷新缓冲区
 78 cout << "hi" << flush;//输出hi,然后刷新缓冲区,不附加 任何额外字符
 79 cout << "hi" << ends;//输出hi和一个空字符,然后刷新缓冲区
 80 
 81 unitbuf操作符
 82 cout << unibuf;//所有输出操作后都会立即刷新缓冲区;
 83 cout << nounitbuf;//恢复正常缓冲方式;
 84 
 85 如果程序崩溃,输出缓冲区不会被刷新
 86 这时候缓冲区的数据就被挂起了;
 87 如果调试发现没有数据,要怀疑程序是否执行到这,也要考虑是否因为数据还留在缓冲区导致的;
 88 
 89 
 90 每个流最多关联到一个流,但是多个流可以关联到同一个流
 91 cin.tie(&cout);//将cin与cout关联,每次Cin数据都会刷新Cout缓冲区
 92 cin.tie(nullptr) //跟cin.tie(0)等价,表示不关联任何流;
 93 
 94 fstream头文件定义了三个类型来支持IO操作
 95 istream:从一个给定的文件读取数据;
 96 ostream:向一个给定的文件写入数据;
 97 fstream:从一个给定的文件既读又写数据;
 98 >>运算符:从一个istream对象读取输入数据
 99 <<运算符:向一个ostream对象写入输出数据
100 cin:一个istream对象,从标准读入读取数据
101 cout:一个ostream对象,向标准输出写入数据;
102 cin << //表示从cin对象读取输入数据;
103 getline函数:从一个给定的istream读取一行数据,存入一个给定的string对象中
104 如:
105 string s; //一定要是string对象 
106 getline(cin,s);//从给定的cin对象读取一行数据存入sting对象s;
107 
108 
109 头文件iostream:
110 istream、wistream;从流读取数据
111 ostream、wostream;向流写入数据
112 iostream、wiostream;读写流
113 
114 头文件fstream:
115 ifstream、wifstream;从文件读取数据
116 ofstream、wofstream;向文件写入数据
117 fstream、wfstream;读写文件
118 
119 头文件sstream:
120 istringstream、wistringstream;从string读取数据
121 ostringstream、wostringstream;向string写入数据
122 stringstream、wstringstream;读写string 
123 
124 
125 这里存在继承类的关系如:基类istream->iftream、istringstream;//后面两个是平行关系,ostraem同理 
126 wchar_t //宽字符类型 ,宽字符版本的类型和函数以w开头
127 wcin、wcout、wcerr分别对应cin、cout、cerr的宽字符版本;
128 注意这里是字符不是字符串;
129 int main()
130 {    
131     wchar_t t= L'A'; 
132     wcout << t << "\n"; 
133 }  
134 
135 流对象每次消亡会隐式调用close,但是我们要手动关,避免一些其他麻烦; 
136 
137 每个流都有一个关联的文件模式
138 in:以读方式打开
139 out:以写方式打开
140 app:每次写操作均定位到文件末尾
141 ate:打开文件后立即定位到文件末尾
142 trunc:清空文件内容 
143 binar:以二进制方式进行IO 
144 
145 一般情况下out模式默认trunc(清空文件),
146 如果想要保存文件原有内容,那么我们要同时指定app模式(在文件末尾增加内容,读或写) 
147 
148 保留被ofstream打开的文件中已有的数据唯一方法是显示指定app或in模式;
149 
150 ofstream out("file1");//隐式以输出模式打开文件并清空内容;
151 ofstream out2("file2",ofstream::out);//隐式清空文件
152 ofstraem out3("file3",ofstream::out | ofstream::trunc);//显示以输出模式打开并显示清空文件内容
153 
154 ofstream streamobj("file4",ofstream::app) ;//隐式以输出模式打开,并保留文件原有内容,并到文件末尾进行写操作;
155 
156 
157 string流
158 
159 struct personinfo{
160     string name;
161     vector<string>phones;
162 }; 
163 
164 string line,word;
165 vector<personinfo>people;
166 while(getline(cin,line))
167 {
168     personinfo info;
169     istreamstream record(line);//一个字符串输入流
170     record >> info.name;//先读取人名,然后遇到空格中止; 
171     while(revord >> word)//只有文件结尾才会返回0, 
172     {
173         info.phones.push_back(word);
174     } 
175     people.push_back(info);     
176 }
177 
178 
179 string 和 vector的元素在连续内存空间中,这也就是为什么支持随机访问;
180 插入和删除操作,需要移动插入/删除位置之后的所有元素
181 而且添加一个元素,如果内存不够还要去申请新内存拷贝过去;
182 
183 list和forward_list不支持随机访问,元素不在连续内存;
184 插入和删除操作比较方便,查询比较麻烦,要遍历链表;
185 
186 deque//双端队列
187 支持随机访问,在两端添加和删除元素比较快;
188 
189 
190 顺序容器几乎可以保存任意类型的元素 
191 vector<vector<string>>line;
192 line是一个vector,其元素类型是string的vector。
193 
194 
195 假设nodefault没有默认构造函数 
196 vector<nodefault>v1(10,init);//正确提供元素初始化 
197 vector<nodefault>v2(10);//错误,必须提供元素初始化 ,因为没有构造函数生成不了对象
198 
199 auto it = v.begin();//普通迭代器,可以修改指向内容;
200 auto it2 = v.cbeign();//常量迭代器,只能访问不能修改
201 auto it3 = v.rbegin();//反向迭代器,指向开头前一个元素;
202 
203 
204 array<int,42> a;//容量大小为42,类型是int的容器;
205 array必须指定类型和数量,因为它是固定大小容器;
206 
207 
208 
209 vector可以进行比较操作
210 如: > = < != ==
211 比较的前提是容器内的元素重载了比较符号,对于内置类型来说是重载过的;
212 
213 
214 新标准引入了三个新成员:
215 1.emplace_front 分别对应push_front //将元素放到元素头部 
216 2.emplace        insert   //将元素放到一个指定位置之前 
217 3.emplace_back  push_back  //将元素放到容器尾部
218 
219 总结一下:
220 emplace_back与push_back的区别就是
221 emplace_back在容器管理的内存空间直接创建对象
222 而push_back是在外面创建一个临时对象,然后再将其拷贝到容器管理的内存
223 这里push_back会比emplace_back多了拷贝过程,而且是跨内存拷贝;
224 所以emplace_back效率会更高;
225 
226 vector<int>v;
227 v.emplace_back(2);//这个2是int类的构造函数的形参;
228 所以emplace_back里的是类构造函数的形参;
229 
230 顺序容器常见操作
231 v.back() //返回v中尾元素的引用,若容器为空,返回的东西未定义
232 v.front()//返回c中首元素的引用,若v容器为空,返回的东西未定义 
233 v[n];//返回v中下表为的元素的引用,若n>=v.size,则返回的东西未定义 
234 v.at[n]; //返回下表为n的元素的引用,如果下表越界,则抛出数组越界的异常
235  
236 再容器中访问元素的成员函数如上述四种,返回的都是引用
237 如果容器是一个const对象,则返回值是const的引用,
238 如果容器对象不是const,则返回普通引用
239 
240 v.pop_bak();//删除v中尾元素,若容器为空,函数未定义,返回类型是void
241 v.pop_front();//删除v中首元素,若容器为空,函数未定义,返回类型是void 
242 v.erase(it);//删除it迭代器指向的元素,返回被删元素之后的元素的迭代器 
243 v.erase(itbegin.itend);//删除迭代器 itbegin和itend[)的元素,返回一个指向最后一个被删除元素之后的元素迭代器 ,也就是)没被取到的那个元素的迭代器 
244 v.clear();//清空容器的所有元素,返回void 
245  
246 v.insert(v.begin(),2);//在迭代器前插入一个元素2并返回指向该元素的迭代器
247 v.erase(v.begin();//删除it指向元素的迭代器(指向v[0]),并返回指向未一开始指向v[1]的迭代器; 
248 
249 
250 lambda表达式
251 格式:
252 
253 [capture list] (parameter list) opt-> return type {function body} 
254 
255 capture list: 捕获列表,是函数中定义的局部变量的列表;"="表示值传递,只读的,"&"表示引用[=],[&];
256 lambda必须使用尾置返回
257 
258 opt可以替换成mutable关键字,表示可以修改,否则只传[=]表示只读,不能修改; 
259 lambda可以忽略参数列表和返回类型,但必须包含捕获列表和函数体
260 auto f = [] {}
261 
262 lambda的调用方式和普通函数调用相同,都是使用调用运算符
263 cout << f() << "\n";
264 
265 lambda没有默认参数,所以实参必须和形参数量一致
266 
267 如果在函数内部使用lambda
268 void test(int sz, int a)
269 {
270     [sz](const string &a){return a.size() >= sz;}
271     //表明lambda只捕获了 sz这个变量,而没有捕获a,并且是以值传递的方式捕获,不会改变sz; 
272  } 
273 
274 inline void test(int a )
275 {
276     cout << a  << "\n";
277     auto f = [=] (){return a;};
278     cout << f() << "\n"; //2
279     a++;
280     cout << f() << "\n";//2表没lambda捕获完后就会一直保存最初捕获的变量值,不会重新捕获;除非改成引用捕获[&] 
281     auto f1 = [=] (){return a;};
282     cout << f1() << "\n"; //3    
283 }
284 
285 int main()
286 {    
287 
288     test(2);
289     return 0; 
290 } 
291 
292 
293 
294 lambda捕获函数内的变量时只捕获非static变量;
295 值传递捕获的值是lambda创建时拷贝的,因此随后对其进行修改不会影响到lambda内对应的值;
296 如果lambda函数体内除了return语句还有其他语句,则默认返回值是void,除非显示指定返回值类型
297 
298  
299 关联容器
300 
301 有序集合 
302 map:pair为元素类型,key-value 
303 set:关键字即值key = value
304 multimap:关键字可重复出现
305 multiset:关键字可以重复出现的set;
306 
307 无序集合
308 unordered_map:用哈希组织的map,无序 
309 unordered_set:用哈希组织的set;
310 unordered_multimap:用哈希组织的map,关键字可以重复出现
311 unordered_multiset:用哈希组织的set,关键字可以重复出现
312 
313 map初涉
314 map<string, int>word_count;
315 string word;
316 while(cin >> word){++word_count[word];}
317 
318 for(const auto &w : word_count)
319 cout << w.first << " " << w.second;
320 
321 while循环每次读取一个单词,如果word还未在map中,下标运算会创建一个新元素,其关键字为word,值为0。
322 
323 set<string>exclude = {"the", "or", "and"};
324 while(cin >> word)
325 {
326     if(exclude.find(word) == exclude.end())++word_count[word];
327 } 
328 
329 在有序的关联容器里,如果容器的元素类没有自定义<号,那么我们需要自定义比较规则,否则不能排序
330 
331 如:
332 bool mycmp(type &obj1, type,obj2)
333 {
334     return obj1 < obj2;
335 }
336 
337 set<type,decltype(mycmp)*>myset(mycmp);//mycmp是函数名退化成函数指针,而<>里的推理不会有退化效应; 
338 上面的意思是推理mycmp是一个函数,然后*表明提供函数的指针,这告诉我们需要提供一个函数指针;也就是说第二个参数是函数指针类型 
339 当我们向myset添加元素时,调用mycmp来为这些元素排序
340 
341 pair的初始化
342 pair<T1, T2>obj;//默认对T1,T2进行它们类的默认初始化
343 pair<T1, T2>obj(a,b);// 用a,b,分别对first,second进行初始化 
344 pair<T1, T2>obj = {a,b}; //用a,b,分别对first,second进行初始化 
345 make_pair(a,b);//同上
346 
347 当不允许重复容器有要插入的元素时,insert失败 
348 auto p = obj.insert({a,b});//insert的返回值是pair,first是迭代器,如果成功返回插入的位置,失败返回尾迭代器,second是一个bool值表示是否插入成功 
349  
350 int main()
351 {    
352 
353     set<int>s;
354     auto p = s.insert(1);
355     auto p2 = s.insert(1);
356     cout << *(p.first) << " " << p.second << "\n";
357     cout << *(p2.first) << " " << p2.second << "\n";
358     cout << typeid(p).name() << "\n";
359     
360     vector<int>a{1,2,3,4,5}; 
361     //范围插入 
362     s.insert(a.begin(), a.end());
363     //也可以用emplace代替insert;
364     //如s.empalce(2);
365     
366     
367     return 0; 
368 } 
369 emplace只能一个个添加不能用迭代器范围添加;
370 
371 对于可重复的无关联容器,insert的返回值是一个指向插入元素的迭代器,因为可以重复,所以肯定是成功的;
372 
373 c.erase(k);//删除每个关键字为k的元素,返回一个int,表示删除元素的数量
374 c.erase(it);//删除it迭代器指向的元素,并返回下一个元素的迭代器;
375 c.erase(itb,ite);//删除迭代器返回的元素,并返回ite;
376 
377 
378 map和unordered_map支持下标操作,而set不支持,本质是pair的映射;
379 同时也不能对multimao,unordered_multimap进行下标操作,范围映射规则
380 
381 用下标进行操作时,如过key不在容器内,会添加一个key,并默认初始化;
382 c[k];//不会抛异常,如果k不在容器内,添加一个k,并默认初始化 
383 c.at[k];//如果K不在容器内,抛出out_of_rang越界的异常
384 
385 set<int>s;
386 find和count的区别:
387 find返回的是迭代器,count返回的是int,表示容器内key的数量;
388  
389 对于可重复元素的查找,重复元素在迭代器上是连续的
390 可以这样
391 multimap<string,string>mp;
392 
393 string tg = "author";
394 auto it = mp.find(tg);
395 auto cnt = mp.count(tg);
396 while(tg--)
397 {
398     cout << (*it).second << "\n";
399     it++;    
400 }
401 或者
402 for(auto beg = mp.lower_bound(tg),ed = mp.upper_bound(tg); beg != ed; bge++)
403 {
404     cout << (*beg).second;
405  } 
406 
407 
408 无序关联容器,底层是哈希表
409 自定义的类不能当作哈希映射,必须自定义哈希映射规则
410 
411 struct pairhash{
412     public:
413     template <typename T, typename U>
414     int operator ()(const pair<T, U> &x)const//仿函数,把类对象当成函数名一样去调用 
415     {
416         return hash<T>()(x.first) ^ hash<u>()(x.second);//hash内置的重载        
417     }
418     //对上面语法解释一下,int operator ()(const pair<T, U> &x)const,重载()运算符,最后的const表示不能通过该函数修改成员变量
419     // hash<T>()(x.first) 前面的hash<T>()是一个hash匿名对象遇上()触发仿函数最后那个括号是仿函数的形参; 
420 }; 
421 
422 int main()
423 {
424     unordered_map<pair<int, int>, int, myhash>mp;//第三个参数是自定义的一个类,里面要有映射规则
425      
426     return 0; 
427 } 

 

posted @ 2022-05-14 20:55  matt-11  阅读(29)  评论(0)    收藏  举报