C与C++标准简介
-
C89标准(ANSI C)
-
C99标准:经典,稳定性好,用的多
-
C11标准:原名C1X,新的标准,最新C++20
C++11标准
https://cplusplus.com/reference/list/list/
-std=c++11 设置编译器标准
C++11常用特性
1)auto关键字※※
一个变量的存储类型修饰符,C++11标准中添加了新的类型推导特性,编译器自动推导变量类型。
-
使用auto类型推导的变量必须马上初始化,这类似于const关键字
-
auto不能与其它任何类型说明符一起使用;
-
auto不能被声明为返回值,auto不能作为形参,auto不能被修饰为模板参数
-
auto仅仅是一个占位符,它并不是一个真正的类型,不能使用一些以类型为操作数的操作符,如sizeof或者typeid
2)nullptr关键字※※
nullptr在任何情况下都代表空指针,而NULL表示空指针或变量值为0的指针,会对C++重载产生影响,所以C++中尽量使用nullptr
3)for循环新语法
基于范围的for循环,如果在循环中修改数组或者容器的每个元素,可使用引用类型。
语法格式:
for(declaration : expression) { 循环体; }
-
declaration:表示此处要定义一个变量,该变量的类型为要遍历序列中存储元素的类型,需要注意的是在C++11标准中,declaration参数处定义的变量类型可以用auto关键字表示,该关键字可以使编译器自行推导该变量的数据类型。
-
expression:表示要遍历的序列,常见的可以为事先定义好的普通数组或者容器,还可以是用0大括号初始化的序列。
-
for循环的这种使用方式的内在实现实际上还是借助迭代器
4)列表初始化
不包含容器的自定义类也可以直接用初始化列表
5)override、final关键字
-
在派生类的成员函数中使用override时,如果基类中无此函数,或基类中的函数并不是虚函数,编译器会给出相关错误信息
-
想禁止派生类覆盖特定的虚方法,为此可在参数列表后面加上final。
6)delete关键字新增功能
在成员函数后加“=delete”,表示显式禁用某个函数
7)default
显式地、强制地要求编译器为我们生成默认版本。
-
特殊成员函数:默认构造函数、拷贝构造函数、析构函数、赋值运算符函数
-
default特性仅适用于类的特殊成员函数,且该特殊成员函数没有默认参数。
-
default可以在类体定义也可以在类外定义。
8)noexcept运算符
void func(int x) noexcept;
// 不抛出异常,用在异常接口声明中
-
在成员函数中,noexcept需要跟在const以及引l用限定符之后,在final、override或虚函数=0之前。
-
noexcept可以出现在该函数的所有的声明或定义语句
9)匿名函数※※
没有函数名的函数。
匿名函数也称为Lambda函数,可以免去函数的声明和定义。这样匿名函数仅在调用函数的时候才会创建函数对象,而调用结束后立即释放,所以匿名函数比非匿名函数更节省空间。
函数表达式:
[capture](parameters)->return-type{body}// ->return-type可省略 ---[捕获区](参数区){代码区}
-
capture:
-
[]:不捕获任何外部变量
-
[x, &y]:x按值捕获,不能改,y按引用捕获
-
[&]:所有外部变量按引用捕获
-
[=]:所有外部变量按值捕获,不能改
-
[&, x]:x按值捕获,其他按引用捕获
-
[=, &z]:z按引用捕获,其他按值捕获,不能改
-
-
parameters:存储函数的形参
-
return-type:函数返回类型
-
body:函数体
与普通函数差别主要有两个,使用[]替代了函数名;没有声明返回类型。返回类型相当于使用decltype根据返回值推断得到的。如果Lambda不包含返回语句,推断出的返回类型将为void。
10)右值引用
提高程序性能,减少拷贝。
区分左值(lvalue)和右值(rvalue):能够被引用被赋值的叫左值。即左值引用。字面量、临时变量都是右值,函数返回值可能是左值也可能是右值。
右值引用(&&)常用的两个场景(其他场景也能用):
-
移动构造函数参数声明中
-
完美转发:类模板或函数模板中形参T&& p,则既可接收左值又可接收左值,万能引用
左值引用和右值引用的绑定规则:
-
非const左值引用只能绑定到非const左值;
-
const左值引用可绑定到const左值、非const左值、const右值、非const右值;
-
非const右值引l用只能绑定到非const右值;
-
const右值引l用可绑定到const右值和非const右值。
注意:
-
右值引用,是延长了对象的生命周期。右值引用是一个持久的变量,它所引用的对象不会在本该被销毁的时候销毁。
-
在移动构造函数和移动赋值运算符中,要确保源对象在资源转移后处于有效但未指定的状态,一般做法是将源对象的指针置为nullptr。
11)线程类
线程是进程的执行流,是任务执行的最小单位。
标准模板库(STL)
https://cplusplus.com/reference/list/list/
1)STL简介
-
STL(StandardTemplateLibrary)即标准模板库,是一个具有工业强度的,高效的C++程序库。
-
主要集成了常用的数据结构(类模板实现)和算法(函数实现)。
-
STL的一个重要特点是数据结构和算法的分离
-
它不是面向对象的,STL主要依赖于模板而不是封装,继承和虚函数(多态性)
六大组件:容器、算法、迭代器、仿函数、配接器、配置器
1.容器(Containers)※※
用来管理某一类对象的集合,是一种数据结构。
C++提供了deque、list、vector、map等容器
-
序列容器:将一组具有相同类型的对象,以严格的线性形式组织在一起,数组和列表的推广
-
vector:动态数组实现
-
deque:双向队列
-
List:双向链表
-
-
关联容器:元素位置取决于特定排序准则,和插入顺序无关
-
set、multiset、map、multimap
-
容器类自动申请释放内存,无需new和delete
-
-
最常用的:string、vector、set、list、map
2.算法(Algorithms)
头文件:<algorithm>
,<numeric>
,<functional>
每个容器都有专属的迭代器,而算法通过迭代器对容器中的元素进行操作。
3.迭代器(iterators)
通过迭代器可以在不了解容器内部原理的情况下遍历容器,算法不直接操作容器中的数据,而是通过迭代器间接操作,每一个容器都定义了其本身所专有的迭代器,用以存取容器中的元素。
4.仿函数
从实现的角度看,仿函数是一种重载了operator()的类或者类模板。可以帮助算法实现不同的策略。
仿函数又叫函数对象,是一个定义了operator()的对象。可以将仿函数视为一般函数,只不过不是将所有语句放在函数体里,而是放在operator()中编写。
5.配接器
一种用来修饰容器或者仿函数或迭代器接口的东西
6.配置器
负责空间配置与管理,一个实现了动态空间配置、空间管理、空间释放的类模板。
2)序列容器之vector
向量(vector)是一个封装了动态大小数组的顺序容(Sequence Container)。跟任意其它类型容器一样,它能够存放各种类型的对象。但一个容器中的对象必须是同一种类型。 优点:尾部添加或移除元素非常快速。但是在中部或头部安插元素比较费时。
vector基本操作:
-
引入vector:头文件:
#include <vector>
vector属于std命名空间
-
vector成员函数:
-
创建一个vector对象
创建对象主要调用的是vector中的构造函数,Vector是一个类模板,声明从类模板产生某种类型的对象时,需要提供附加信息
-
访问vector中的元素
-
vector::at()//推荐,检测下标越界问题
-
vector::operator[]//重载了下标运算符[]
-
-
添加元素
-
删除元素
-
迭代器遍历元素
-
其他
-
capacity0表示容器在它已经分配的内存中可以容纳多少元素,调用insert或push_back等)会引发上面的重新分配。
-
size()告诉你容器中有多少元素。它没有告诉你容器为它能容纳的元素分配了多少内存。
-
max_size()最大可能的元素个数.
-
resize(Container:size_typen)强制把容器改为容纳n个元素。调用resize之后,size将会返回n。如果n小于当前大小,容器尾部的元素会被销毁。
-
-
其他函数
-
voidswap(vector&):交换两个同类型向量的数据
-
voidassign(intn,constT&x):设置向量中第n个元素的值为x
-
void assign(const_iterator first,const_iterator last):向量中[first,last)中元素设置成当前向量元素。
-
3)序列容器之List
List底层实现是双向链表,适合快速的插入和删除,不适合做查询,随机访问。
定义和初始化
常用操作函数
4)关联容器之Map
Map的元素是成对的键值/实值,内部的元素依据其值⾃动排序,
-
Map内的相同数值的元素只能出现⼀次,
-
Multimaps内可包含多个数值相同的元素,内部由⼆叉树实现,便于查找
-
map⽀持[ ]运算符,multimap不⽀持[ ]运算符;
-
map和multimap都需要
#include<map>
基本操作函数
-
用insert插入pair数据
-
pair结构体说明:
-
用insert函数插入value_type数据
-
用数组方式插入数据
元素的查找与删除
小结
每个容器都有专属的迭代器,而算法通过迭代器对容器中的元素进行操作。
-
容器能够通过模版的方法,装下各种类型的节点元素。
-
迭代器是一种smartpointer,迭代器所指向的对象为容器中元素(结构体)的节点。 如定义一个classList的容器,容器的节点为ListItem,那么迭代器就应该指向(的对象)容器中的ListItem节点。而迭代器通过重载*(解引|用)运算符,从而就可以得到节点的值。通过重载operator->运算符就可以得到容器节点地址(指针)。
-
算法通过操作容器对应的迭代器,就可以间接地操作容器中的元素。而不需要关注容器的内部细节。
标准模板库之算法
-
⾮可变序列算法:指不直接修改其所操作的容器内容的算法。
-
可变序列算法:指可以修改它们所操作的容器内容的算法。
-
排序算法:包括对序列进⾏排序和合并的算法、搜索算法以及有序序列上的集合操作
-
数值算法:对容器内容进⾏数值计算。
1)查找算法
-
count(_InIt _First, _InIt _Last, const _Ty& _Val);
利⽤等于操作符,把标志范围内的元素与输⼊值⽐较,返回相等元素个数。
-
first:容器的首迭代器
-
last:容器的末迭代器
-
val:统计的元素
-
-
count_if(_InIt _First, _InIt _Last, _Pr _Pred);
返回区间中满⾜指定条件的元素数⽬。
-
_First 输⼊迭代器,指向将被搜索的区间第⼀个元素的位置。
-
_Last 输⼊迭代器,指向将被搜索的区间最后⼀个元素后的。
-
_Pred ⽤户⾃定义的 predicate function object ,定义了元素被计数需满⾜的条件。 predicate 只带⼀个参数, 返回 true 或 false.
-
-
bool binary_search (ForwardIterator first, ForwardIterator last, const T& val);
在有序序列中查找value,找到返回true。重载的版本实⽤指定的⽐较函数对象或函数指针来判断相等。
-
InputIterator find (InputIterator first, InputIterator last, const T& val);
利⽤底层元素的等于操作符,对指定范围内的元素与输⼊值进⾏⽐较。当匹配时,结束搜索,返回指向该元素的Iterator。
#include <iostream> #include <iomanip> #include <string> #include <list> #include <vector> #include <algorithm> using namespace std; bool comp(int val) { if(val > 10) return true; else return false; } int main(int argc, char const *argv[]) { vector<int> vec{11, 55, 6, 7, 3, 66, 9}; // 查找有无符合条件的数据,成功返回true //InputIterator find (InputIterator first, InputIterator last, const T& val); if (find(vec.begin(), vec.end(), 77) != vec.end()) cout << "find target value" << endl; else cout << "not find" << endl; // 查找算法,通过第三个形参来查找 if (find_if(vec.begin(), vec.end(), comp) != vec.end()) cout << "find target value" << endl; else cout << "not find" << endl; // 统计指定的值在集合中出现的次数 cout << count(vec.begin(), vec.end(), 6) << endl; cout << count_if(vec.begin(), vec.end(), comp) << endl; //bool binary_search (ForwardIterator first, ForwardIterator last, const T& val); sort(vec.begin(), vec.end()); bool ret = binary_search(vec.begin(), vec.end(), 11);// 二分查找,查找的集合要提前排好序(从小到大) cout << ret << endl; }
2)排序算法
-
merge: 合并两个有序序列,存放到另⼀个序列
-
random_shuffle: 对指定范围内的元素随机调整次序
-
nth_element: 将范围内的序列重新排序,使所有⼩于第n个元素的元素都出现在它前⾯,⽽⼤于它的都出现在后⾯
-
reverse: 将指定范围内元素重新反序排序。
-
sort: 对给定区间所有元素进⾏排序, 排序对象可以给数组,list、vector排序
-
默认升序
-
定义自定义函数要构造一个普通的bool函数
-
复合类型排序
-
第三个参数使用标准库函数
-
只能实现简单的排序,对结构体不适用
-
-
stable_sort: 与sort类似,不过保留相等元素之间的顺序关系。
#include <iostream> #include <iomanip> #include <string> #include <list> #include <vector> #include <algorithm> using namespace std; struct stu { int chinese; int math; }; bool comp(stu val1, stu val2) { if(val1.chinese+val1.math > val2.chinese+val2.math) return true; else return false; } int main() { vector<int> vec1{10,11,5,77,22}; vector<int> vec2(5); vector<stu> vec3{{55,66}, {88,99}, {77,66}}; // void random_shuffle (RandomAccessIterator first, RandomAccessIterator last); random_shuffle(vec1.begin(), vec1.end());// 随机打乱集合中元素的顺序 for(auto e:vec1) { cout << e << endl; } // void reverse (BidirectionalIterator first, BidirectionalIterator last); reverse(vec1.begin(), vec1.end());// 反序集合 for(auto e:vec1) { cout << e << endl; } // void sort (RandomAccessIterator first, RandomAccessIterator last); sort(vec1.begin(), vec1.end());// 默认从小到大的排序 for(auto e:vec1) { cout << e << endl; } sort(vec3.begin(), vec3.end(), comp); for(auto e:vec3) { cout << e.chinese << ":" << e.math << endl; } return 0; }
3)删除与替换算法
-
copy: 复制序列。
-
copy_backward: 与copy相同,不过元素是以相反顺序被拷⻉。
-
remove: 删除指定范围内所有等于指定元素的元素。注意,该函数不是真正删除函数。内置函数不适合使⽤remove和remove_if函数。
-
remove_copy: 将所有不匹配元素复制到⼀个制定容器,返回OutputIterator指向被拷⻉的末元素的下⼀个位置。
-
remove_if: 删除指定范围内输⼊操作结果为true的所有元素。
-
remove_copy_if: 将所有不匹配元素拷⻉到⼀个指定容器
-
replace: 将指定范围内所有等于vold的元素都⽤vnew代替。
-
replace_copy: 与replace类似,不过将结果写⼊另⼀个容器。
-
replace_if: 将指定范围内所有操作结果为true的元素⽤新值代替。
-
replace_copy_if: 与replace_if,不过将结果写⼊另⼀个容器
-
swap: 交换存储在两个对象中的值。
-
swap_range: 将指定范围内的元素与另⼀个序列元素值进⾏交换。
-
unique: 清除序列中重复元素,和remove类似,它也不能真正删除元素。重载版本使⽤⾃定义⽐较操作。
-
unique_copy: 与unique类似,不过把结果输出到另⼀个容器。
#include <iostream> #include <iomanip> #include <string> #include <list> #include <vector> #include <algorithm> using namespace std; struct stu { int chinese; int math; }; bool comp(stu val1, stu val2) { if(val1.chinese+val1.math > val2.chinese+val2.math) return true; else return false; } int main() { vector<int> vec1{10,11,5,77,22}; vector<int> vec2(5); vector<stu> vec3{{55,66}, {88,99}, {77,66}}; // OutputIterator copy (InputIterator first, InputIterator last, OutputIterator result); copy(vec1.begin(), vec1.end(), vec2.begin());// 要保证最后一个形参有足够的空间来容纳前一个集合 for(auto e:vec2) { cout << e << endl; } move(vec1.begin()+1, vec1.end(), vec2.begin());// 移动元素到后边的集合中 for(auto e:vec1) { cout << e << endl; } //void replace (ForwardIterator first, ForwardIterator last, const T& old_value, const T& new_value); replace(vec1.begin(), vec1.end(), 5, 100);// 对指定集合中的老值全部替换为新值 for(auto e:vec1) { cout << e << endl; } generate(vec2.begin(), vec2.end(), [](){static int a = 100; a++; return a;});// 根据指定的函数生成某种规律的数字集合并复制到集合中 for(auto e:vec2) { cout << e << endl; } // ForwardIterator remove (ForwardIterator first, ForwardIterator last, const T& val); remove(vec1.begin(), vec1.end(), 10);// 删除集合范围内的某个值 for(auto e:vec1) { cout << e << endl; } return 0; }
4)关系算法
-
equal: 如果两个序列在标志范围内元素都相等,返回true。重载版本使⽤输⼊的操作符代替默认的等于操作符。
-
includes: 判断第⼀个指定范围内的所有元素是否都被第⼆个范围包含,使⽤底层元素的<操作符,成功返回true。重载版本使⽤⽤户输⼊的函数。
-
max: 返回两个元素中较⼤⼀个。重载版本使⽤⾃定义⽐较操作。
-
min: 返回两个元素中较⼩⼀个。重载版本使⽤⾃定义⽐较操作。
-
max_element: 返回⼀个ForwardIterator,指出序列中最⼤的元素。重载版本使⽤⾃定义⽐较操作。
-
min_element: 返回⼀个ForwardIterator,指出序列中最⼩的元素。重载版本使⽤⾃定义⽐较操作。
-
mismatch: 并⾏⽐较两个序列,指出第⼀个不匹配的位置,返回⼀对iterator,标志第⼀个不匹配元素位置。如果都匹配,返回每个容器的last。重载版本使⽤⾃定义的⽐较操作。
#include <iostream> #include <iomanip> #include <string> #include <list> #include <vector> #include <algorithm> using namespace std; void print(int val) { cout << val << endl; } bool comp(int val) { if(val > 10) return true; else return false; } int main(int argc, char const *argv[]) { vector<int> vec{11, 55, 6, 7, 3, 66, 9}; // 以第一个集合为准来比较第二个集合,如果两个集合相等则返回两个集合的end() vector<int> vec1{12,33,44}; vector<int> vec2{12,33,44,4}; auto ret = mismatch(vec1.begin(), vec1.end(), vec2.begin()); if(ret.first == vec1.end() && ret.second == vec2.end()) cout << "两个集合相等" << endl; else cout << "两个集合不相等" << endl; // 比较两个集合是否完全一样或者检测第一个集合是不是第二个集合的子集 auto ret1 = equal(vec1.begin(), vec1.end(), vec2.begin()); cout << ret1 << endl; cout << *max_element(vec1.begin(), vec1.end()) << endl;// 返回迭代器 cout << *min_element(vec1.begin(), vec1.end()) << endl; }
5)for_each遍历算法
for_each算法⽤于对指定范围内所有元素进⾏遍历,前提是容器必须要提供迭代器。该函数不能修改序列中的元素。
#include <iostream> #include <iomanip> #include <string> #include <list> #include <vector> #include <algorithm> using namespace std; void print(int val) { cout << val << endl; } int main(int argc, char const *argv[]) { vector<int> vec{11, 55, 6, 7, 3, 66, 9}; // 集合中的每个元素都需要通过第三个形参传过去进行处理 for_each(vec.begin(), vec.end(), print);// 列举每一个数据 return 0; }
标准模板库之迭代器
迭代器(Iterator)是指针(pointer)的泛化,它允许程序员以相同的⽅式处理不同的数据结构(容器),提供了类似指针的操作(诸如++、*、->运算符)或者说迭代器是⼀种检查容器内元素并遍历元素的数据类型。
C++ 的迭代器与 Python 的迭代器不同,它并不提出⼀套新的接⼝,⽽是尽可能模仿指针的⾏为。
不同容器的迭代器,其功能强弱有所不同,定义的操作不一样。常用的迭代器按功能强弱分为输入、输出、正向、双向、随机访问5种类型。
注意:所有迭代器都支持++运算符,如p++和++p
-
Input Iterator:只能单步向前迭代元素,不允许修改由该类迭代器引⽤的元素。
-
输⼊迭代器⽤于从⼀个对象中不断读出元素。⽀持++与*操作符。
-
格式:istream_iterator<要从流中读取的数据类型> 迭代器名(绑定的流);//创建的时候,就会调⽤标准输⼊
-
-
Output Iterator:该类迭代器和Input Iterator极其相似,也只能单步向前迭代元素,不同的是该类迭代器对元素只有写的权⼒。向对象不断添加元素。
-
格式:ostream_iterator<要从流中读取的数据类型> 迭代器名(绑定的流,元素分隔符);
-
输出流迭代器必须绑定流!
-
-
正向迭代器。假设 p 是⼀个正向迭代器,则 p ⽀持以下操作:++p,p++,*p。此外,两个正向迭代器可以互相赋值,还可以⽤==和!=运算符进⾏⽐较
-
双向迭代器。双向迭代器具有正向迭代器的全部功能。除此之外,若 p 是⼀个双向迭代器,则–p和p–都是有定义的。–p使得 p 朝和++p相反的⽅向移动。
-
随机访问迭代器也是双向迭代器,但能够在序列中的任意两个位置之间进⾏跳转,如指针、使⽤vector的begin()、end()函数得到的迭代器.
-
p+=i:使得 p 往后移动 i 个元素。
-
p-=i:使得 p 往前移动 i 个元素。
-
p+i:返回 p 后⾯第 i 个元素的迭代器。
-
p-i:返回 p 前⾯第 i 个元素的迭代器。
-
p[i]:返回 p 后⾯第 i 个元素的引⽤
-
两个随机访问迭代器 p1、p2 还可以⽤ <、>、<=、>= 运算符进⾏⽐较。p1<p2的含义是:p1 经过若⼲次(⾄少⼀次)++操作后,就会等于 p2。其他⽐较⽅式的含义与此类似
-
每种容器还定义了⼀种名为const_iterator的类型。该类型的迭代器只能读取容器中的元素,不能⽤于改变其值。