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

山西省建设厅入晋备案网站seo培训一对一

山西省建设厅入晋备案网站,seo培训一对一,山西网站开发建设,如需郑州网站建设上篇: 【C】-- C11基础常用知识点(上)_川入的博客-CSDN博客 目录 新的类功能 默认成员函数 可变参数模板 可变参数 可变参数模板 empalce lambda表达式 C98中的一个例子 lambda表达式 lambda表达式语法 捕获列表 lambda表达底层 …

上篇: 【C++】-- C++11基础常用知识点(上)_川入的博客-CSDN博客


目录

新的类功能

默认成员函数

可变参数模板

可变参数

可变参数模板

empalce

lambda表达式

C++98中的一个例子

lambda表达式

lambda表达式语法

捕获列表

lambda表达底层

包装器

function包装器

bind绑定


新的类功能

默认成员函数

原来C++类中,有6个默认成员函数:
  1. 构造函数
  2. 析构函数
  3. 拷贝构造函数
  4. 拷贝赋值重载
  5. 取地址重载
  6. const 取地址重载

        最后重要的是前4个,后两个用处不大。默认成员函数就是我们不写编译器会生成一个默认的。 C++11 新增了两个:移动构造函数和移动赋值运算符重载

        所以到了C++11后有8个默认成员函数。

移动构造函数和移动赋值运算符重载的又来以及原理:
【C++】-- C++11 - 右值引用和移动语义(上万字详细配图配代码从执行一步步讲解)_川入的博客-CSDN博客

只有在深拷贝的情况下才会有移动构造函数移动赋值运算符重载。可以认为:

  • 拷贝构造函数与拷贝赋值重载:针对于左值的拷贝。
  • 移动构造函数和移动赋值重载:针对于右值的拷贝。

        移动构造函数移动赋值重载,编译器自行生成的默认成员函数,能用的条件的复杂度与苛刻程度远远大于:构造函数、析构函数 、拷贝构造函数 、拷贝赋值重载4个默认成员函数。(由于:取地址重载 、const 取地址重载几乎不用自己写,用编译器的即可,所以忽略)

针对移动构造函数和移动赋值运算符重载有一些需要注意的点如下:
  • 编译器生成默认移动构造函数条件

        没有自己实现移动构造函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个。那么编译器会自动生成一个默认移动构造。

  • 编译器生成默认移动构造函数实现

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

  • 编译器生成默认动赋值重载函数条件
        你没有自己实现移动赋值重载函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个,那么编译器会自动生成一个默认移动赋值。
  • 编译器生成默认动赋值重载函数实现
        默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动赋
值,如果实现了就调用移动赋值,没有实现就调用拷贝赋值。(默认移动赋值跟上面移动构造完全类似)。
  • 如果你提供了移动构造或者移动赋值,编译器不会自动提供拷贝构造和拷贝赋值。
强制生成默认函数的关键字default:
 
        C++11可以让我们更好的控制要使用的默认函数。假设你要使用某个默认的函数,但是因为一些原因这个函数没有默认生成。比如:我们提供了拷贝构造,就不会生成移动构造了,那么我们可以使用default关键字显示指定移动构造生成。
class Person
{
public:Person(const char *name = "", int age = 0): _name(name), _age(age){}Person(const Person &p): _name(p._name), _age(p._age){}Person(Person &&p) = default;private:bit::string _name;int _age;
};int main()
{Person s1;Person s2 = s1;Person s3 = std::move(s1);return 0;
}
禁止生成默认函数的关键字delete:
        如果能想要限制某些默认函数的生成,在C++98中,是该函数设置成private,并且只声明补丁已,这样只要其他人想要调用就会报错。在C++11中更简单,只需在该函数声明加上=delete即可,该语法指示编译器不生成对应函数的默认版本,称=delete修饰的函数为删除函数。
class Person
{
public:Person(const char *name = "", int age = 0): _name(name), _age(age){}Person(const Person &p) = delete;private:bit::string _name;int _age;
};
int main()
{Person s1;Person s2 = s1;Person s3 = std::move(s1);return 0;
}

        可以使用default关键字强行让编译器生成,但是需要注意析构函数 、拷贝构造、拷贝赋值重载也会收到影响,需要自己写或也强制生成。没有什么意义,所以一般default关键字是用于构造,因为拷贝构造也属于构造,如果写了拷贝构造就不会默认生成构造了。

#问:如何用delete关键字实现一个类,只能再堆上创建对象?

        平时我们创建的类,是可以在栈区、全局数据区上创建的。

class HeapOnly
{};int main()
{HeapOnly hp1;  // 栈区static HeapOnly h2; // 全局数据区return 0;
}

        我们可以通过delete析构函数,然后使用new开辟类。


class HeapOnly
{
public:// HeapOnly()// {//     str_ = new char[10];// }// void Destroy()// {//     delete[] str_;//     operator delete(this); // 内存管理之重载operator delete// }~HeapOnly() = delete;
private:char* str_;
};int main()
{// HeapOnly hp1;  // 栈区 -- 会调析构// static HeapOnly h2; // 全局数据区 -- 会调析构// new出来的对象会调用构造 -- 这个时候会导致资源泄漏HeapOnly *ptr = new HeapOnly;operator delete(ptr);return 0;
}
  • new是c++中的操作符,malloc是c中的一个函数。
  • new不止是分配内存,而且会调用类的构造函数,同理delete会调用类的析构函数
  • malloc只会单纯的分配内存,不会进行初始化类成员的工作,同样free也不会调用析构函数。

#问:

class HeapOnly
{
public:HeapOnly(){str_ = new char[10];}~HeapOnly() = delete;
private:char* str_;
};

        对于构造函数是new空间,因为不能调用析构而不能使用delete,导致值空间泄漏怎么办?

        我们可以搞一个函数,利用函数将其释放。

class HeapOnly
{
public:HeapOnly(){str_ = new char[10];}void Destroy(){delete[] str_;operator delete(this); // 内存管理之重载operator delete// 也可以使用free}~HeapOnly() = delete;
private:char* str_;
};int main()
{// HeapOnly hp1;  // 栈区 -- 会调析构// static HeapOnly h2; // 全局数据区 -- 会调析构// new出来的对象会调用构造 -- 这个时候会导致资源泄漏HeapOnly *ptr = new HeapOnly;ptr->Destroy();return 0;
}

        继承的时候要小心,因为指针是可能出现偏移的,继承之后,切片可能成员位置发生变化,operator delete(this);的释放位置就可能不对。

可变参数模板

可变参数

可变参数最早的出现是在C语言:

         以printf,不确定参数传多少个参数,后面可以传一串值,也就可变参数,可以有0 ~ n个参数。底层是用数组实现的。

可变参数模板

下面就是一个基本可变参数的函数模板:
// Args是一个模板参数包,args是一个函数形参参数包
// 声明一个参数包Args...args,这个参数包中可以包含0到任意个模板参数。
template <class ...Args>
void ShowList(Args... args)
{}// (不一定非要写作:Args、args,可以换一个名字,只是这两个常用)
#include <string>// 可变参数的函数模板
template <class ...Args>
void ShowList(Args... args)
{}int main()
{std::string str("hello");ShowList();ShowList(1);ShowList(1, 'A');ShowList(1, 'A', str);return 0;
}
        上面的参数args前面有省略号,所以它就是一个可变模版参数,我们把带省略号的参数称为“参数包”,它里面包含了0到N(N>=0)个模版参数。我们无法直接获取参数包args中的每个参数的,只能通过展开参数包的方式来获取参数包中的每个参数,这是使用可变模版参数的一个主要特点,也是最大的难点,即如何展开可变模版参数。
        如果,我们想拿到参数包里面的参数,是不好拿的,sizeof可以帮助我们算参数包里面有多少个参数:
#include <string>
#include <iostream>// 可变参数的函数模板
template <class ...Args>
void ShowList(Args... args)
{std::cout << sizeof...(args) << std::endl;
}int main()
{std::string str("hello");ShowList();ShowList(1);ShowList(1, 'A');ShowList(1, 'A', str);return 0;
}
Note:
for(int i = 0; i< sizeof...(args); i++)
{std::cout << args[i] << " "; // error:args[i]不支持
}

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

第一种:递归函数方式展开参数包

        将参数包改一改,增加一个参数。

#include <iostream>
#include <string>// 递归终止函数
template <class T>
void ShowList()
{std::cout << std::endl;
}// 展开函数
template <class T, class... Args>
void ShowList(const T& value, Args... args) // 第一个参数传给value,剩下的传给参数包args。
{cout << value << " ";ShowList(args...); // 参数超过0个递归调自己,参数0个调递归终止函数。
}int main()
{ShowList(1);ShowList(1, 'A');ShowList(1, 'A', std::string("sort"));return 0;
}

        利用递归不断地推出参数包中的内容。

第二种:逗号表达式展开参数包

        这种展开参数包的方式,不需要通过递归终止函数,是直接在ShowList函数体中展开的, PrintArg不是一个递归终止函数,只是一个处理参数包中每一个参数的函数。这种就地展开参数包的方式实现的关键是逗号表达式,因为逗号表达式会按顺序执行逗号前面的表达式。

#include <iostream>
#include <string> template <class T>
void PrintArg(cosnt T t)
{std::cout << t << " ";
}// 展开函数
template <class... Args>
void ShowList(Args... args)
{// 利用逗号表达式去初始化arr,arr编译的时候就会知道要开多大,这个时候就会依次展开args参数包。// 利用逗号表达式去取右边的值0。(逗号表达式会按顺序执行逗号前面的表达式)int arr[] = {(PrintArg(args), 0)...};std::cout << std::endl;
}int main()
{ShowList(1);ShowList(1, 'A');ShowList(1, 'A', std::string("sort"));return 0;
}

同理,也可以优化为不适用逗号表达式展开参数包:

#include <iostream>
#include <string> template <class T>
int PrintArg(cosnt T t)
{std::cout << t << " ";return 0; 
}// 展开函数
template <class... Args>
void ShowList(Args... args)
{// arr编译的时候就会知道要开多大,这个时候就会依次展开args参数包。int arr[] = { PrintArg(args)... };std::cout << std::endl;
}int main()
{ShowList(1);ShowList(1, 'A');ShowList(1, 'A', std::string("sort"));return 0;
}

empalce

        分析STL容器中的empalce相关接口函数:

https://cplusplus.com/reference/vector/vector/emplace/

https://cplusplus.com/reference/vector/vector/emplace_back/
https://cplusplus.com/reference/list/list/emplace_back/
以vector容器的emplace_back为例:

          emplace_back是在一个函数模板里面,把一个成员函数是实现成可变参数包。其就是通过将可变参数包不断不断的往下传,传到最下面去初始化对应数据,或者是链表的话就初始化节点里的数据。

template <class... Args>
void emplace_back (Args&&... args);
        首先我们看到的emplace系列的接口,支持模板的可变参数,并且万能引用/引用折叠(即可以引用左值,也可以引用右值)
#问:那么相对insert和emplace系列接口的优势到底在哪里呢?
// vector::emplace_back
#include <iostream>
#include <vector>int main ()
{std::vector<int> myvector;myvector.push_back(100);myvector.emplace_back(200);return 0;
}
如果只是简单的int的,其与push_back就没有什么区别。主要的区别在于:
// vector::emplace_back
#include <iostream>
#include <vector>
#include <string>
#include <utility>int main()
{std::vector<std::pair<std::string, int>> myvector;myvector.push_back(std::make_pair("sort", 1));myvector.emplace_back(std::make_pair("sort", 1));myvector.emplace_back("sort", 1);return 0;
}

       效率上就emplace_back更好,因为make_pair是先构造,构造了一个pair。如此push_back就传了一个pair对象。所以调push_back是:

  • 左值:构造 + 拷贝构造。
  • 右值:构造 + 移动构造。

        emplace_back是不用着急创建pair对象,我们可将这个参数包一直向下传递,直到最后需要插入数据的时候,直接用这个数据包创建pair对象。

  • 直接构造。

        所以emplace系列比insert系列接口不一定高效。

通过代码凸显区别:

        不一定所有容器都会出现,于源码的实现有关系,此处使用list容器,并在VS2019实现出来的:

#include <iostream>
#include <list>
#include <string>class Date
{
public:Date(int year = 1, int month = 1, int day = 1):_year(year), _month(month), _day(day){std::cout << "Date(int year = 1, int month = 1, int day = 1)" << std::endl;}Date(const Date& d):_year(d._year), _month(d._month), _day(d._day){std::cout << "Date(const Date& d)" << std::endl;}private:int _year;int _month;int _day;
};int main()
{std::list<Date> lt1;lt1.push_back(Date(2022, 11, 16));std::cout << "---------------------------------" << std::endl;lt1.emplace_back(2022, 11, 16);return 0;
}

        所以建议:这个这种场景下直接使用emplace系列接口。

lambda表达式

        lambda也叫做匿名函数。

像函数使用的对象 / 类型:

  1. 函数指针 -- C++不喜欢的操作,所以有了仿函数。(全局的函数)
  2. 仿函数 / 函数对象。(全局的类)
  3. lambda。(局部)

 C++98中的一个例子

        因为由于仿函数有诸多的不便。如果待排序元素为自定义类型,需要用户定义排序时的比较规则,对于以下的三个成员一个就要创建2个(less、greater),就是6个。

#include <string>struct Goods
{std::string _name;  // 名字double _price; // 价格int _evaluate; // 评价Goods(const char* str, double price, int evaluate):_name(str), _price(price), _evaluate(evaluate){}
};
        随着C++语法的发展,人们开始觉得上面的写法太复杂了,每次为了实现一个algorithm算法,都要重新去写一个类,如果每次比较的逻辑不一样,还要去实现多个类,特别是相同类的命名,这些都给编程者带来了极大的不便。因此,在C++11语法中出现了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函数定义中,参数列表和返回值类型都是可选部分,而捕捉列表和函数体可以为。因此C++11中最简单的lambda函数为:[]{}; 该lambda函数不能做任何事情。
#include <iostream>int main()
{// 两个数相加的lambda// 没有函数名,加一个捕捉列表[]而已。因为没有名字,所以调用不好调// 但是[](int a, int b) -> int{ return a + b; }整体是一个对象,所以就可以巧用auto。auto add1 = [](int a, int b) -> int{ return a + b; };std::cout << add1(1, 2) << std::endl;// 省略返回值auto add2 = [](int a, int b){ return a + b; };std::cout << add2(1, 2) << std::endl;
}

        于是对于前面的三个成员一个就要创建2个(less、greater),就是6个。解决:

#include <string>
#include <vector>
#include <algorithm>struct Goods
{std::string _name; // 名字double _price;     // 价格int _evaluate;     // 评价//...Goods(const char *str, double price, int evaluate): _name(str), _price(price), _evaluate(evaluate){}
};int main()
{std::vector<Goods> 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._name < g2._name; });sort(v.begin(), v.end(), [](const Goods &g1, const Goods &g2){ return g1._name > g2._name; });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; });
}

#问:如何写一个交换swap函数?

        可以像上面那样写,但是会非常的难看。

#include <iostream>int main()
{// 交换变量的lambda - 行数会多int x = 0, y = 1;auto swap1 = [](int &x1, int &x2) -> void{int tmp = x1; x1 = x2; x2 = tmp; };swap1(x, y);std::cout << x << ":" << y << std::endl;
}

        我们可以这样写:

#include <iostream>int main()
{// 交换变量的lambda - 行数会多int x = 0, y = 1;auto swap1 = [](int &x1, int &x2) -> void{int tmp = x1; x1 = x2; x2 = tmp; };swap1(x, y);std::cout << x << ":" << y << std::endl;
}

捕获列表

#问:假如我们想不传参数交换x,y呢?

利用捕捉列表实现,注意:

  • 想捕捉谁就写谁,只能捕捉跟lambda表达式同一个作用域的对象。
  • 默认捕捉过来的变量不能修改 —— 加mutable让捕捉过来的变量可以修改(使用mutable须加())。
  • 默认捕捉是拷贝的方式捕捉,严格意义上说是传值捕捉。(lambda还是一个函数调用,是有栈帧的 —— 可以理解为:改变形参,不会改变实参)
#include <iostream>int main()
{// 交换变量的lambda - 行数会多int x = 0, y = 1;// 可以理解为:改变形参,不会改变实参auto swap = [x, y]()mutable{int tmp = x; x = y; y = tmp; };swap();std::cout << x << ":" << y << std::endl;
}

        所以mutable在实际中不起价值作用。

捕获列表说明:

捕捉列表描述了上下文中那些数据可以被lambda使用,以及使用的方式传值还是传引用。

  • [var]:表示值传递方式捕捉变量var。
  • [=]:表示值传递方式捕获所有父作用域中的变量(包括this)。
  • [&var]:表示引用传递捕捉变量var。
  • [&]:表示引用传递捕捉所有父作用域中的变量(包括this)。
  • [this]:表示值传递方式捕捉当前的this指针。

注意:

  • 父作用域指包含lambda函数的语句块
  • 语法上捕捉列表可由多个捕捉项组成,并以逗号分割
    • 比如:
      • [=, &a, &b]:以引用传递的方式捕捉变量a和b,值传递方式捕捉其他所有变量。
      • [&, a, this]:值传递方式捕捉变量a和this,引用方式捕捉其他变量。
  • 捕捉列表不允许变量重复传递,否则就会导致编译错误
    • 比如:[=, a]:=已经以值传递方式捕捉了所有变量,捕捉a重复
  • 在块作用域以外的lambda函数捕捉列表必须为空
  • 在块作用域中的lambda函数仅能捕捉父作用域中局部变量,捕捉任何非此作用域或者非局部变量都会导致编译报错。
  • lambda表达式之间不能相互赋值,即使看起来类型相同
#include <iostream>void (*PF)();int main()
{auto f1 = []{ std::cout << "hello world" << std::endl; };auto f2 = []{ std::cout << "hello world" << std::endl; };// f1 = f2;   // 编译失败--->提示找不到operator=()// 允许使用一个lambda表达式拷贝构造一个新的副本auto f3(f2);f3();// 可以将lambda表达式赋值给相同类型的函数指针PF = f2;PF();return 0;
}

lambda表达底层

        函数对象,又称为仿函数,即可以想函数一样使用的对象,就是在类中重载了operator()运算符的类对象,与范围for很像。

范围for:

        并没有看起来这么的智能,实际上是底层运用迭代器实现的。

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);// lamberauto r2 = [=](double monty, int year) -> double{return monty * rate * year;};r2(10000, 2);return 0;
}

        仿函数的名称就是:lambda_uuid。所以lambda表达式对于我们是匿名的,对于编译器而言是有名的。实际在底层编译器对于lambda表达式的处理方式,完全就是按照函数对象的方式处理的,即:如果定义了一个lambda表达式,编译器会自动生成一个类,在该类中重载了operator()。

包装器

function包装器

        function包装器也叫作适配器,C++中的function本质是一个类模板,也是一个包装器。
#include <iostream>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;}
};
int main()
{// 函数名std::cout << useF(f, 11.11) << std::endl;// 仿函数对象std::cout << useF(Functor(), 11.11) << std::endl;// lamber表达式对象std::cout << useF([](double d)->double{ return d/4; }, 11.11) << std::endl;return 0;
}

        因为上述的 f 的类型不同,于是会被实例化成三个

        包装器可以很好的解决上面的问题,将其变为1份。

std::function在头文件<functional>
// 类模板原型如下
template <class T> function;     // undefined
template <class Ret, class... Args>
class function<Ret(Args...)>;
模板参数说明:
Ret: 被调用函数的返回类型
Args…:被调用函数的形参
使用方法:
// 使用方法如下:
#include <functional>
#include <iostream>int f(int a, int b)
{return a + b;
}struct Functor
{
public:int operator()(int a, int b){return a + b;}
};int main()
{// 函数名(函数指针)std::function<int(int, int)> func1 = f;std::cout << func1(1, 2) << std::endl;// 函数对象std::function<int(int, int)> func2 = Functor();std::cout << func2(1, 2) << std::endl;// lamber表达式std::function<int(int, int)> func3 = [](const int a, const int b){ return a + b; };std::cout << func3(1, 2) << std::endl;return 0;
}

        对于静态成员函数与非静态成员函数的不同:

//使用方法如下:
#include <functional>
#include <iostream>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::function<int(int, int)> func4 = Plus::plusi; std::cout << func4(1, 2) << std::endl;// 非静态成员函数需要加&,并且不能直接调用,需要传对象,此处为Plus。(成员函数多传一个)std::function<double(Plus, double, double)> func5 = &Plus::plusd; std::cout << func5(Plus(), 1.1, 2.2) << std::endl;return 0;
}

        如果对于非静态成员函数,不想多传一个类对象的参数,可以通过绑定的方式解决这个问题。

        所以对于上面的,因为上述的 f 的类型不同,于是会被实例化成三个,就可以解决了:

#include <iostream>
#include <functional>template <class F, class T>
T useF(F f, T x)
{static int count = 0;std::cout << "count:" << ++count << std::endl;std::cout << "count:" << &count << std::endl;return f(x);
}
double f(double i)
{return i / 2;
}
struct Functor
{double operator()(double d){return d / 3;}
};int main()
{// 函数指针std::function<double(double)> f1 = f;std::cout << useF(f1, 11.11) << std::endl;// 函数对象std::function<double(double)> f2 = Functor();std::cout << useF(f2, 11.11) << std::endl;// lamber表达式对象std::function<double(double)> f3 = [](double d)->double{ return d / 4; };std::cout << useF(f3, 11.11) << std::endl;return 0;
}

包装器的其他一些场景:
 

class Solution
{
public:int evalRPN(vector<string> &tokens){stack<long long> st;map<string, function<long long(long long, long long)>> opFuncMap ={{"+", [](long long i, long long j){ return i + j; }},{"-", [](long long i, long long j){ return i - j; }},{"*", [](long long i, long long j){ return i * j; }},{"/", [](long long i, long long j){ return i / j; }}};for (auto &str : tokens){if (opFuncMap.find(str) != opFuncMap.end()){long long right = st.top();st.pop();long long left = st.top();st.pop();st.push(opFuncMap[str](left, right));}else{// 1、atoi itoa// 2、sprintf scanf// 3、stoi to_string C++11st.push(stoll(str));}}return st.top();}
};

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);

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

库中就是使用了placeholders来占位:

https://legacy.cplusplus.com/reference/functional/placeholders/

        其中的_1、_2、_3等,就是用来占位的。_1代表第1个参数,_2代表第2个参数……。调整的是形参的顺序。

#include <functional>
#include <iostream>int Div(int a, int b)
{return a / b;
}int main()
{int x = 10, y = 2;std::cout << Div(x, y) << std::endl;// 调整顺序 -- 鸡肋,一般用不上// _1, _2.... 定义在placeholders命名空间中,代表绑定函数对象的形参,// _1,_2... 分别代表第一个形参、第二个形参...//std::function<int(int, int)> bindFunc = bind(Div, std::placeholders::_2, std::placeholders::_1);auto bindFunc = bind(Div, std::placeholders::_2, std::placeholders::_1);// 传时候不会变std::cout << bindFunc(x, y) << std::endl;return 0;
}

可以理解为:

// x -> _1 ->a
// y -> _2 ->b。
auto bindFunc = bind(Div, _1, _2);
bindFunc(x, y);// x -> _2 ->b
// y -> _1 ->a。
auto bindFunc = bind(Div, _2, _1);
bindFunc(x, y);

        可以用绑定解决前面的非静态成员函数,需要传类对象(成员函数多传一个),以绑定参数解决 -> 调整个数。

#include <functional>
#include <iostream>
#include <map>int Plus(int a, int b)
{return a + b;
}int Mul(int a, int b, double rate)
{return a * b * rate;
}class Sub
{
public:int sub(int a, int b){return a - b;}
};// 11:50继续
int main()
{// 调整个数, 绑定死固定参数std::function<int(int, int)> funcPlus = Plus;// 本来要传3个.// function<int(Sub, int, int)> funcSub = &Sub::sub;// 将其变为只传2个,将1个(此处Sub())固定在这个地方绑死 — 不能变。std::function<int(int, int)> funcSub = std::bind(&Sub::sub, Sub(), std::placeholders::_1, std::placeholders::_2);// 1.5就固定死了std::function<int(int, int)> funcMul = std::bind(Mul, std::placeholders::_1, std::placeholders::_2, 1.5);std::map<std::string, std::function<int(int, int)>> opFuncMap = {{ "+", Plus},{ "-", std::bind(&Sub::sub, Sub(), std::placeholders::_1, std::placeholders::_2)}};std::cout << funcPlus(1, 2) << std::endl;std::cout << funcMul(2, 2) << std::endl;std::cout << funcSub(1, 2) << std::endl;std::cout << opFuncMap["+"](1, 2) << std::endl;std::cout << opFuncMap["-"](1, 2) << std::endl;return 0;
}

http://www.dinnco.com/news/50343.html

相关文章:

  • 武汉十大跨境电商公司深圳网站建设推广优化公司
  • 哪些是企业网站优秀的网页设计网站
  • 成都手机网站建设哪家公司好网络推销
  • b2b2c o2o是什么意思seo优化知识
  • 网上做兼职真实大网站常德seo公司
  • 惠阳区城市建设规划局网站排名优化公司
  • 有什么可以做cad赚钱的网站百度推广托管公司
  • wordpress几种系统免费检测网站seo
  • 教育局门户网站建设目的今天国际新闻最新消息10条
  • 学网站建设维护国外网站谷歌seo推广
  • 网站开发是先做前段还是后台网络营销与直播电商
  • 阳江招聘网娱乐业武汉seo排名公司
  • 免费建站网站 百度一下灰色词优化培训
  • 交互型网站难做吗seo外链技巧
  • 郑州网站建设专注乐云seo宁波seo营销平台
  • 海参企业网站怎么做游戏代理加盟平台
  • 网站设计动图怎么建设怎么注册自己的网址
  • 怎样做天猫网站视频最新的国际新闻
  • 山东网站建设设计公司做推广的公司一般都叫什么
  • 浙江网站建设报价长沙优化网站厂家
  • 高校建设主流的校园网站360手机优化大师下载
  • 做网站推广公司专业搜索引擎seo技术公司
  • 电子商务网站建设课后作业域名注册哪个平台比较好
  • 赣州爆炸事故东莞网络优化公司
  • 电子产品商务网站模板湖南seo博客seo交流
  • 哪个网站的课件做的好处公司营销网站建设
  • 创新的专业网站建设企业网站的基本功能
  • dede 网站内页标题修改阿里巴巴官网
  • 给人做传销网站新seo排名点击软件
  • 电商资讯网站有哪些seo优化网络