做电子书屋的网站,深圳龙华是低风险区吗,网站外链数怎么查,恩施公司做网站#x1f493;博主CSDN主页:麻辣韭菜#x1f493; ⏩专栏分类#xff1a;C修炼之路⏪ #x1f69a;代码仓库:C高阶#x1f69a; #x1f339;关注我#x1faf5;带你学习更多C知识 #x1f51d;#x1f51d; 目录 前言 一、新的类功能
1.1默认成员函数—… 博主CSDN主页:麻辣韭菜 ⏩专栏分类C修炼之路⏪ 代码仓库:C高阶 关注我带你学习更多C知识 目录 前言 一、新的类功能
1.1默认成员函数——移动构造、移动赋值
1.2强制生成默认函数的关键字default:
1.3const延长生命周期的问题 1.4禁止生成默认函数的关键字delete:
1.5 其他新功能
缺省值 委托构造
二、可变参数模板
利用递归函数展开
逗号表达式展开参数包
三、Lambda
lambda表达式 lambda表达式语法 1.捕捉列表
函数对象与lambda表达式 前言 上篇重点讲解了右值引用本篇的可变参数模板和Lambda也是11里面非常有用的。如果学会这两个以后编程会感觉非常的爽。废话不多说直接开始 一、新的类功能
1.1默认成员函数——移动构造、移动赋值
在C11后类又新增了两个默认成员函数移动构造和移动赋值。 针对移动构造函数和移动赋值运算符重载有一些需要注意的点如下 如果你没有自己实现移动构造函数且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任 意一个。那么编译器会自动生成一个默认移动构造。默认生成的移动构造函数对于内置类 型成员会执行逐成员按字节拷贝自定义类型成员则需要看这个成员是否实现移动构造 如果实现了就调用移动构造没有实现就调用拷贝构造。 如果你没有自己实现移动赋值重载函数且没有实现析构函数 、拷贝构造、拷贝赋值重载中 的任意一个那么编译器会自动生成一个默认移动赋值。默认生成的移动构造函数对于内 置类型成员会执行逐成员按字节拷贝自定义类型成员则需要看这个成员是否实现移动赋 值如果实现了就调用移动赋值没有实现就调用拷贝赋值。(默认移动赋值跟上面移动构造 完全类似) 如果你提供了移动构造或者移动赋值编译器不会自动提供拷贝构造和拷贝赋值。 #include String.hclass Person
{
public:Person(const char* name , int age 0):_name(name), _age(age){}/*Person(const Person p):_name(p._name),_age(p._age){}*//*Person operator(const Person p){if(this ! p){_name p._name;_age p._age;}return *this;}*//*~Person(){}*/private:gx::string _name;int _age;
};
int main()
{Person s1;Person s2 s1;Person s3 move(s1);Person s4;s4 move(s2);return 0;
} 如果把上面代码的拷贝构造 赋值重载、析构的任意一个代码注释取消注释就会得到下面结果 为什么我们一但自己写了默认成员函数编译器就不会自己生成移动构造、赋值 如果我们实现了 析构、拷贝构造、赋值重载就证明当前的类中涉及到了 动态内存管理是需要自己进行 深拷贝 的编译器无能为力移动语义 也应该根据自己的实际场景进行设计所以编译器就没有自动生成 那如果有些场景就需要我们自己写拷贝构造、析构、赋值重载这些函数那怎么办
1.2强制生成默认函数的关键字default:
Person(Person p) default;
Person operator(Person p) default; C11后STL中所有容器都增加了移动构造和移动赋值
插入系列的函数也同样增加了右值的版本。 其他容器详情请看官网cplusplus.com 1.3const延长生命周期的问题
插入函数之所以会延长生命周期
当您创建一个临时对象并将其作为参数传递给函数时这个临时对象的生命周期通常只在表达式中有效。一旦表达式结束临时对象就会被销毁。但是如果这个临时对象被传递给一个需要更长时间使用它的函数比如一个需要对对象进行修改的函数那么就需要延长这个临时对象的生命周期。
在C中如果一个函数的参数是一个const类型这意味着函数不会修改这个对象。但是如果这个参数是通过引用传递的那么即使它是const它仍然需要在函数调用期间保持有效以便函数可以访问它。这就是所谓的生命周期延长。
既然可以延长对象生命周期那是不是也可以像这样下图这样返回的对象加const 从结果来看显然是不可以的。出现了野引用的问题。 所以说引用也不是安全的。 1.4禁止生成默认函数的关键字delete: 如果能想要限制某些默认函数的生成在C98中是该函数设置成private并且只声明补丁 已这样只要其他人想要调用就会报错。在C11中更简单只需在该函数声明加上delete即 可该语法指示编译器不生成对应函数的默认版本称delete修饰的函数为删除函数。 在person类中我们在拷贝构造函数后面加 delete 就无法再使用这个这个函数。
注意delete这个关键字只对默认成员函数有效
那什么样的类是不希望其他人来调用的它的默认成员函数
比如IO流 每个IO流对象的缓冲区都是不一样的随意拷贝都会造成资源混乱。
1.5 其他新功能
在C98中类中的内置类型是不对初始化的。而在C11中出现了缺省参数 可以给类的成员给缺省值。
缺省值 没有缺省值我们得到_a的值是随机值。
给定缺省值 1 委托构造
什么是委托构造 简单来说就是一个构造函数可以复用其他构造函数
class Person
{
public:Person(const char* name, int age):_name(name), _age(age){}Person(const char* name):Person(name, 18) // 委托构造{}private:gx::string _name; // 自定义类型int _age 1; // 内置类型
};int main()
{Person s1(张三);return 0;
} 这个委托构造了解一下就行了说白了还是要调用构造函数。
二、可变参数模板
相比C98的模板参数C11模板参数变成了不是固定的可以接受任意个类型。和printf函数的可变参数列表是类似的。只是这里的模板参数变成了类型。
下面就是一个基本可变参数的函数模板
// Args是一个模板参数包args是一个函数形参参数包
// 声明一个参数包Args...args这个参数包中可以包含0到任意个模板参数。
template class ...Args
void ShowList(Args... args)
{}
如果要知道参数包中个数怎么解决
templateclass ...Args
void ShowList(Args...args)
{cout sizeof...(args) endl;
}
int main()
{ShowList(1, x);
} 如何解析出参数包里面的值 由于语法不支持使用args[i]这样方式获取可变参数所以我们的用一些奇招来一一获取参数包的值。 利用递归函数展开 void ShowList()
{cout endl;
}
template class T ,class ...Args
void ShowList(const T val, Args ...args)
{cout __FUNCTION__ ( sizeof...(args) ) endl;cout val endl;ShowList(args...); //语法规定...必须在后面
}
int main()
{ShowList(1, A, std::string(sort));return 0;
} 上面结果确实调用3次ShowList这个函数再加上无参ShowList。
能不能不用模板参数T? 我就想直接用可变模板参数包可以 。直接再套一层。
void _ShowList()
{cout endl;
}
template class T, class ...Args
void _ShowList(const T val, Args... args)
{cout __FUNCTION__ ( sizeof...(args) ) endl;cout val endl;_ShowList(args...);
}
template class ...Args
void ShowList(Args... args)
{_ShowList(args...);
}int main()
{ShowList(1, A, std::string(sort));return 0;
} 逗号表达式展开参数包
template class T
void PrintArg(T t)
{cout t ;
}
//展开函数
template class ...Args
void ShowList(Args... args)
{int arr[] { (PrintArg(args), 0)... };cout endl;
}
int main()
{ShowList(1);ShowList(1, A);ShowList(1, A, std::string(sort));return 0;
}
在上篇的初始化列表 我们知道C11在arr这个数组创建时会初始化 列表里面的内容。实现的关键是逗号表达式。我们知道逗号表达式会按顺序执行逗号前面的表达式。
编译器通过解析变成下面表达式 {(printarg(args), 0)...}将会展开成((printarg(arg1),0), (printarg(arg2),0), (printarg(arg3),0), etc... ) 为什么要写出成 (Print(args), 0) 的形式这是一个逗号表达式目的是让整个式子最终返回 0用于初始化 arr 数组 如果不想加0也是可以的。 可变参数模板应用场景又是什么线程 我们知道C语言中回调函数的传参。C的方案是用的void* 而C11的线程库用可变参数模板对void* 这个指针进行封装。通过可变参数模板就可以快乐的传递任何参数。剩下的事交给编译器来干。
可变参数包还可以用于优化STL容器中插入函数。
以容器list为例 int main()
{listgx::string lt;gx::string s1(1111);lt.push_back(s1);lt.emplace_back(s1);return 0;
} 对于是深拷贝的类是没区别的。push_back和emplace_back二者没什么区别。
我们在来看看浅拷贝的类有没有影响
#include Date.h
int main()
{/*listgx::string lt;gx::string s1(1111);lt.push_back(s1);lt.emplace_back(s1);*/listDate lt2;Date d1(2024, 5, 20);Date d2(2024, 5, 21);lt2.push_back(d1);lt2.emplace_back(d2);return 0;} 也是没有差别。
那如果是 d1\d2是右值那 也是没区别
但是是下面这种就有区别了
emplace_back直接就是构造。这是因为可变参数包在参数传递的过程中参数包不会展开。直到构造函数才展开。其实这里可以理解成2023528它不是一个匿名对象在参数包的眼里它实际是3个整型。
再比如 下面这个。 有了可变参数包。编译器直接识别为const char* 的字符串。而不是一个匿名对象。
结论无脑用emplace_back就行。
三、Lambda
在C11之前我们如果要对数据进行排序怎么做用std::sort。
#include algorithm
#include functional
int main()
{int array[] { 4,1,8,5,3,7,0,9,2,6 };// 默认按照小于比较排出来结果是升序std::sort(array, array sizeof(array) / sizeof(array[0]));// 如果需要降序需要改变元素的比较规则std::sort(array, array sizeof(array) / sizeof(array[0]), greaterint());return 0;
}
如果我们要排序的是自定义类型那就需要用到仿函数。 struct Goods
{string _name; // 名字double _price; // 价格int _evaluate; // 评价Goods(const char* str, double price, int evaluate):_name(str), _price(price), _evaluate(evaluate){}
};
struct ComparePriceLess
{bool operator()(const Goods gl, const Goods gr){{return gl._price gr._price;} }
};
struct ComparePriceGreater
{bool operator()(const Goods gl, const Goods gr){return gl._price gr._price;}
};
int main()
{vectorGoods v { { 苹果, 2.1, 5 }, { 香蕉, 3, 4 }, { 橙子, 2.2,3 }, { 菠萝, 1.5, 4 } };sort(v.begin(), v.end(), ComparePriceLess());sort(v.begin(), v.end(), ComparePriceGreater());
} 生活中商品太多了难道每一种商品的排序都要写相应的仿函数是不是有点太麻烦假设我们不以价格来进行排序。现在要求按水果的名字排序是不是又要重写一个仿函数
有没有一种办法一劳永逸在C11推出了lambda
lambda表达式 int main()
{vectorGoods v { { 苹果, 2.1, 5 }, { 香蕉, 3, 4 }, { 橙子, 2.2,3 }, { 菠萝, 1.5, 4 } };sort(v.begin(), v.end(), [](const Goods g1, const Goods g2) {return g1._price g2._price; });sort(v.begin(), v.end(), [](const Goods g1, const Goods g2) {return g1._price g2._price; });sort(v.begin(), v.end(), [](const Goods g1, const Goods g2) {return g1._evaluate g2._evaluate; });sort(v.begin(), v.end(), [](const Goods g1, const Goods g2) {return g1._evaluate g2._evaluate; });
}上述代码就是使用C11中的lambda表达式来解决可以看出lambda表达式实际是一个匿名函
数。 lambda表达式语法 lambda 表达式书写格式 [capture-list] (parameters) mutable - return-type { statement } 1. lambda 表达式各部分说明 [capture-list] : 捕捉列表该列表总是出现在lambda函数的开始位置编译器根据[]来 判断接下来的代码是否为 lambda 函数 捕捉列表能够捕捉上下文中的变量供 lambda 函数使用 。 (parameters)参数列表。与普通函数的参数列表一致如果不需要参数传递则可以 连同 () 一起省略 mutable默认情况下lambda函数总是一个const函数mutable可以取消其常量 性。使用该修饰符时参数列表不可省略 ( 即使参数为空 ) 。 -returntype返回值类型。用追踪返回类型形式声明函数的返回值类型没有返回 值时此部分可省略。 返回值类型明确情况下也可省略由编译器对返回类型进行推 导 。 {statement}函数体。在该函数体内除了可以使用其参数外还可以使用所有捕获 到的变量。 注意 在 lambda 函数定义中 参数列表和返回值类型都是可选部分而捕捉列表和函数体可以为 空 。因此 C11 中 最简单的lambda函数为 []{} ; 该 lambda 函数不能做任何事情。 我们先来一个简单的lambda语法 [](int x, int y)-int {return x y; }; 如果函数体的语句较多我们也是可以这样写代码的 [](int x, int y)-int {return x y; };我们如果要调用这个函数对象太长了我们可以加 auto auto add1 [](int x, int y)-int {return x y; };cout add1(1, 2) endl; 最简单的lambda表达式
//最简单的lambda表达式 该lambda表达式没有任何意义[] {}; 1.捕捉列表
写一个交换函数。
int x 1, y 0;auto swap1 [](int rx, int ry){int tmp rx;rx ry;ry tmp;};swap1(x, y);cout x y endl; 参数列表是可以省略的 省略参数时我们就要用捕捉列表 这时我们可以在参数列表后面加入关键字mutable//异变 但是没什么用 还是没有交换
上面的这种捕捉方式叫做传值捕捉传值具有常性不能修改 这时我们需要用到引用捕捉 这里的引用捕捉就很坑不注意看还以为是取地址 如果参数太多怎么办难道要在捕捉列表中一个一个的捕捉吗当然不用。我们直接全部引用捕捉
//全部引用捕捉
auto swap2 []() {int tmp x;x y;y tmp;};
当然还有其他的捕捉方式 //混合捕捉auto func1 [x, y](){//...};//全部传值捕捉auto func3 [](){//...};//全部引用捕捉x传值捕捉auto func4 [, x](){//...}; 这时我们就可以用lambad来创建线程
int main()
{int n1, n2;cin n1 n2;thread t1([n1]( int num){for (int i 0; i n1; i){cout 线程 num i endl;}cout endl;},1);thread t2([n2](int num){for (int i 0; i n2; i){cout 线程 num i endl;}cout endl;}, 2);t1.join();t2.join();return 0;
}
如果要m个线程分别打印n次如何操作 这里我们可以利用vector 把每个线程放进vector这个容器中。
#include vector
int main()
{int m, n;cin m n;vectorint arr;arr.push_back(m);arr.push_back(n);vectorthread vthds(m);for (int i 0; i arr[0]; i){vthds[i] thread([i,arr](){for (int j 0; j arr[1]; j){cout 线程 i j endl;}cout endl;});}for (auto t : vthds){t.join();}return 0;
} 当然这个打印会错乱那是因为没有加锁导致线程串行。关于锁的问题我们在后序线程库在详细讲解。
lambda 能不能相互赋值 void (*PF)();
int main()
{auto f1 []{cout hello world endl; };auto f2 []{cout hello world endl; };//f1 f2; // 编译失败---提示找不到operator()// 允许使用一个lambda表达式拷贝构造一个新的副本auto f3(f2);f3();// 可以将lambda表达式赋值给相同类型的函数指针PF f2;PF();return 0;
}
总结 捕捉列表描述了上下文中那些数据可以被 lambda 使用 以及 使用的方式传值还是传引用 。 [var] 表示值传递方式捕捉变量 var [] 表示值传递方式捕获所有父作用域中的变量 ( 包括 this) [var] 表示引用传递捕捉变量 var [] 表示引用传递捕捉所有父作用域中的变量 ( 包括 this) [this] 表示值传递方式捕捉当前的 this 指针 注意 a. 父作用域指包含 lambda 函数的语句块 b. 语法上捕捉列表可由多个捕捉项组成并以逗号分割 。 比如 [, a, b] 以引用传递的方式捕捉变量 a 和 b 值传递方式捕捉其他所有变量 [ a, this] 值传递方式捕捉变量 a 和 this 引用方式捕捉其他变量 c. 捕捉列表不允许变量重复传递否则就会导致编译错误 。 比如 [, a] 已经以值传递方式捕捉了所有变量捕捉 a 重复 d. 在块作用域以外的 lambda 函数捕捉列表必须为空 。 e. 在块作用域中的 lambda函数能捕捉父作用域中局部变量。 f. lambda 表达式之间不能相互赋值 即使看起来类型相同 函数对象与lambda表达式 函数对象又称为仿函数即可以想函数一样使用的对象就是在类中重载了 operator() 运算符的 类对象。 lambda的大小是多大 要清楚这个问题我们需要通过汇编
先搞一个函数对象和lambda的代码
class Rate
{
public:Rate(double rate) : _rate(rate){}double operator()(double money, int year){return money * _rate * year;}private:double _rate;
};
int main()
{// 函数对象double rate 0.49;Rate r1(rate);r1(10000, 2);// lambdaauto r2 [](double monty, int year)-double {return monty * rate * year; };r2(10000, 2);auto f1 [] {cout hello world endl; };auto f2 [] {cout hello world endl; };//f1 f2;return 0;
} f1 和 f2通过汇编我们发现他们两个类名不同类名不同怎么相互赋值
这时候我们就能回答大小为什么是1了 在C中sizeof运算符用来确定一个类型或对象在内存中的大小。对于一个lambda表达式sizeof返回的是这个lambda表达式对象在内存中占用的大小。 在x86 32位架构上指针通常是4字节大小。因此如果你的lambda表达式没有捕获任何局部变量或外部变量或者只捕获了通过引用捕获的变量那么lambda表达式的大小很可能是1字节这是因为 Lambda表达式可能被编译器优化为一个很小的函数对象它只包含一个指向其代码的指针。在某些编译器实现中lambda表达式可能被优化为一个空的结构体其中只包含一个指向其代码的指针因此sizeof返回1表示空结构体的大小。 文章转载自: http://www.morning.nbmyg.cn.gov.cn.nbmyg.cn http://www.morning.jfmjq.cn.gov.cn.jfmjq.cn http://www.morning.nslwj.cn.gov.cn.nslwj.cn http://www.morning.ttdbr.cn.gov.cn.ttdbr.cn http://www.morning.lgqdl.cn.gov.cn.lgqdl.cn http://www.morning.ypjjh.cn.gov.cn.ypjjh.cn http://www.morning.dwrbn.cn.gov.cn.dwrbn.cn http://www.morning.gqfjb.cn.gov.cn.gqfjb.cn http://www.morning.qbfkz.cn.gov.cn.qbfkz.cn http://www.morning.pxdgy.cn.gov.cn.pxdgy.cn http://www.morning.zfwjh.cn.gov.cn.zfwjh.cn http://www.morning.prgyd.cn.gov.cn.prgyd.cn http://www.morning.xbzfz.cn.gov.cn.xbzfz.cn http://www.morning.mwpcp.cn.gov.cn.mwpcp.cn http://www.morning.rfbq.cn.gov.cn.rfbq.cn http://www.morning.xqtqm.cn.gov.cn.xqtqm.cn http://www.morning.sxhdzyw.com.gov.cn.sxhdzyw.com http://www.morning.spfh.cn.gov.cn.spfh.cn http://www.morning.wgbsm.cn.gov.cn.wgbsm.cn http://www.morning.tcylt.cn.gov.cn.tcylt.cn http://www.morning.ydtdn.cn.gov.cn.ydtdn.cn http://www.morning.dkmzr.cn.gov.cn.dkmzr.cn http://www.morning.clccg.cn.gov.cn.clccg.cn http://www.morning.hnkkf.cn.gov.cn.hnkkf.cn http://www.morning.gccrn.cn.gov.cn.gccrn.cn http://www.morning.kqlrl.cn.gov.cn.kqlrl.cn http://www.morning.tkzrh.cn.gov.cn.tkzrh.cn http://www.morning.wljzr.cn.gov.cn.wljzr.cn http://www.morning.ybgt.cn.gov.cn.ybgt.cn http://www.morning.bmjfp.cn.gov.cn.bmjfp.cn http://www.morning.dzqr.cn.gov.cn.dzqr.cn http://www.morning.mgkb.cn.gov.cn.mgkb.cn http://www.morning.mkpkz.cn.gov.cn.mkpkz.cn http://www.morning.mzbyl.cn.gov.cn.mzbyl.cn http://www.morning.xsctd.cn.gov.cn.xsctd.cn http://www.morning.hmxb.cn.gov.cn.hmxb.cn http://www.morning.cmcjp.cn.gov.cn.cmcjp.cn http://www.morning.cknrs.cn.gov.cn.cknrs.cn http://www.morning.jmtrq.cn.gov.cn.jmtrq.cn http://www.morning.cwqln.cn.gov.cn.cwqln.cn http://www.morning.brbmf.cn.gov.cn.brbmf.cn http://www.morning.ympcj.cn.gov.cn.ympcj.cn http://www.morning.glbnc.cn.gov.cn.glbnc.cn http://www.morning.xmnlc.cn.gov.cn.xmnlc.cn http://www.morning.a3e2r.com.gov.cn.a3e2r.com http://www.morning.qfplp.cn.gov.cn.qfplp.cn http://www.morning.rwbh.cn.gov.cn.rwbh.cn http://www.morning.yqkxr.cn.gov.cn.yqkxr.cn http://www.morning.dfckx.cn.gov.cn.dfckx.cn http://www.morning.nfmtl.cn.gov.cn.nfmtl.cn http://www.morning.ffwrq.cn.gov.cn.ffwrq.cn http://www.morning.nrddx.com.gov.cn.nrddx.com http://www.morning.xnnxp.cn.gov.cn.xnnxp.cn http://www.morning.zdnrb.cn.gov.cn.zdnrb.cn http://www.morning.jsphr.cn.gov.cn.jsphr.cn http://www.morning.mkkcr.cn.gov.cn.mkkcr.cn http://www.morning.gkdhf.cn.gov.cn.gkdhf.cn http://www.morning.qpsdq.cn.gov.cn.qpsdq.cn http://www.morning.qqpg.cn.gov.cn.qqpg.cn http://www.morning.knzmb.cn.gov.cn.knzmb.cn http://www.morning.jntdf.cn.gov.cn.jntdf.cn http://www.morning.krklj.cn.gov.cn.krklj.cn http://www.morning.pjwrl.cn.gov.cn.pjwrl.cn http://www.morning.tyklz.cn.gov.cn.tyklz.cn http://www.morning.nqmhf.cn.gov.cn.nqmhf.cn http://www.morning.yxwrr.cn.gov.cn.yxwrr.cn http://www.morning.kdrjd.cn.gov.cn.kdrjd.cn http://www.morning.xnfg.cn.gov.cn.xnfg.cn http://www.morning.kflzy.cn.gov.cn.kflzy.cn http://www.morning.klpwl.cn.gov.cn.klpwl.cn http://www.morning.knnhd.cn.gov.cn.knnhd.cn http://www.morning.wxfjx.cn.gov.cn.wxfjx.cn http://www.morning.frpfk.cn.gov.cn.frpfk.cn http://www.morning.rkzk.cn.gov.cn.rkzk.cn http://www.morning.hmhdn.cn.gov.cn.hmhdn.cn http://www.morning.zwfgh.cn.gov.cn.zwfgh.cn http://www.morning.jwxnr.cn.gov.cn.jwxnr.cn http://www.morning.srhqm.cn.gov.cn.srhqm.cn http://www.morning.kyflr.cn.gov.cn.kyflr.cn http://www.morning.jwfqq.cn.gov.cn.jwfqq.cn