二、C++标准库
1.string
#include <iostream> #include <string>
1.1构造
//赋值构造 string str = "zxm"; //无参构造,构造一个空字符串,构造之后可以通过=进行赋值 string str2; //用char *构造 string str3("zxm"); //重复字符构造,输出11个a string(11,'a'); //拷贝构造 str2和str5内容是一样的 string str5(str2); //移动拷贝 str2没有内容,移动到str6里去了 string str6(move(str2)); //用指定范围内的字符进行构造,从第四个下表开始,取三个给str7 string str7(str2,4,3); //字符串拼接 string str8+str2+"111";
1.2 元素访问
string str="hello world"; str[1]='E'; str.at(1)='E'; //第一个字符 cout << str.front() << endl; //最后一个字符 cout << str.back() << endl; //data() c_str 获取数据 cout << str.data() <<endl;
1.3容量操作
//判断是否为空 返回0、1 true、false string str = "hello world"; cout << boolalpha << str3.empty() <<endl; //字符串中的大小和长度 cout << str.size() << endl; cout << str.length() << endl; //字符串对象最大的容量 cout str.max_size() << endl; //截取前几个字符 cout << str.resize(10) << endl; //已经分配的内存容量 cout << str.capacity() << endl; //分配内存大小 str.reserve(); //没有用到的内存释放掉 str.shrink_to_fit();
1.4迭代器
迭代器:为容器类提供统一的遍历接口,不用管容器底层内存管理方式。
1.(正向)迭代器 iterator
2.(正向)只读迭代器 const_iterator
3.反向迭代器 reverse_iterator
4.反向只读迭代器 const_reverse_iterator
string str = "hello world"; //指向第一个位置 string::iterator it = str.begin(); //输出h cout << *it <<endl; //输出l it+=3; cout << *it < endl; //可以修改,如果是只读迭代器的话,就不可以修改了 *it ='E'
//循环遍历 for(;it!=str.end();it++){ cout << *it <<endl; }
反向迭代器:
string::reverse_iterator it = str.rbegin(); //循环遍历 for(;it!=str.rend();it++){ cout << *it <<endl; }
1.5string比较相关操作
//== string str="hello world"; string str2="Hello World"; cout << std::boolalpha <<(str==str2) <<endl; //返回true or false //compare cout << str.compare(str2) <<endl; //判断是否以指定的内容开始或者结尾 cout << boolalpha << str.starts_with("hello") << endl; cout << boolalpha << str.ends_with("jpg") <<endl; //可以判断后缀名 //判断字符串中是否含有某些内容 cout << boolalpha << str.contains("txt") <<endl;
1.6插入和删除操作
string str="hello world"; //在现有的字符串基础上再加E str.push_back("E"); //把最后一个字符串给删除 str.pop_back(); //在字符串基础上追加内容,就是在最后开始追加 str.append(3,'A'); //追加三个A str.append(3,'A').append(2,'B'); str.append(str2); //从第三个字符开始追加 str.append(str2,3); str.append("Nice"); str.append(str2.begin(),str2.begin()+3); //insert() 在指定位置插入字符 str.insert(2,3,'C'); //从索引为2开始插入三个C str.insert(2,str2,5,2); //清除字符串的所有内容 str.clear(); //清楚指定位置 str.erase(2,2); //从2开始 清除两个字符 str.erase(str.begin()+2); //从str开始的第二个字符开始清除 ser.erase(str.begin()+2,str.begin()+5); //清除[2,5)字符
1.7 替换子串
string str = "hello world"; string str2="北国风光"; string str3 = "hello Cpp"; str.replace(2,1,str2) ; //索引为2的字符替换一个字符 替换成str2 he北国风光lo world //取字符串 cout << str.substr(3) << endl; //从索引3开始提取 cout << str.substr(3,2) << endl; //从索引3开始提取2个字符
1.8查找
string str = "AAABBBCCCDDD" //返回查到的第一个字符的索引号 找不到返回npos(-1) cout << str.find("AA") <<endl; int index = str.find("KDKD"); if(string::npos==index){ cout << "没找到" << endl; }else{ cout << "找到了" <<endl; } //从后面往前找 str.rfind(); //找最先出现的字符的位置 str.find_first_of("EECD"); //返回B的索引 str.find_first_not_of("AAA"); //从后往前找 find_last_of(); find_last_not_of();
1.9其他操作
to_string(); //将数组转化为string str+to_string(12); //将string转换为数字 cout << stoi(str2,&n,16) << endl; //将str2转换为16进制的数字,返回转换的个数n cout << n << endl; //将string转换为浮点型:DOUBLE,float cout << stod(str2) << endl; stof; //计算hash值 cout << hs(str) <<endl;
1.10 string_view
const char *s="望长城内外"; string_view sv = s;
这样 sv和s都是指向同一个地址。否则,正常的string会再会创建另一个地址把内容存进去 节省了内存空间
//移除六个字节 sv.remove_prefix(6); cout << sv << endl; //从后面移除字节数 sv.remove_suffix(6);
2.容器
2.1array
静态的连续数组
//头文件 #include<array> //创建 array<int,5> arr = {1,2,3,4,5}; cout << arr[0] <<endl; cout << arr.at(0) <<endl;
//填充 将这五个位置都给填充上 array<int,5> arr; //可以存储自己定义的数据类型 arr.fill(111); for(auto &n :arr){ cout << n << endl; //输出:111 111 111 111 111 } //使用迭代器遍历 array<int,5>::iterator it = ar.begin(); for(;it!=arr.end();it++){ cout << *it << endl; }
2.2vector
动态的连续数组 大小是变化的
#include<vector> //声明 vector<int> vec; //int 可以替换成对象 cout << vec.size() << endl; cout << vec.capacity() << endl; for(auto &n : vec){ cout << n << "\t"; } //分配了三个元素的大小 用100来填充 vector<int> vec(3,100); //分配了10个元素的大小 用0来填充 vector<int> vec(10);
#include<vector> //声明 vector<int> vec(10); //分配十个内存大小 //插入 vec.push_back(10); //输出地址 cout << (uintptr_t)vec.data() << endl; //插入 vec.push_back(122); //输出地址 cout << (uintptr_t)vec.data() << endl; //大小 cout << vec.size() << endl; //分配的大小 cout << vec.capacity() << endl; for(auto &n : vec){ cout << n << "\t"; //输出 } //分配了三个元素的大小 用100来填充 vector<int> vec(3,100); //分配了10个元素的大小 用0来填充 vector<int> vec(10);
1.调用无参构造进行初始化
调用了三参构造和移动构造,移动构造将临时变量移动到地址
vector<User> vec(3); //在末尾插入user vec.push_back(User(1,"tom",22));
2.调用拷贝构造
vector<User> vec(3); User u1(1,"tom",22); vec.push_back(u1); User u2(1,"Jey",22); vec.push_back(u2);
首先是u1的三参构造,然后就是拷贝对象,因为要拷贝到里面那个位置上
首先是u2的三参构造,然后就是拷贝对象。然后u1又进行了一次对象拷贝
内存分配的问题。给u2的内存分配完成后,将u1拷贝到u2的前面的那个位置
3.能不能不进行拷贝或者移动
vec.emplace_back(2,"jj",21); //插入
只进行了一次三参构造
vec.emplace_back();
只进行了一次无参构造
vec.emplace(vec.begin()+1,3,"Le",4);
3.单向链表-forward_list
声明:
#include<forward_list>
forward_list<int> fs = {2,4,1,9,3} forward_list<int> fs2 = {5,8,1,6,9}
只能通过自增的方式一步一步的往后移
forword_list<int>::iterator it = fs.begin(); it++;
擦除元素
it++; it++; fs.erase_after(it); // 擦除it后面的元素 9被擦除了 for(auto &i : fs){ cout << i << "\t"; }
合并元素
fs.merge(fs2); //fs和fs2合并在一起 但是是无序的 //可以先排序,再合并 fs.sort(); fs2.sort(); fs.merge(fs2); //这样合并之后就是有序的了 把fs2合并到fs里面 fs2就没有内容了
指定位置合并
fs.splice_after(it,fs2); //在1后面把指定的链表插入进去
移除指定的值
fs.remove(8); //移除8
//根据条件移除对应的内容
bool pre(const int &n){ return n<4; } fs.remove_if(pre); //把<4的内容移除掉 对fs的每个元素用提供的函数做判断 fs.remoce_if([] (const int& n){return n<4});
//升序
fs.sort();
//逆序,降序
bool cmp(const int &a,const int &b){ return a>b; } fs.sort(cmp); fs.sort([](const int &a,const int &b){return a<b});
greater<int> gt; fs.sort(gt); fs.sort(greater<int>());
//移除连续重复的元素
fs.unique();
4.list双链表
和单链表类似
list<int> ls = {1,3,4,5,6,7} list<int>::iterator it = ls.begin(); it++; it++; it--; cont << *it <<endl;
5.栈stack
引入头文件
#include<stack>
stack<string> str_stack; str_stack.push("zzz"); //入栈 简单的数据类型用push
//取栈顶数据 string str = str_stack.top(); //栈顶元素删除 str_stack.pop(); cout << str << endl;
//出栈
while(!str_stack.empty()){ string str = str_stack.top(); str_stack.pop(); cout << str << endl; }
6.队列queue
引入头文件
#include<queue>
queue<const char *>q; q.qush("张三");
q.emplace("赵六"); //多个数据
获取第一个元素
const char *s=q.front(); //出队 q.pop(); cout << s << endl;
循环一次出队
while(!q.empty()){ const char *s=q.front(); //出队 q.pop(); cout << s << endl; }
7.双端队列priority_queue 优先队列
priority_queue<int> q; q.push(10); q.push(20); q.push(15); while(!q.empty()){ auto top = q.top(); //获取队首元素 q.pop(); cout << top <<endl; } 输出:20 15 10 按照从大到小输出
可以从小到大输出
priority_queue<int,vector<int>,greater<int>> q; //参数:类型,容器,默认vector,比较器 q.push(10); q.push(20); q.push(15); 输出: 10 15 20
//如果存入的数据是自定义的类,那就需要重载一个<运算符 就可以用默认的比较器比较大小 priority_queue<Persion> q; q.emplace(60,"Tom"); q.emplace(70,"Jerry"); q.emplace(65,"Lee"); //根据年龄比较大小 //重载<运算符 bool operator < (const Persion &p1,const Persion &p2){ return p1.getAge() < p2.getAge(); } 输出: 70 65 60
自定义比较器
方式一:模仿less定义的比较器
struct Comp{ bool operator()(const Persion &p1,const Persion &p2) const{ return p1.getAge() < p2.getAge(); } }; priority_queue<Persion,vector<Persion>,Comp> q;
方式二:定义普通比较函数
bool cmp(const Persion &p1,const Persion &p2){ return p1.getAge() < p2.getAge(); } typefdef bool (*cmp2) (const Persion &p1,const Persion &p2); //宏定义函数指针 priority_queue<Persion,vector<Persion>,cmp2> q(cmp);
方式三:通过lambda表达式定义比较函数
auto cmp3 = [](const Persion& p1,const Persion& p2){ return p1.getAge() < p2.getAge(); }; priority_queue<Persion,vector<Persion>,decltype(cmp3)> q(cmp3); //decltype自动类型推导
8.set
不重复
#include<set>
set<int> st {1,3,4,3} //输出:1 3 4 重复的删除掉了
vector<int> vec = {1,1,2,2,3,3,4,4,5,5}; set<int> st2(vec.begin(),vec.end()); //输出:1 2 3 4 5
判断两个元素是否相等
如何两个对象a和b相互不比较小于对方:!a<b && !a>b 那么认为他们等价
//插入元素节点 st.insert(0);
//把指定元素提取出来
set<int>::node_type node = st.extract(3); //把3提取出来 cout << node.value() << endl;
//合并集合 合并的是原先集合没有的
st.marge(st2)
返回元素的个数
st.count()
find:查找指定的值
返回指定的值的下边界,返回的是对应值的下标 返回的是迭代器
cout << *st.lower_bound(3) <<endl;
返回指定的值的上边界,返回的是对应值的下标 返回的是迭代器
cout << *st.upper_bound(3) <<endl;
9.map
pair类型
pair<string,int> p; p.first = "AGB"; p.second=25;
map返回的pair类型
定义:
#include<map> map<string,int> mp = {{"张三,120"},{"李四",210},{"王五",200}}; //迭代 for(anto it = mp.begin();it!=mp.end();it++){ cout << m->first << "-->" << m->second <<endl; // 张三,120 } //循环 for(anto &m :mp){ cout << m.first << "-->" << m.second <<endl; // 张三,120 }
通过key获取值value
cout << mp.at("张三") << endl; mp["张三"]
还可以赋值
mp.at("张三")=100;
插入,如果存在,不进行任何操作
mp.insert(pair<string,int>("赵六",3330)); mp.try_emplace("张三",333);
修改,如果不存在,则插入
mp.insert_or_assign("张三",333);
10.bitset
类模板bitset表示一个N位的固定大小的序列,可以用标准逻辑运算符操作bitset 并将他与字符串和整数相互转换。
#include<bitset> bitset<8> bst("1010111"); //输出二进制 bitset<8> bst2(121u); //转换成二进制 cout << bst << endl; cout << bst2 << endl;
cout << bst2[0] << endl; 取第一个值 从右往左
检测对应的位置 如果位1返回true 如果为0返回false
bst.test(0); //判断第一个元素是1还是0 bst.all(); //全部为1返回true bst.any(); //其中 存在1就返回true bst.none(); //全部为0返回true
统计1的个数
bst.count(); bst.size(); //返回多少位
把指定位置设定位1
bst.set(0); //把第一位设置位1
把指定位置设定为0
bst.reset(0);
进行0 1 反转 0的位置变为1 1的位置变为0
bst.filp();
转成字符串
bst.to_string();
转成整形long
bst.to_ulong(); bst.to_ullong();
左移
bst2 = bst2 << 2; bst<<=2;
按位与
bst & bst2
按位或
bst | bst2
按位异或 不同的为1
bst ^ bst2
取反
~bst
11.bit
判断是大端存储还是小端存储
cout << boolalpha << (endian::native == endian::big) << endl; //判断是大端吗 cout << boolalpha << (endian::native == endian::little) << endl; //判断是小端吗
3.算法
1.for_each()
for_each(@begin,@end,f(0)); //开始 结束 函数
作用:针对迭代器指定的每一个元素都来调用一下f函数来进行处理
定义
#include<algorithm>
vector<int> vec = {46,72,91,89,36,11}; int a[10] = {60,51,10,63,82,23};
for_each(vec.begin(),vec.end(),fun);
void fun(const int& n){ cout << n <<endl; }
for_each(vec.begin(),vec.end(),[](const int& n){ cout << n <<endl; });
for_each(a,a+10,f2);
for_each_n(a,3,f2); //对前三个值进行操作
2.any_of() all_of() none_of()
vector<int> vec = {46,72,91,89,36,11}; int a[10] = {60,51,10,63,82,23}; cout << any_of(vec.begin(),vec.end(),check) <<endl; //只要存在满足函数的条件就返回true cout << all_of(vec.begin(),vec.end(),check) <<endl; //所有的值满足条件返回true、 cout << none_of(vec.begin(),vec.end(),check) <<endl; //没有条件满足返回true
bool check(const int& n){ return n >90 }
3.count() count_if()
vector<int> vec = {46,72,91,89,36,11}; int a[10] = {60,51,10,63,82,23}; cout << count(vec.begin(),vec.end(),46); //统计46的值有多少个 cout << count_if(vec.begin(),vec.end(),check) //统计有多少个大于90 的
bool check(const int& n){ return n >90 }
4.find() find_if()
vector<int> vec = {46,72,91,89,36,11}; anto it = find(vec.begin(),vec.end(),91); //查找91 if(it==vec.end()){ cout << "没找到" << endl; }else{ cout << *it <<endl; } anto it = find_if(vec.begin(),vec.end(),check); //返回第一个符合值的索引值
5.queal() mismatch()
vector<int> vec = {46,72,91,89,36,11}; vector<int> vec2 = {46,72,91,89,36,11}; cout << boolalpha <<equal(vec.begin(),vec.end(),vec2.begin(),vec2.endl()) << endl; //判断vec和vec2是否相等
auto pa = mismatch(vec.begin(),vec.end(),vec2.begin()); cout << pa.first -vec.begin() << endl; // 判断第几个位置不一样了
6.二分查找
排序:
vector<int> vec = {46,72,91,89,36,11}; vector<int> vec2 = {46,72,91,89,36,11}; int a[10] = {60,51,10,63,82,23,98}; //排容器 sort(vec.begin(),vec.end()); //升序 for_each(vec.begin(),vec.end(),print); //排数组 sort(a,a+7,greater<int>()); //降序 for_each(vec.begin(),vec.end(),print);
void print(const int &n){ cout << n <<"\t" }
binary_search():在排好序的容器中,使用二分查找
查到返回true 查不到返回false
cout << binary_search(vec.begin(),vec.end(),63) <<endl; //查到63返回true
只要能找到就返回true 不需要连着
cout << includes(vec.begin(),vec.end(),vec2.begin(),vec2.end()) << endl; //查找vec中是否还有vec2
7.最大值 最小值
最大值max
cout << max({60,51,10,63,82,23,98}) << endl; //输出:98 cout << min({60,51,10,63,82,23,98}) << endl; //最小值
返回迭代器
vector<int> vec = {46,72,91,89,36,11};
auto it = max_element(vec.begin(),vec.end()); //最大值 auto it = min_element(vec.begin(),vec.end()); //最小值 cout << *it <<endl;
判断是否已经排好序了
cout << is_sorted(vec.begin(),vec.end()) << endl;
判断升序的元素的下一个元素
cout << is_sorted_until(vec.begin(),vec.end()) << endl; //输出89 ,因为 46 72 91 是升序的
判断奇数和偶数是不是分好组了,还是交叉
cout << is_partitioned(vec.begin(),vec.end()) << endl;
8.复制copy,移动move、交换swapping元素
1.复制copy
vector<int> vec = {46,72,91,89,36,11}; int a[10]={0}; auto end = copy(vec.begin(),vec.begin()+5,a); for_each(a,end,print); //输出:46,72,91,89,36
指定复制多少个元素
auto end = copy_n(vec.begin(),5,a);
从后往前复制
vector<int> vec = {46,72,91,89,36,11}; int a[10]={0}; auto end = copy_backward(vec.begin(),vec.begin()+5,a+10); for_each(a,a+10,print); //输出:0,0,0,0,0,46,72,91,89,36
按条件复制
vector<int> vec = {46,72,91,89,36,11}; int a[10]={0}; auto end = copy_if(vec.begin(),vec.begin()+5,a,check); for_each(a,end,print); //输出:46,72,36
bool check(const int& n){ return n%2==0 }
2.移动move
vector<string> v2 = {"A","B","C","D","E","F"}; string s2[5]; move(v2.begin(),v2.end()-3,s2); for(auto &&i :v2){ cout << i <<"\t"; }
3.随机采样sample
//从里面随机取出数据 vector<int> vec = {46,72,91,89,36,11}; int a[10]={0}; auto end = sample(vec.begin(),vec.end(),a,3,default_random_engine(random_device{}());//随机取3个数 for_each(a,end,print); //输出:46,72,89,
4.交换 iter_swap
vector<int> vec = {46,72,91,89,36,11}; int a[10]={0}; iter_swap(vec.begin(),a); for_each(a,a+10,print); 46,0,0,0,0,0 cout << endl; for_each(vec.begin(),vec.end(),print); 0,72,91,89,36,11
自身两个元素进行交换
vector<int> vec = {46,72,91,89,36,11}; iter_swap(vec.begin(),vec.begin()+1); //72,46,91,89,36,11
交换一个范围
iter_ranges(vec.begin(),vec.begin()+5,a);
4.数值库-随机数生成
引入头文件
#include<random>
random_device rd; //随机生成一个数 cout << rd() <<endl; //确定随机数生成器 default_random_engine re(rd()); //设置随机数的分布 uniform_int_distribution<> dis{20,30}; //随机生成20-30之间的数 均匀分布 //生成随机数 cout << dis(re) <<endl;
5.通用工具库
5.1-智能指针
用了智能指针就不用手动释放了
int *ptr = new int; unique_ptr<int> p(ptr); *p = 10; cout << *p <<endl;
nuique_ptr<User> p(new User(1,"Tom",11)); nuique_ptr<User> p = make_unique<User>(1,"Tom",11); nuique_ptr<User[]> p = make_unique<User[]>(10); //10:数组大小
5.2 tuple
#include<tuple> int main(){ tuple<int,int,string> tp = {12,100,"Tom"}; //初始化 cout << get<1>(tp) << endl; //访问方式,获取第二个数:100 auto [a,b,str] = tp; return 0; }
5.3 function
C++ 标准库中的 <functional>
头文件提供了一组函数模板,这些模板允许我们使用函数对象(function objects)作为参数传递给算法,或者作为算法的返回值。函数对象是那些重载了 operator()
的对象,它们可以像普通函数一样被调用。
#include <iostream> #include <functional> void greet() { std::cout << "Hello, World!" << std::endl; } int main() { std::function<void()> f = greet; // 使用函数 f(); // 输出: Hello, World! std::function<void()> lambda = []() { std::cout << "Hello, Lambda!" << std::endl; }; lambda(); // 输出: Hello, Lambda! return 0; }
使用 std::bind
std::bind
允许创建一个可调用对象,它在调用时会将给定的参数绑定到一个函数或函数对象。
#include <iostream> #include <functional> int add(int a, int b) { return a + b; } int main() { auto bound_add = std::bind(add, 5, std::placeholders::_1); //std::placeholders::_1之绑定一个参数 std::cout << bound_add(10) << std::endl; // 输出: 15 return 0; }
6.文件系统库
1.path
引入头文件
#include<filesystem> namespace fs = std::filesystem;
fs::path pth="D::\\Users\\ZZ"; //获取当前路径 fs::path pth2=fs::current_path(); cout << pth2 <<endl;
追加路径
fs::path pth="D::\\Users\\ZZ"; pth.append("aaa"); //输出:D::\\Users\\ZZ\\aaa pth /=aaa; //输出:D::\\Users\\ZZ\\aaa pth.concat("aaa") //D::\\Users\\ZZaaa
移除文件名
pth.remove_filename(); //只会移除最后一个,不会判断是否是文件名
替换文件名
pth.replace_filename("KKK"); //只会移除最后一个,不会判断是否是文件名 pth.replace_extension("jpg"); //替换扩展名
转换成string
cout << pth.string() <<endl;
root_path:返回根路径
relative_path:返回相对路径
filename:返回文件名
stem:返回文件主干部分
extension:返回文件扩展名
empty:检查路径字符串是否问空
2.directory_iterator
fs::path pth=fs::current_path(); //获取当前目录下的所有内容 fs::directory_iterator di(pth); for(const fs::directory_entry &entry:di){ cout << entry.path() << endl; //输出所有目录 } entry.is_directory(); //判断是否是目录 entry.is_regular_file(); //判断是否为文件 entry.file_size() //文件大小
用迭代器的方法输出
for(anto it = begin(di);it!=end(di);it++){ cout << it->path() <<endl; }
子目录也输出了
fs::recursive_directory_iterator di(pth);
3.其他
1.文件或者目录复制:copy
fs::copy(pth / "text.cpp", pth / "haha.cpp"); //把text.cpp复制到了haha.cpp
2.创建目录
fs::path pth = fs::current_path(); cout << fs::create_directory(pth / "aaa") <<endl; //在当前目录下创建aaa目录 cout << fs::create_directories(pth / "aaa/cc") <<endl; //创建多层目录
7.chrono库:事件日期工具
引入头文件
#include<chrono>
1.时间点
//获取当前时间:1970年到现在时间的时间段 纳秒级别 chrono::time_point<chrono::system_clock,chrono::nanoseconds> t1 = chrono::system_clock::now(); //获取当前时间:1970年到现在时间的时间段 chrono::system_clock::time_point t1 = chrono::system_clock::now(); //转换成秒级别 chrono::time_point<chrono::system_clock,chrono::seconds> t2 = chrono::time_point_cast<chrono::seconds>(t1); cout << t2.time_since_epoch() << endl;
2.时间段:duration
头文件
#include<ratio>
chrono::duration<int> d(10); cout << d <<endl; //10s chrono::duration<int,ration<60>> d(10); cout << d <<endl; //10min chrono::duration<int,ration<3600>> d(10); cout << d <<endl; //10h chrono::duration<int,std::nano> d(10); //10ns
auto duration = std::chrono::seconds(5); //5s
chrono::steady_clock::time_point t1 = chrono::steady_clock::now(); chrono::steady_clock::time_point t2 = chrono::steady_clock::now(); chrono::nanoseconds d = t1.time_since_epoch(); cout << d <<endl; chrono::duration<double> d = t2-t1
#include<thread> cout << "start" << endl; this_thread::sleep_for(chrono::seconds(2));//本线程休眠多久 cout << "end" << endl;
8.线程库
1.thread
引入头文件
#include<thread>
int i = 0; void tf(){ while(i<10){ cout << "子线程" << i <<endl; this_thread::sleep_for(chrono::milliseconds(10)); i++; } } int main(){ thread th(tf) //用thread声明一个对象,把需要启动的函数给放进去 while(i<10){ cout << "主线程进行中" << i <<endl; this_thread::sleep_for(chrono::milliseconds(10)); i++; } th.join(); //主线程等待 return 0; }
class A{ private: int i; public: void fn(){ i = 0; while(i<10){ cout << "A::fn" << i <<endl; i++; this_thread::sleep_for(chrono::milliseconds(10)); } } void fn2(int num){ cout << "A::fn num = " << num <<endl; num+=100; } }; int main(){ A a; //声明对象 thraed th(&A::fn,&a); //无参数:用类中的方法进行启动 需要启动的类里面的函数指针传给他 对应的对象的指针也传给他 thraed th(&A::fn2,&a,num); //有参数 把参数方后面 while(i<10){ cout << "主线程进行中" << i <<endl; this_thread::sleep_for(chrono::milliseconds(10)); i++; } th.join(); //主线程等待 return 0; }
如何让子线程改变全局变量的值,则
void fn2(int &num){ cout << "A::fn num = " << num <<endl; num+=100; } thraed th(&A::fn,&a,ref(num)); //使用引用包装器ref包装一下
问题:输出流对象和变量i会抢占他们的使用权
2.互斥量(mutex)
为了避免多个线程同时访问共享资源。这会避免竞争,并提供线程间的同步支持。
lock:锁定互斥
try_lock 尝试锁定互斥
unlock 解锁互斥
引入头文件
#include<mutex>
声明mutex对象
mutex mtx;
int i = 0; void tf(){ while(i<10){ mtx.lock(); //加锁 cout << "子线程" << i <<endl; i++; mut.unlock(); //解锁 this_thread::sleep_for(chrono::milliseconds(10)); } } int main(){ thread th(tf) //用thread声明一个对象,把需要启动的函数给放进去 while(i<10){ mtx.lock(); //加锁 cout << "主线程进行中" << i <<endl; i++; mut.unlock(); //解锁 this_thread::sleep_for(chrono::milliseconds(10)); } th.join(); //主线程等待 return 0; }
3.lock_guard
实现锁的自动管理
int i = 0; mutex mtx; void tf(){ while(i<10){ lock_guard<mutex> lock(mtx); //自动进行加锁,作用范围结束后解锁 cout << "子线程" << i <<endl; i++; this_thread::sleep_for(chrono::milliseconds(10)); } } int main(){ thread th(tf) //用thread声明一个对象,把需要启动的函数给放进去 while(i<10){ lock_guard<mutex> lock(mtx); //自动进行加锁,作用范围结束后解锁 cout << "主线程进行中" << i <<endl; i++; this_thread::sleep_for(chrono::milliseconds(10)); } th.join(); //主线程等待 return 0; }
4.unique_lock
unique_lock<mutex> lk(mtx); //自动进行加锁,作用范围结束后解锁 unique_lock<mutex> lk(mtx,std::defer_lock); //延迟锁定 必须手动加锁 lk.lock()
3.条件变量
主线程和子线程一个一次交替进行
wait:阻塞当前线程 直到条件变量被唤醒
wait_for: 阻塞当前线程,直到条件变量被唤醒,或到指定的时长以后
wait_until:阻塞当前线程,直到条件变量被唤醒,或直到抵达指定时间点
引入头文件
#include<condition_variable> condition_variable cv; //条件变量对象 bool sub_run = false; //控制子线程或者主线程谁先运行
int i = 0; mutex mtx; void tf(){ while(i<10){ unique_lock<mutex> lk(mtx); //定义一个锁 cv.wait(lk,[&] {return sub_run;});//在这个地方等待,如果sub_rune为true,那么他会往下执行,如果为fasle那么他会 一直等着 [&] {return sub_run;} cout << "子线程" << i <<endl; i++; this_thread::sleep_for(chrono::milliseconds(10)); sub_run = false; cv.notify_all(); //唤醒等待的线程 } } int main(){ thread th(tf) while(i<10){ cv.wait(lk,[&] {return !sub_run;}); cout << "主线程进行中" << i <<endl; i++; this_thread::sleep_for(chrono::milliseconds(10)); sub_run = false; cv.notify_all(); //唤醒等待的线程 } th.join(); //主线程等待 return 0; }
启动三个线程轮流输出ABC
#include <iostream> #include <thread> #include <mutex> #include <condition_variable> using namespace std; class PrintChar { private: int loop_num; int index; mutex mtx; //互斥量 condition_variable cv; //条件变量 public: PrintChar(int loop_num) : loop_num(loop_num), index(0) {} void A() { for (int i = 0; i < loop_num; i++) { unique_lock<mutex> ulk(mtx); cv.wait(ulk, [&]() { return index % 3 == 0; }); cout << "A"; this_thread::sleep_for(chrono::milliseconds(100)); index ++; cv.notify_all(); } } void B() { for (int i = 0; i < loop_num; i++) { unique_lock<mutex> ulk(mtx); cv.wait(ulk, [&]() { return index % 3 == 1; }); cout << "B"; this_thread::sleep_for(chrono::milliseconds(100)); index ++; cv.notify_all(); } } void C() { for (int i = 0; i < loop_num; i++) { unique_lock<mutex> ulk(mtx); cv.wait(ulk, [&]() { return index % 3 == 2; }); cout << "C"; this_thread::sleep_for(chrono::milliseconds(100)); index ++; cv.notify_all(); } } }; int main() { int num = 0; cout << "请输入循环次数:" ; cin >> num; PrintChar p(num); thread th1(&PrintChar::A, &p); thread th2(&PrintChar::B, &p); thread th3(&PrintChar::C, &p); th1.join(); th2.join(); th3.join(); return 0; }
4.单次调用
声明once_flag这个标志
once_flag flag;
void tf(){ call_once(flag,init,this_thread::get_id()); }
5.Future
标准库提供了一些工具来获取异步任务(即在单独的线程中启动的函数)的返回值,并捕捉其所抛出的异常。
引入头文件
#include<future>
#include <vector> #include <thread> #include <future> #include <iostream> #include <chrono> using namespace std; void sumfromto(int start, int end, promise<int> ps) { int sum = 0; for (int i = start; i <= end; i++) { sum += i; } ps.set_value(sum); //可以放在其他地方 } int main() { promise<int> ps; //声明promise的变量 future<int> sum_future = ps.get_future(); //声明future变量 thread th(sumfromto, 1, 100, move(ps)); //声明线程,移动的方式传递过去 cout << sum_future.get() << endl; //获取 th.join(); }
6.async
也是开启一个线程 ,返回的future
void sumfromto(int start, int end) { int sum = 0; for (int i = start; i <= end; i++) { sum += i; } return sum; } int main() { //future<int> f1 = async(paralell_sum,1,100); future<int> f1 = async(std::launch::deferred,paralell_sum,1,100); //子线程延迟启动,在调用get时启用 cout << "主线程进行" << endl; cout << f1.get() << endl; //获取对应的值 return 0; }
线程开启方式:thread, packaged_task, async
7.原子操作
它允许无锁并发编程
声明一个原子变量
#include<atomic> atomic_int total(0);
#include <iostream> #include <vector> #include <chrono> #include <thread> #include <mutex> #include <atomic> using namespace std; // int total(0); atomic_int total(0); mutex mtx; void fun() { for (int i = 0; i < 1000000; i++) { // mtx.lock(); total ++; total --; // mtx.unlock(); } } int main() { auto start = chrono::steady_clock::now(); vector<thread> vec; for (int i = 0; i < 8; i++) { vec.emplace_back(fun); } for (int i = 0; i < 8; i++) { vec[i].join(); } cout << "total = " << total << endl; auto end = chrono::steady_clock::now(); auto dur = chrono::duration_cast<chrono::milliseconds>(end - start); cout << dur << endl; return 0; }