怎么做直播视频教学视频网站,构建新引擎激发新动力,区域信息网站怎么做,网络游戏手游排行榜前十一.新的类功能 默认成员函数
原来 C 类中#xff0c;有 6 个默认成员函数#xff1a; 构造函数 析构函数拷贝构造函数拷贝赋值重载 取地址重载const 取地址重载 C11 新增了两个#xff1a;移动构造函数和移动赋值运算符重载。 针对移动构造函数和移动赋值运算符重载有一些需…一.新的类功能 默认成员函数
原来 C 类中有 6 个默认成员函数 构造函数 析构函数拷贝构造函数拷贝赋值重载 取地址重载const 取地址重载 C11 新增了两个移动构造函数和移动赋值运算符重载。 针对移动构造函数和移动赋值运算符重载有一些需要注意的点如下 如果你没有自己实现移动构造函数且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任 意一个 。那么编译器会自动生成一个默认移动构造。 默认生成的移动构造函数对于内置类 型成员会执行逐成员按字节拷贝自定义类型成员则需要看这个成员是否实现移动构造 如果实现了就调用移动构造没有实现就调用拷贝构造。 如果你没有自己实现移动赋值重载函数且没有实现析构函数 、拷贝构造、拷贝赋值重载中 的任意一个那么编译器会自动生成一个默认移动赋值。默认生成的移动构造函数对于内 置类型成员会执行逐成员按字节拷贝自定义类型成员则需要看这个成员是否实现移动赋 值如果实现了就调用移动赋值没有实现就调用拷贝赋值。 ( 默认移动赋值跟上面移动构造 完全类似 ) 如果 你提供了移动构造或者移动赋值编译器不会自动提供拷贝构造和拷贝赋值。
二.可变参数模板
2.1 可变参数模板的定义 C11的新特性可变参数模板能够让您创建可以接受可变参数的函数模板和类模板。
// Args是一个模板参数包args是一个函数形参参数包
// 声明一个参数包Args...args这个参数包中可以包含0到任意个模板参数。
template class ...Args
void ShowList(Args... args)
{} 上面的参数 args 前面有省略号所以它就是一个可变模版参数我们把带省略号的参数称为 “ 参数包 ” 它里面包含了 0 到 N N0 个模版参数。我们无法直接获取参数包 args 中的每个参数的 只能通过展开参数包的方式来获取参数包中的每个参数这是使用可变模版参数的一个主要特 点也是最大的难点即如何展开可变模版参数. 但这里可变参数有点抽象我们只讲用法不再深入了解。 先看一个简单的例子
#includeiostream
using namespace std;template class ...Args
void ShowList1(Args... args)
{// 参数个数cout sizeof...(args) endl;//计算的是参数包中形参的个数
}int main()
{ShowList1(1, 2, 3, x, 1.1);return 0;
}输出结果: 2.2 参数包的展开
2.2.1 递归方式展开
通过递归方式展开参数包需要提供一个参数包展开的函数和一个递归终止函数。
#includeiostream
using namespace std;// 递归终止函数
template class T
void ShowList(const T t)
{cout t endl;
}
// 展开函数
template class T, class ...Args
void ShowList(T value, Args... args)
{cout value ;ShowList(args...);
}
int main()
{ShowList(1);ShowList(1, A);ShowList(1, A, std::string(sort));return 0;
}
结果为
2.2.2 逗号表达式展开参数包 #includeiostreamtemplateclass T
void print(T tmp){std::couttmpstd::endl;
}
//可变参数函数模板
templateclass...T
void expand(T...args){//逗号运算符//初始化列表int a[]{(print(args),0)...};
}int main(){expand(1,2,3,4);return 0;
}expand 函数的逗号表达式 (print(args), 0) 也是按照这个执行顺序 先执行 print(args) 再得到逗号表达式的结果 0。 同时 通过初始化列表来初始化一个变长数组 { (print(args), 0)… }将会展开成( (print(args1),0), (print(args2), 0), (print(args3), 0), etc…), 最终会创建一个元素只都为 0 的数组 inta[sizeof…(args)] 。 三.lambda表达式 3.1 lambda的概念 函数指针是在C语言中不过因为过于麻烦因此C中新出了仿函数。sort这个函数就可以根据我们自定义的仿函数来进行对不同的元素排序不过如果我们需要对很多种自定义类型进行排序那么就会很麻烦因此C11中就又出现了lambda表达式使之进一步简化。而lambda的底层其实就是仿函数其出现主要是为了更近一步的简化用法方便程序员。 3.2 lambda的使用 假设我们要对一个自定义类型进行排序其中有三个元素分别是名字价格评价等 现在我们要对他进行排序但是我们不知道该如何排序显然在不同场景中我们有不同的排序需求一般而言我们可以定义三个仿函数。 但lambda表达式可以这样使用
#includeiostream
#includestring
#includevector
#includealgorithm
using namespace std;struct Goods
{string _name; // 名字double _price; // 价格int _evaluate; // 评价Goods(const char* str, double price, int evaluate):_name(str), _price(price), _evaluate(evaluate){}
};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;});return 0;
} 上述代码的排序问题就是使用C11中的lambda表达式来解决可以看出lambda表达式实际是一个匿名函数。 3.3 lambda表达式语法 lambda表达式书写格式
[capture-list] (parameters) mutable - return-type { statement} lambda表达式各部分说明 [ capture-list ] : 捕捉列表该列表总是出现在lambda函数的开始位置编译器根据[]来判断接下来的代码是否为lambda函数捕捉列表能够捕捉上下文中的变量供lambda函数使用。(parameters)参数列表。与普通函数的参数列表一致如果不需要参数传递则可以连同()一起省略mutable默认情况下lambda函数总是一个const函数mutable可以取消其常量性。使用该修饰符时参数列表不可省略(即使参数为空)。-returntype返回值类型。用追踪返回类型形式声明函数的返回值类型没有返回值时此部分可省略。返回值类型明确情况下也可省略由编译器对返回类型进行推导。{statement}函数体。在该函数体内除了可以使用其参数外还可以使用所有捕获到的变量。 注意 在lambda函数定义中参数列表和返回值类型都是可选部分而捕捉列表和函数体可以为 空。因此C11中最简单的lambda函数为[]{}; 该lambda函数不能做任何事情。 3.4 捕获列表说明 捕获列表决定了上下文中哪些数据可以被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表达式之间不能相互赋值即使看起来类型相同 例如
#includeiostream
#includestring
#includevector
#includealgorithm
using namespace std;int main()
{// 最简单的lambda表达式, 该lambda表达式没有任何意义[] {};// 省略参数列表和返回值类型返回值类型由编译器推导为intint a 3, b 4;[] {return a 3; };// 省略了返回值类型无返回值类型auto fun1 [](int c) {b a c; };fun1(10);cout a b endl;// 各部分都很完善的lambda函数 额外取地址使用bauto fun2 [, b](int c)-int {return b a c; };cout fun2(10) endl;// 复制捕捉xint x 10;auto add_x [x](int a) mutable { x * 2; return a x; };cout add_x(10) endl;return 0;
}
代码结果为
3.5 lambda的底层逻辑 在底层编译器对于lambda表达式的处理方式完全就是按照函数对象的方式处理如果定义了一个lambda表达式编译器会自动生成一个类在该类中重载了operator。
四.包装器
4.1.function包装器 function包装器也叫适配器。C中的function本质是一个类模板也是一个包装器。 在C中有很多类型既可以是函数也可以是成员函数、静态成员函数、仿函数又或者是lambda表达式等等如此多的类型使用模板就会导致效率低下因此有了function包装器。
4.1.1包装器语法 std::function在头文件functional // 类模板原型如下
template class T function; // undefinedtemplate class Ret, class... Args
class functionRet(Args...); 模板参数说明 Ret: 被调用函数的返回类型 Args…被调用函数的形参
4.1.2 包装器使用
int f(int a, int b)
{return a b;
}struct Functor
{
public:int operator() (int a, int b){return a b;}
};class Plus
{
public:static int plusi(int a, int b){return a b;}double plusd(double a, double b){return a b;}
};int main()
{std::functionint(int, int) func1 f;cout func1(1, 2) endl;std::functionint(int, int) func2 Functor();cout func2(10, 20) endl;std::functionint(int, int) func3 Plus::plusi;cout func3(100, 200) endl;// 非静态成员函数包装std::functiondouble(Plus, double, double) func4 Plus::plusd;cout func4(Plus(), 100.11, 200.11) endl;std::functionint(int, int) func5 [](int a, int b) {return a b; };cout func5(100, 200) endl;return 0;
} 代码结果为 上面的包装器就分别有函数、仿函数、静态成员函数、成员函数和lambda表达式。
4.2 实战价值
逆波兰表达式求值
class Solution {
public:int evalRPN(vectorstring tokens) {stacklong long st;mapstring, functionint(int, int) opFuncMap {{ , [](long long x, long long y){ return x y; }},{ -, [](long long x, long long y){ return x - y; }},{ *, [](long long x, long long y){ return x * y; }},{ /, [](long long x, long long y){ return x / y; }}};for(auto str : tokens){// 操作符if(opFuncMap.count(str)){long long right st.top();st.pop();long long left st.top();st.pop();st.push(opFuncMap[str](left, right));}// 操作数else{st.push(stoll(str));}}return st.top();}
};