当前位置: 首页 > wzjs >正文

定制系统软件开发seo技术教程

定制系统软件开发,seo技术教程,会计招聘,太原网站怎么做seo目录 7、新的类功能 8、可变参数模板 8.1、emplace系列的接口和普通的接口的差别 9、lambda表达式 9.1、lambda表达式语法 10、包装器 10.1、function包装器 10.2、bind(绑定) 7、新的类功能 C11的新东西:移动构造和移动赋值 注:如果你没有自己…

目录

7、新的类功能

8、可变参数模板

8.1、emplace系列的接口和普通的接口的差别

9、lambda表达式

9.1、lambda表达式语法

10、包装器

10.1、function包装器

10.2、bind(绑定)


7、新的类功能

C++11的新东西:移动构造和移动赋值

注:如果你没有自己去实现这两个函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载这三个函数(三个函数通常都是绑定在一起的,要写其中一个,其他两个也需要写),则编译器会自动生成这两个函数。如果你实现了上述的那三个函数中的任意一个,则编译器则不会自动生成移动构造和移动赋值。如果你提供了移动构造或者移动赋值,编译器就不会自动提供拷贝构造和拷贝赋值。

注:默认生成的移动构造/赋值函数,对于内置类型成员会执行逐成员按字节拷贝(值拷贝),对于自定义类型成员,则需要看这个成员是否实现移动构造/赋值,如果实现了就调用移动构造/赋值,没有实现就调用拷贝构造/赋值。

注:编译器是可以同时生成默认的拷贝构造函数和移动构造函数的。前提是你没有显示定义 析构、拷贝构造、拷贝赋值重载、移动构造、移动赋值函数 中的任意一个。

强制生成默认函数的关键字default。

ex:

Person(Person&& p) = default; // 强制生成一个Person类的移动构造

禁止生成默认函数的关键字delete。

ex:

Person(const Person& p) = delete; // 禁止编译器默认生成一个Person类的拷贝构造

注:如果能想要限制某些默认函数的生成,在C++98中,是该函数设置成private,并且只声明不定义,这样只要其他人想要调用就会报错。在C++11中更简单,只需在该函数声明加上 = delete即可,该语法指示编译器不生成对应函数的默认版本,称 = delete修饰的函数为删除函数。

注:delete适合于当要求一个类不希望被拷贝的场景。-- ex:库里的 istream 和 ostream,你用传值传参会报错,因为它内部把拷贝构造给 delete 掉了,不希望流对象被拷贝。

举个栗子:假设要求一个类只能在堆上生成对象。

class HeapOnly
{
public:static HeapOnly *CreateObj() // 专门写一个函数用于只在堆上生成该类的对象。// 不加static的话,需要通过创建这个类的对象才能去调用这个函数,但是构造函数是私有的,又没法直接创建对象,需要去调用这个函数,所以...{return new HeapOnly;}private:HeapOnly() // 方法一:把这个类的构造函数弄成私有的,这样就没法随便去生成这个类的对象了{}HeapOnly(const HeapOnly &);int _a = 1;
};void test8()
{HeapOnly *p1 = HeapOnly::CreateObj(); // 静态成员函数可以通过类域直接调用// 但是方法一有漏洞,如果没有把拷贝构造也私有化,那么下面这句代码就能成功运行,然后就成功创建出该类的对象了。// HeapOnly obj(*p1); // obj是在栈上创建的 // 此时,C++98的做法是把拷贝构造也私有化,即 私有 + 只声明不实现。//	     C++11的做法是加了个关键字delete。
}

注:C++11中还新添加了继承和多态中的final与override关键字。

8、可变参数模板

早在C语言中就有可变参数这个概念。

例如Printf函数,去官网上看该函数的文档时会发现它的参数中有一个参数是 ...

C语言中就是用 ... 来代表可变参数的,其底层大概就是用一个数组去存了各种各样的参数,当你传过来一个参数的时候,它内部会自动去这个数组中匹配看是哪种参数。

举个C++中的可变参数模板的栗子:

下面就是一个基本可变参数的函数模板:

Args是一个模板参数包,args是一个函数形参参数包

声明一个参数包Args...args,这个参数包中可以包含0到任意个模板参数(类型)。

template <class... Args>
void Example(Args... args) // args:模板参数包Args 定义的 形参对象包// 注意:在<>里 ... 是在Args前面的,但是在()里 ... 是在Args后面的,两个地方刚好相反,这里的格式要记一下。
{
}

上面的参数args前面有省略号,所以它就是一个可变模版参数,我们把带省略号的参数称为“参数包”,它里面包含了0到N(N >= 0)个模版参数。

我们无法直接获取参数包args中的每个参数的,只能通过展开参数包的方式来获取参数包中的每个参数。

这是使用可变模版参数的一个主要特点,也是最大的难点,即如何展开可变模版参数。

由于语法不支持使用args[i]这样方式获取可变参数,所以我们的用一些奇招来一一获取参数包的值。

奇招一:递归函数方式展开函数包(编译时,参数推导的递归)

举个栗子:

众所周知,C语言的printf有个致命的缺陷就是,只能打印内置类型,对于自定义类型的打印是没有办法。

那现在来实现一个C++版本的可打印任意类型对象的含可变参数的函数。

// 正确的递归出口
void _Cpp_Printf() // 写个空函数就好
{cout << endl;
}// 递归函数
template <class T, class... Args>
void _Cpp_Printf(const T &val, Args... args)
{// if (sizeof(...args) == 0) return; // 注意:这样写递归出口是不行的,因为这个递归函数走的是编译时的逻辑,但是这里的sizeof走的是运行时,不匹配。cout << val << endl;_Cpp_Printf(args...);
}// 展开函数
template <class... Args>
void Cpp_Printf(Args... args)
{// 补充:这个args是可以sizeof一下的,通过sizeof可以知道这个args的参数个数cout << sizeof...(args) << endl;// 但是:这个args的sizeof的语法设计的很奇怪// 补充:不支持这样获取参数包中的每个参数// error C3520: “args”: 必须在此上下文中扩展参数包/*for (size_t i = 0; i < sizeof...(args); ++i){cout << args[i] << endl;}cout << endl;*/// 开始递归_Cpp_Printf(args...);
}

奇招二:逗号表达式展开参数包 -- 有点抽象

这种展开参数包的方式,不需要通过递归终止函数,是直接在expand函数体中展开的。

printarg不是一个递归终止函数,只是一个处理参数包中每一个参数的函数。

也就是说在构造int数组的过程中就将参数包展开了,这个数组的目的纯粹是为了在数组构造的过程展开参数包。

template <class T>
int PrintArg(T t)
{cout << t << " ";return 0;
}// 展开函数
template <class... Args>
void Cpp_Printf2(Args... args)
{// 编译时推导,args...有几个参数,PrintArg就调用几次,就有几个返回值,arr就开多大int arr[] = {PrintArg(args)...};cout << endl;
}// 其底层也就是编译器实现的时候,大概是这样的
void Cpp_Printf2(int x, char y, std::string z)
{int arr[] = {PrintArg(x), PrintArg(y), PrintArg(z)};cout << endl;
}

注:这个可变参数可以加万能引用&&

例如:

template <class... Args>
void Func(Args &&...args); // 注意:万能引用加的位置

8.1、emplace系列的接口和普通的接口的差别

先说结论:推荐使用emplace系列的接口

void test9()
{Cpp_Printf(1);Cpp_Printf(1, 'A');Cpp_Printf(1, 'A', std::string("sort"));// 在只传一个参数的时候,emplace系列的接口和普通的接口没什么区别kk::string s1("kkkkkk");list<kk::string> lt;lt.push_back(s1);lt.push_back(move(s1));kk::string s2("kkkkkk");lt.emplace_back(s2);lt.emplace_back(move(s2));// 真正的区别在于传复合类型的参数时的场景list<pair<kk::string, int>> lt2;pair<kk::string, int> kv1("lll", 1);lt2.push_back(kv1);lt2.push_back(move(kv1));lt2.emplace_back(kv1);lt2.emplace_back(move(kv1));// 正常情况下也不会这样去用emplace系列的接口,不然就还是跟普通接口还是没什么区别// 而是这样写lt2.emplace_back("lll", 1); // 这才是emplace系列接口的真正用处// 对于普通的接口,在这种场景下,只能先去构造一个pair类型的对象,再进行传参// 但是emplace系列的接口支持可变参数,你可以直接给它传构建pair对象所需的参数,然后它自己去创建对象// 补充:// 上面的push_back(move(kv1))需要进行 一次构造 + 一次移动构造// 而这里的emplace_back("lll", 1)只进行了一次构造// 但是:移动构造的消耗并不大,所以要记住emplace系列的接口在效率上并不比普通接口高多少。// 网上有人说:emplace系列的接口的效率比普通接口更高,这种说法并不完全正确,//		 补充:直接 emplace 或者 push/insert (有移动构造的)右值对象 -> 构造 + 移动构造//			   直接 emplace 参数包 -> 构造//             在有移动构造的深拷贝的对象中,两种接口的效率差别不大。//			   因为移动构造的代价很小。//			   直接 emplace 或者 push/insert 左值对象/浅拷贝的右值对象 -> 构造 + 拷贝构造//			   直接 emplace 参数包 -> 构造//			   这种情况下用普通接口的代价就大了很多。//			   因为拷贝构造的代价很大。//		   注:在没有移动构造的浅拷贝的对象中(只有拷贝构造),两者的效率差别也很大,emplace系列的接口在这种情况下更优。// 注:可以去看list的模拟实现,在那里模拟实现了一个emplace的接口// 总结:emplace系列的接口的特点是:它们可以做到直接构造,而普通接口只能做到先构造再拷贝构造。// 所以:以后在使用容器插入接口(push/insert)的时候,推荐使用emplace系列的,其次emplace能用参数包就用参数包。// 补充:单参数也可以用参数包list<string> lt3;lt3.emplace_back("kkkk");		  // 这里是直接构造lt3.emplace_back(string("kkkk")); // 这里是 构造 + 移动构造
}

总结:可变参数模板是个伟大的设计,比如当你在定义一个函数接口时,如果想要这个函数可以接收任意多个参数,就可以选择把该接口写成一个可变参数的函数模板。

9、lambda表达式

在实际生活中,大部分情况下,只进行 int 和 int 之间的比较是没有什么意义的,一般都是进行 自定义类型 和 自定义类型 之间的比较。

但是一般实际中,两个 自定义类型 的比较可能需要考虑很多因素。

比如下面的自定义类型商品,可能需要你按价格比较,也可能需要你按评价去比较。

这时候,在C++11出来之前你只能去写很多个仿函数,给每个比较因素都专门写一个仿函数去实现比较逻辑。

后来觉得这样写太麻烦了,于是就有了lambda表达式。

补充:lambda表达式就像一个匿名函数对象(就是仿函数),本质是一个特殊的,匿名的类类型。它是一个带有operator()的类,即仿函数。

9.1、lambda表达式语法

lambda表达式书写格式:[capture - list](parameters) mutable -> return-type{ statement }

1. lambda表达式各部分说明

[capture - list]:捕捉列表,该列表总是出现在lambda函数的开始位置,编译器根据[]来判断接下来的代码是否为lambda函数,捕捉列表能够捕捉上下文中的变量供lambda函数使用。

注:捕捉列表不能被省略。

(parameters):参数列表。与普通函数的参数列表一致,如果不需要参数传递,则可以连同()一起省略。

mutable:默认情况下,lambda函数总是一个const函数,mutable可以取消其常量性。使用该修饰符时,参数列表不可省略(即使参数为空)。

注:默认情况下,mutable 可以省略

->returntype:返回值类型。用追踪返回类型形式声明函数的返回值类型,没有返回值时此部分可省略。返回值类型明确情况下,也可省略,由编译器对返回类型进行推导。

{ statement }:函数体。在该函数体内,除了可以使用其参数外,还可以使用所有捕获到的变量。

注意:

在lambda函数定义中,参数列表和返回值类型都是可选部分,而捕捉列表和函数体可以为空。

因此C++11中最简单的lambda函数为:[]{}; 该lambda函数不能做任何事情。

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;}
};void test10()
{vector<Goods> v = {{"苹果", 2.1, 5}, {"香蕉", 3, 4}, {"橙子", 2.2, 3}, {"菠萝", 1.5, 4}};// 没有 lambda 之前,对 v 的排序方法:// sort(v.begin(), v.end(), ComparePriceLess());// sort(v.begin(), v.end(), ComparePriceGreater());// lambda 使用举例// 一个 Add 的 lambda 函数[](int a, int b) -> int{ return a + b; };// 那么该如何使用这个函数呢?(这个函数是匿名的,没有名字)auto add1 = [](int a, int b) -> int{ return a + b; }; // 方法一:可以用auto//		  因为严格来说lanmda表达式的类型,你不知,我不知,只有编译器知道cout << add1(1, 2) << endl;cout << typeid(add1).name() << endl; // 想看lambda表达式到底是什么类型,也可以用typeid看,然后会发现看不懂auto add2 = [](int a, int b) -> int{ return a + b; };cout << typeid(add2).name() << endl;// 注:即使两个lambda表达式写的一模一样,它们两个的类型也不一样。// 原因:lambda表达式的原理类似于范围for,lambda编译时,编译器会生成对应的仿函数,上面的typeid的结果就是这个仿函数的名称,//       编译器为了避免生成重复的名称(uuid),所以才会导致,即使两个lambda表达式写的一模一样,你去typeid出来的结果也不一样。// 注:通过汇编层面可以发现使用lambda表达式的汇编指令,跟使用仿函数的汇编指令是一样的。// 返回值明确的情况下,返回值可以省略,编译器会自动推导auto add3 = [](int a, int b){ return a + b; };// 没有参数,参数列表也可以省略auto func1 = []{ cout << "hello world" << endl; };func1();// 有了lambda表达式之后,上面的商品的sort就可以这样写auto priceLess = [](const Goods &gl, const Goods &gr) -> bool{ return gl._price < gr._price; }; // 价格的升序sort(v.begin(), v.end(), priceLess);// 更简洁一点的写法sort(v.begin(), v.end(), [](const Goods &gl, const Goods &gr) -> bool // 价格的降序{ return gl._price > gr._price; });sort(v.begin(), v.end(), [](const Goods &gl, const Goods &gr) -> bool // 评价的升序{ return gl._evaluate < gr._evaluate; });sort(v.begin(), v.end(), [](const Goods &gl, const Goods &gr) -> bool // 评价的降序{ return gl._evaluate > gr._evaluate; });// 捕获列表说明 -- 注:被捕捉过来的对象就相当于被当成了一个类的成员变量使用int a = 1, b = 2;auto swap1 = [](int &x, int &y){int tmp = x;x = y;y = tmp;};swap1(a, b);// 捕捉列表可以捕捉当前局部域的对象auto swap2 = [a, b]() mutable // 可以不传参数,捕捉a,b对象给lambda用// 注意:这里是传值捕捉,所以这里的a、b的修改并不影响上面的a、b// 传值捕捉过来的对象具有const属性// 要想修改传值捕捉过来的对象,就需要加个mutable去除掉其const属性// 因为这里的a、b是拷贝过来的,即使修改也不改变外面的a、b,所以使用mutable的场景不多// 有没有传值捕捉,都可以加mutable,只是说在没有传值捕捉的时候,mutable就不起作用了。{int tmp = a;a = b;b = tmp;};swap2();// 引用方式捕捉(这里有点坑,让人不好区分这里的 & 到底是 取地址 还是 引用)auto swap3 = [&a, &b](){int tmp = a;a = b;b = tmp;};swap3();// 注意:不能直接捕捉a、b的地址,但是可以间接捕捉int *pa = &a, *pb = &b;auto swap4 = [pa, pb](){int tmp = *pa;*pa = *pb;*pb = tmp;};swap4();// 不过,这没有什么意义,有引用方式的捕捉何必多此一举,简直神经// 以值传递方式捕捉当前作用域中的所有变量(包括this)int q = 1, w = 2, e = 3, r = 4, t = 5;auto func2 = [=](){return q + w + e + r * t;};cout << func2() << endl;// 以引用传递捕捉当前作用域中的所有变量(包括this)auto func3 = [&](){++a;++b;++q;++w;};// 混合捕捉auto func4 = [&a, &b, q, w](){++a;++b;// ++q; // 这里 q 没法++,因为是传值捕捉的q,在不加mutable的情况下,传值捕捉过来的对象具有const属性};
}

捕捉列表总结:

捕捉列表描述了上下文中哪些数据可以被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表达式

  1. 从使用方式上来看,函数对象(仿函数)与lambda表达式完全一样。
  2. 实际在底层编译器对于lambda表达式的处理方式,完全就是按照函数对象(仿函数)的方式处理的,即:如果定义了一个lambda表达式,编译器会自动生成一个类,在该类中重载了operator()。

总结:lambda表达式适合用于当前需要一个小函数的场景,需要一个大函数的时候就不适用了。

10、包装器

10.1、function包装器

注:目的就是为了做到类型统一。

function包装器,也叫作适配器。C++中的function本质是一个类模板,也是一个包装器。

补充一个概念:可调用对象,包括:

  1. 函数指针(类型定义复杂)
  2. 仿函数对象(要定义一个类,用的时候有点麻烦,且不适合做类型统一)
  3. lambda(从用的角度上,没有类型概念)

那么我们来看看,我们为什么需要function呢?

ret = func(x);

上面func可能是什么呢?那么func可能是函数名?函数指针?函数对象(仿函数对象)?也有可能是lamber表达式对象?

所以这些都是可调用的类型!如此丰富的类型,可能会导致模板的效率低下!

为什么呢?继续往下看

template<class F, class T>
T useF(F f, T x)
{static int count = 0;cout << "count:" << ++count << endl;cout << "count:" << &count << endl;return f(x);
}
double f(double i)
{return i / 2;
}
struct Functor
{double operator()(double d){return d / 3;}
};void test11()
{// 函数名cout << useF(f, 11.11) << endl;// 函数对象cout << useF(Functor(), 11.11) << endl;// lamber表达式cout << useF([](double d)->double { return d / 4; }, 11.11) << endl;
}

通过上面的程序验证,我们会发现useF函数模板实例化了三份。(count的地址每次打印出来的都不一样)

包装器可以很好的解决上面的问题。

注:std::function在头文件<functional>

类模板原型如下:

template <class T> function; // undefined
template <class Ret, class... Args>
class function<Ret(Args...)>;

模板参数说明:

Ret : 被调用函数的返回类型

Args…:被调用函数的形参

注:通过查看function的文档可知,function的底层是一个仿函数,因为它是一个重载了operator()的类。

#include <functional>// 注:function不是用来定义可调用对象的,是用来包装可调用对象的。int f1(int a, int b)
{return a + b;
}struct Functor1
{
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;}
};void test12()
{// 先介绍一下function的用法function<int(int, int)> fcn; // 注意:这里的格式跟以前定义一个类模板对象的格式不太一样// 还可以这样写function<int(int, int)> fcn1(f1);function<int(int, int)> fcn2 = f1;function<int(int, int)> fcn3 = Functor1();function<int(int, int)> fcn4 = [](int x, int y){ return x + y; };cout << fcn1(1, 2) << endl;cout << fcn1.operator()(1, 2) << endl;cout << fcn2(1, 2) << endl;cout << fcn3(1, 2) << endl;// 包装器就能很好的解决上面的问题(test11)(包装器能够统一类型)// 函数名std::function<double(double)> func1 = f;cout << useF(func1, 11.11) << endl;// 函数对象std::function<double(double)> func2 = Functor();cout << useF(func2, 11.11) << endl;// lambda表达式std::function<double(double)> func3 = [](double d) -> double{ return d / 4; };cout << useF(func3, 11.11) << endl;// 此时,count的地址每次打印出来的都是一样的。// 包装类的成员函数时,格式:&类名::函数名function<int(int, int)> fc1 = &Plus::plusi; // 静态成员函数这样包装就行了(可以不加&,但建议加上,这样好记一点)// 静态成员函数是没有this指针的cout << fc1(1, 2) << endl;function<double(Plus *, double, double)> fc2 = &Plus::plusd; // 非静态成员函数要这样包装(规定必须要加一个&)// 注意:参数要匹配,普通的成员函数前面还有一个this指针// 注意:如果function(fc2)是这样写的,则这里不能用匿名对象,匿名对象是右值,没有办法取地址&Plus plus;cout << fc2(&plus, 1.1, 2.2) << endl; function<double(Plus, double, double)> fc3 = &Plus::plusd;// 如果function(fc3)这样写的,这里就可以用匿名对象。cout << fc3(Plus(), 1.1, 2.2) << endl; // 注:这里并不是直接把这几个参数传给对应的成员函数,首先,使用成员函数时就不能显示传递参数给 this 。// 原因:这里不论是传匿名对象,还是对象指针,其底层都并不是直接将其传给this指针的,而是用这个匿名对象或者函数指针去调用那个成员函数。
}

10.2、bind(绑定)

std::bind函数定义在头文件中,是一个函数模板。

它就像一个函数包装器(适配器),接受一个可调用对象(callable object),生成一个新的可调用对象来“适应”原对象的参数列表。-- 包装一个可调用对象和它的参数

一般而言,我们用它可以把一个原本接收N个参数的函数fn,通过绑定一些参数,返回一个接收M个(M可以大于N,但这么做没什么意义)参数的新函数。

同时,使用std::bind函数还可以实现参数顺序调整等操作。

原型如下:

template <class Fn, class... Args>

/* unspecified */ bind(Fn&& fn, Args&&... args);

// with return type (2)

template <class Ret, class Fn, class... Args>

/* unspecified */ bind(Fn&& fn, Args&&... args);

注:unspecified -- 不确定的,不明确的

功能:用于调整可调用对象的参数个数和参数顺序的。

注:调用bind的一般形式:auto newCallable = bind(callable,arg_list);

其中,newCallable本身是一个可调用对象,arg_list是一个逗号分隔的参数列表,对应给定的callable的参数。

注:arg_list中的参数可能包含形如_n的名字,其中n是一个整数,这些参数是“占位符”。

int sub(int a, int b)
{return a - b;
}void test_bind1()
{auto f1 = sub;// cout << typeid(f1).name() << endl; // f1的类型是int(*)(int,int)cout << f1(10, 5) << endl;// 功能一:调整参数顺序(没啥用,了解即可)auto f2 = bind(sub, placeholders::_2, placeholders::_1); // _1永远代表你传的第一个参数,_2永远代表你传的的第二个参数cout << f2(10, 5) << endl;								 // 调整顺序的原理:10传给了_1,5传给了_2,然后_1传给了b,_2传给了acout << typeid(f2).name() << endl;						 // 尝试查看一些bind的返回值是什么类型,发现看不懂,不过可以知道的是bind的本质也是一个仿函数,因为它也重载了 operator()
}class Sub
{
public:Sub(int x): _x(x){}int sub(int a, int b){return (a - b) * _x;}private:int _x;
};void test_bind2()
{// 功能二:调整参数个数(实际就是调整你需要传参的个数)auto f3 = bind(&Sub::sub, placeholders::_1, placeholders::_2, placeholders::_3); // 有个参数对应的是 this 指针cout << f3(Sub(1), 10, 5) << endl;Sub sub1(1);cout << f3(&sub1, 10, 5) << endl;auto f4 = bind(&Sub::sub, Sub(1), placeholders::_1, placeholders::_2);cout << f4(10, 5) << endl;auto f5 = bind(&Sub::sub, &sub1, placeholders::_1, placeholders::_2);cout << f5(10, 5) << endl;auto f6 = bind(&Sub::sub, &sub1, placeholders::_1, 5); // 这样写,就相当于把第三个参数写死了,不能再给第三个参数传东西了cout << f6(10) << endl;auto f7 = bind(&Sub::sub, &sub1, 10, 5); // 你甚至可以把所有的参数都用bind绑死,这样你传参的时候可以一个都不用传cout << f7() << endl;// 注:这里和 function 那里是一样的,不论是传匿名对象,还是对象指针,其底层都并不是直接将其传给this指针的,而是用这个匿名对象或者函数指针去调用那个成员函数。
}void fx(const string &name, int x, int y)
{cout << name << " " << "血量:" << x << " " << " 蓝量:" << y << endl;
}void test_bind3()
{// 这里来个贴近实际的栗子// 以前使用fx函数都是这样使用的fx("卤蛋", 3450, 666);fx("卤蛋", 3568, 456);fx("卤蛋", 3456, 334);fx("卤蛋", 3984, 865);// 会发现你一个参数实际上都是固定的,没有变// 所以,可以现在可以这样写auto f8 = bind(fx, "卤蛋", placeholders::_1, placeholders::_2); // 专门弄一个函数f8给"卤蛋"用f8(3450, 666);f8(3568, 456);f8(3456, 334);f8(3984, 865);	// 还可以只绑死中间的某一个参数auto f9 = bind(fx, placeholders::_1, 5000, placeholders::_2); // 开了锁血挂f9("monkey", 600);f9("monkey", 624);f9("monkey", 646);f9("monkey", 656);
}void test_bind4()
{// 补充:// 只要是可调用对象都可以用 bind 去绑定auto f10 = bind([](int a, int b){ return a - b; }, placeholders::_1, placeholders::_2);cout << f10(1, 2) << endl;// bind也可以传给functionfunction<void(string, int, int)> f11 = bind(fx, placeholders::_1, 5000, placeholders::_2);// 注意:传给了function之后,typeid出来的就是function类型了cout << typeid(f11).name() << endl;// 实际应用中bind还可以用来解决这种问题map<string, function<int(int, int)>> opFuncMap = {{"+", [](int a, int b){ return a + b; }},// {"-",&Sub::sub}, // 这里是编译不过去的,因为sub是普通的成员函数,有个this指针,你这样写会导致参数不匹配// 在这种情况中,你也不能去修改上面的fucntion的参数个数,你改了其他的就不匹配了,所以这时候bind就可以派上用场了{"-", bind(&Sub::sub, Sub(10), placeholders::_1, placeholders::_2)},{"*", [](int a, int b){ return a * b; }},{"/", [](int a, int b){ return a / b; }} };
}// 这里最后介绍一下 bind 原型中的 class Ret的作用(了解即可,很少用)
template <class T>
void fy(int n)
{T *p = new T[n];
}double my_divide(double a, double b)
{return a / b;
}// 请问:这里的fy函数要怎么调用
void test_fy()
{// fy(10); // 这样调肯定是调不动的fy<int>(10); // 只能显示实例化// bind 中的 class Ret 的作用就是让你可以去显示实例化指定 这个绑定的可调用对象的 返回值auto func = bind<int>(my_divide, placeholders::_1, placeholders::_2);// my_divide 默认的返回值的类型是 double,但经过现在这么一 bind ,类型就变成了 int 。cout << func(10, 3) << endl;
}
http://www.dtcms.com/wzjs/341272.html

相关文章:

  • 嘉定企业网站制作佛山网站建设技术托管
  • 邢台做网站哪家便宜优化深圳seo
  • wordpress怎么加关键词和描述南宁白帽seo技术
  • 网站上的定位怎么做网络推广营销公司
  • 手机网站带后台源代码搜索引擎免费登录入口
  • 连云港网站建设sitall厦门seo网络优化公司
  • 网站行程表怎么做免费推广软件哪个好
  • 做外贸的人如何上国外网站人民政府网站
  • 无锡网页建站公司百度竞价代理公司
  • 做网站的公司搞什么活动seo关键词排名优化工具
  • 成都网站建设网广告电话
  • 大同网站建设制作哪家好台州关键词优化平台
  • 怎么做下载网站吗百度云网盘资源
  • 产品设计作品网站手机建网站软件
  • 网站设计背景图片怎么做的西安做seo的公司
  • 网站设计论文介绍潍坊自动seo
  • 网站地图咋做中国新闻最新消息今天
  • 外贸日文网站今日国内新闻热点
  • 做文献ppt模板下载网站上海百度
  • 无锡专业制作网站品牌推广服务
  • 河南建一个网站大概要多少钱小程序设计
  • 衡水学校网站建设徐州关键词优化平台
  • 网站怎么做关键词流量网站关键字优化价格
  • 宁波网站建站山东关键词快速排名
  • 舟山建设银行网站潍坊网站开发公司
  • 请人帮忙做网站推广做个电商平台要多少钱
  • 做企业网站需要什么米拓建站
  • 国内好的网站建设关键词排名点击器
  • wordpress布局构建器廊坊百度推广seo
  • 免费简历模板制作网站家庭优化大师