[C++]22:C++11_part_one

C++11

  • 一.列表初始化:
    • 1.{}初始化:
    • 2.C++11扩大了列表初始化的范围:
    • 3.std::initializer_list
      • 1.简单类型的列表初始化:
      • 2.复杂类型的列表初始化
      • 3.实现vector的列表初始化
      • 4.实现list的列表初始化:
      • 5.不支持列表初始化:
  • 二.变量类型推导:
    • 1.auto好处:
    • 2.auto作返回值:
    • 3.decltype推导表达式类型:
    • 4.nullptr
  • 三.范围for循环:
  • 四.final与override:
  • 五.新容器:
    • 1.array
    • 2.forward_list
    • 3.unordered系列
  • 六.右值引用与左值引用:
    • 1.左值引用:
    • 2.左值引用和拷贝构造:C++98中的优化和不优化
    • 3.右值引用:
    • 4.右值引用和移动构造和移动赋值:C++11中的优化
      • 1.概念加强:
      • 1.string实现移动构造和移动赋值
      • 2.优化过程?
      • 3.list实现移动构造和移动赋值
      • 4.list提供了右值版本的push_back:
        • 1.产生的问题:
        • 2.解决方法一:进行多次的move操作!
        • 3.使用完美转发:
      • 5.左值引用和右值引用:
      • 6.万能引用or引用折叠
    • 总结:新的类功能
      • 默认成员函数:
      • 强制生成默认函数的关键字default:
      • 禁止生成默认函数的关键字delete
  • 七.可变参数模板:
    • 1.概念引入:
    • 2.可变模板参数包
      • 1.简单验证:
      • 2.如何获取每一个参数?
    • 3.emplace接口+函数模板参数包:
  • 八.lambda表达式:
    • 1.函数指针:
    • 2.仿函数:
    • 3.lambda表达式:
      • 1.基本使用:
      • 2.捕捉列表的说明:
        • 1.lambda表达式的常量性:
        • 2.各种情况的捕捉列表使用:
      • 4.priority_queue使用包装器的比较函数?
      • 5.lambda表达式的本质?
  • 九.包装器:
    • 1.简单使用:
    • 2.一个题目:
    • 4.bind:
      • 1.一般概念:
      • 1.控制参数的顺序:
      • 2.控制参数的个数:

一.列表初始化:

1.{}初始化:

C++98中允许花括号对数组和结构体进行初始化:

struct A {
	int _a;
	int _b;
};

int main()
{
	int arr[] = { 1,2,3,4 };
	A add = { 1,2 };
	return 0;
}

2.C++11扩大了列表初始化的范围:

1.支持内置类型和用户自定义类型使用初始化列表。
2.自定义类型的列表初始化是依赖构造函数初始化。
3.总结:C++11 一切皆可列表初始化!

class Date {
public:
	Date(int year, int month, int day)
		:_year(year)
		,_month(month)
		,_day(day)
	{}
	Date(Date& t)
	{
		_year = t._year;
		_month = t._month;
		_day = t._day;
	}
private:
	int _year;
	int _month;
	int _day;
};


int main()
{
	//1.内置类型的列表初始化:
	int a = { 1 };
	int b{1};

	double c = { 1.1 };
	double d{1.1};
	//2.自定义类型的页表初始化:
	vector<int> arr_1 = { 1,2,3,4,5,6 };
	vector<int> arr_2{ 1,2,3,4,5,6 };

	//C++11的列表初始化适合于new表达式!
	int* n = new int[5] {1, 2, 3, 4, 5};

	//自定义类型的{}初始化
	Date d1 = { 2023,10,21 };
	Date* d2 = new Date(2023, 10, 21);
	return 0;
}

3.std::initializer_list

1.简单类型的列表初始化:

在这里插入图片描述

在这里插入图片描述

1.initializer_list类型在C++11中通过列表初始化!
2.列表中的数据类型确定initializer_list的具体存贮数据类型。

2.复杂类型的列表初始化

int main()
{
	//1.创造对象+map插入数据
	pair<int, string> s_1(1, "one");
	pair<int, string> s_2(2, "two");
	pair<int, string> s_3(3, "three");

	map<int, string> s1 = { s_1,s_2,s_3 };

	//2.列表初始化:
	map<int, string> s2 = { {1,"one"},{2, "two"},{3, "three"}};

	return 0;
}

1.首先map中是一个pair类型的数据
2.给map进行列表初始化,通过pair类型的数据进行列表初始化。
3.pair支持列表初始化,给pair进行列表初始化,给map进行列表初始化。

3.实现vector的列表初始化

在这里插入图片描述

1.vector的构造函数C++11支持列表初始化的原理?
2.initializer_list支持模板的一个类,根据列表中的的内容确定这个对象的具体的类型。
3.用initializer_list对象去重载构造函数。
4.本质用initializer_list对象作为构造函数参数传递数据的一个过程。

在这里插入图片描述

请添加图片描述

4.实现list的列表初始化:

在这里插入图片描述

在这里插入图片描述

5.不支持列表初始化:

在这里插入图片描述

在这里插入图片描述

想要去支持列表初始化?
1.实现列表初始化的构造函数。
2.可以实现一个mystack/myqueue继承stack/queue
3.给自己的类新增列表初始化的构造函数。

#include <iostream>  
#include <stack>  
#include <vector>  
#include <initializer_list>  
  
template <typename T>  
class MyStack : public std::stack<T, std::vector<T>> {  
public:  
    MyStack(std::initializer_list<T> initList) {  
        for (const auto& elem : initList) {  
            this->push(elem);  
        }  
    }  
};  
  
int main() {  
    // 使用自定义构造函数和初始化器列表初始化MyStack  
    MyStack<int> myStack = {1, 2, 3, 4, 5};  
    // 输出stack中的元素,应该是5 4 3 2 1(因为stack是LIFO)  
    while (!myStack.empty()) {  
        std::cout << myStack.top() << ' ';  
        myStack.pop();  
    }  
    std::cout << std::endl;  
    return 0;  
}

二.变量类型推导:

1.auto好处:

1.变量类型的自动推导,解决类型不好写的一个情况。
2.迭代器,lambda表达式,map<> , set<>类型可以auto自动推导。

int main()
{
	map<int, string> s1 = { {1,"one"},{2,"two"},{3,"three"}};

	//1.自己去写类型:
	map<int, string>::iterator it_1 = s1.begin();
	//2.自动类型推导:
	auto it_2 = s1.begin();
	return 0;
}

2.auto作返回值:

1.auto组为返回值类型是一个非常不好的语法。
2.函数只有一个还可以去找函数看类型。
3.如果使用多个函数并且使用auto返回那么我们可能找不到具体的类型。
4.可以使用typeid(变量名).name()打印变量类型吗?
5.自定义类型的打印返回是非常复杂的。

在这里插入图片描述

3.decltype推导表达式类型:

1.declitype(表达式) 将变量的类型声明为表达式类型。
2.auto返回的一个函数获取类型auto识别。
3.声明一个对应类型的对象就可以使用decltype

auto add(int a, int b)
{
	int sum = a+b;
	return sum;
}

int main()
{
	auto n_1 = add(10,20);
	//decltype(变量名称/表达式)作为类型去使用!
	decltype(n_1) n_2;
	n_2 = n_1;
	return 0;
}

在这里插入图片描述

int main()
{
	//1.底层const:修饰对象本身
	int n = 10;
	const int* p = &n;

	decltype(p) p2 = p;
	*p2 = 10;

	//2.顶层const:修饰指向内容
	int m = 10;
	int* const p1 = &m;

	decltype(p1) p3 = p1;
	*p3 = 10;
	return 0;
}

4.nullptr

1.在C++中NULL被定义为字面量0,这样就可能会带来一些问题,因为0既能表示指针常量,又能表示整形常量。所以考虑到安全性,C++11中新增加了nullptr,用来表示空指针。
2.使用了条件编译,既可以表示整形,又可以用来表示指针类型。

在这里插入图片描述

三.范围for循环:

1.支持迭代器就支持范围for
2.范围for依赖了auto自动类型识别。
3.引用可以修改容器中的内容。

在这里插入图片描述

在这里插入图片描述

四.final与override:

多态中有详细说过这两个关键字

五.新容器:

1.array

1.C语言中使用数组如果不在外面计算出数组的长度那么让数组传参数。
2.在函数中就等于丢失了数组的长度。
3.C++11的array类似于数组但是没有这样的问题。

在这里插入图片描述

1.array的出现主要解决了函数传参过程中类型的降低。
2.sizeof(数组名)代表这个数组的大小,一传参数就退化为指针了。
3.array解决了这个问题!

在这里插入图片描述

2.forward_list

在这里插入图片描述

1.forward_list支持头插可以支持尾插但是需要去找尾。
2.stl中就没有去实现单链表的尾插。
3.forwardlist有一些自己的使用场景。

在这里插入图片描述

3.unordered系列

unordered_set和unordered_map底层实现

六.右值引用与左值引用:

1.引用就是取别名!
2.语法上左值引用和右值引用都是去取别名。
3.本质都是指针:引用是通过指针实现的,左值引用的是当前左值的地址,右值引用是存贮右值拷贝到栈上的一个临时空间的地址。

1.左值引用:

1.左值是一个表示数据的表达式:变量名 + 解引用指针 + 函数引用返回值。
2.左值可以出现在赋值符号的左边,不可以出现在赋值符号的右边。
3.左值的地址是可以被取地址的,右值是不可以被取地址的。
4.左值引用就是对左值的引用,给左值取取别名。
5.左值在一般情况下可以对它去赋值。

在这里插入图片描述

1.左值引用解决了那些问题呢?
2.解决了传参拷贝的问题不需要多去拷贝数据。
3.解决部分返回对象的拷贝问题,(出函数作用域,返回对象还在的,可以使用左值引用返回)。

2.左值引用和拷贝构造:C++98中的优化和不优化

在这里插入图片描述

C++98中的左值引用没有解决什么问题呢?
1.如果返回的对象是一个局部变量,出了函数的作用域生命周期就到了,只能进行传值返回,就存在拷贝,一些对象拷贝的消耗非常大怎么办?
2.比如返回一个二维数组,存在拷贝问题怎么办?

3.右值引用:

1.C++11中新增了右值引用,解决上面产生的问题。
2.右值和左值一样也是一个表示数据的表达式,(字面量,表达式,函数返回值)。
3.右值可以出现在赋值符号的右边,不可以出现在赋值符号的左边。
4.右值是不可以被取地址的。
5.右值引用就是对右值的引用对右值取别名。

在这里插入图片描述

4.右值引用和移动构造和移动赋值:C++11中的优化

1.概念加强:

C++11对右值的解释:
1.纯右值(内置类型的右值)比如:20 30
2.将亡值(自定义类型的右值)比如:匿名对象,传值返回的函数。

1.string实现移动构造和移动赋值

在这里插入图片描述

1.C++98提供的优化,直接使用函数中的s1去拷贝构造main中的s1.
2.C++11中新增了移动构造和移动赋值!

void swap(string& s)
	{
		std::swap(_str, s._str);
		std::swap(_size, s._size);
		std::swap(_capacity, s._capacity);
	}

	//1-2:移动构造
	string(string&& str)
	{
		cout << "string(string&& str) 移动构造" << endl;
		swap(str);
	}

	//1-3:移动赋值:
	string& operator=(string&& str)
	{
		cout << "string& operator=(string&& str) 移动赋值" << endl;
		swap(str);
		return *this;
	}
sfpy::string fun(const char* str)
{
	sfpy::string s1(str);
	cout << "******************" << endl;
	return s1;
}

int main()
{
	sfpy::string s1 = fun("12345");
	cout << "1234" << endl;
	return 0;
}

在这里插入图片描述

2.优化过程?

在这里插入图片描述

3.list实现移动构造和移动赋值

//移动构造+移动赋值:

		list(list<T>&& tmp)
		{
			swap(tmp);
		}

		list& operator=(list<T>&& tmp)
		{
			swap(tmp);
			return *this;
		}


sfpy::list<int> fun()
{
	sfpy::list<int> l1 = { 1,2,3,4,5 };
	cout << &l1 << endl;
	return l1;
}

int main()
{
	sfpy::list<int> L1 = fun();
	cout << &L1 << endl;
	return 0;
}



移动构造+移动赋值:
在这里插入图片描述

拷贝构造+拷贝赋值
在这里插入图片描述

在这里插入图片描述

4.list提供了右值版本的push_back:

在这里插入图片描述

1.我们在对右值进行右值引用然后为了右值引用后的对象可以被处理–>右值被右值引用后属性是一个左值!
2.产生了一个问题:连续使用右值引用会导致属性发生变化,导致右指属性丢失?

1.产生的问题:

在这里插入图片描述

2.解决方法一:进行多次的move操作!

在这里插入图片描述

3.使用完美转发:

在这里插入图片描述

std::forward完美转发在传参的过程中保留对象原有的属性!

在这里插入图片描述

5.左值引用和右值引用:

1.左值引用不可以给右值取别名,但是const左值引用可以。
2.右值引用不可以给左值取别名,但是可以给move(左值)可以。

6.万能引用or引用折叠

// 模板中的&&不代表右值引用,而是万能引用,其既能接收左值又能接收右值。
// 模板的万能引用只是提供了能够接收同时接收左值引用和右值引用的能力,
// 但是引用类型的唯一作用就是限制了接收的类型,后续使用中都退化成了左值,
// 我们希望能够在传递过程中保持它的左值或者右值的属性, 就需要用完美转发


template<typename T>
void PerfectForward(T&& t)
{
	//使用完美转发保持原有的类型!
	Fun(std::forward<T>(t));
}

//& -->&& --> &
//&& -->&& -->&&


int main()
{
	PerfectForward(10);//右值
	int a;
	PerfectForward(a);// 左值
	PerfectForward(std::move(a)); // 右值

	//const int b = 8;
	//PerfectForward(b);//const 左值
	//PerfectForward(std::move(b)); // const 右值
	return 0;
}

总结:新的类功能

默认成员函数:

默认成员函数:构造+析构 拷贝构造+拷贝赋值构造 取地址重载+const取地址重载 -------> 移动构造+移动赋值 , 一共有8个默认成员函数!

1.如果你没有自己实现移动构造函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个。那么编译器会自动生成一个默认移动构造。默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动构造,如果实现了就调用移动构造,没有实现就调用拷贝构造。

强制生成默认函数的关键字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:
	sfpy::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:
	sfpy::string _name;
	int _age;
};
int main()
{
	Person s1;
	Person s2 = s1;
	Person s3 = std::move(s1);
	return 0;
}

一个类我不想让他去拷贝,C++98可以private声明。
C++11中可以提供delete关键字阻止默认函数的生成。

七.可变参数模板:

1.概念引入:

1.我们最早接触到的使用参数链表的形式是printf的格式化打印。
2.本质上是使用数组去保存参数列表中的数据。
3.调用数组中的数据进行格式化的打印。

在这里插入图片描述

2.可变模板参数包

1.简单验证:

在这里插入图片描述

验证是否可以使用像printf数组的方式去获取数据!

请添加图片描述

在这里插入图片描述

2.如何获取每一个参数?

1.使用递归的方式去展开参数包。
2.函数模板自动对类型进行匹配。
3.加一个模板参数T的方式可以提取参数包中的每一个数据。
4.重载一个递归结束的版本,方便递归结束!

template<class T>
void showlist(const T& value)
{
	cout << value << ' ';
}

template<class T, class... Args>
void showlist(const T& value , Args... args)
{
	cout << value << ' ';
	showlist(args...);
}

int main()
{
	showlist(1,2,3);
	return 0;
}

3.emplace接口+函数模板参数包:

在这里插入图片描述

在这里插入图片描述

1.观察emplace系列有两个地方值到注意。
2.模板的可变参数包。
3.支持万能引用。

//模拟实现list的emplace_back

template<class ...Args>
void emplace_back(Args&&... arg)
{
	emplace(end(), std::forward<Args>(arg)...);
}

template<class ...Args>
iterator emplace(iterator pos,Args&&... args)
{
	Node* cur = pos._node;
	Node* prev = cur->_prev;
	Node* newnode = new Node(std::forward<Args>(args)...);

	// prev newnode cur
	prev->_next = newnode;
	newnode->_prev = prev;
	newnode->_next = cur;
	cur->_prev = newnode;

	//return iterator(newnode);
	return newnode;
}

template<class... Args>
	ListNode(Args&&... args)
		:_next(nullptr)
		, _prev(nullptr)
		, _data(std::forward<Args>(args)...)
	{}


class Date {
public:
	Date(int year, int month, int day)
		:_year(year)
		,_month(month)
		,_day(day)
	{}
	Date(Date& t)
	{
		_year = t._year;
		_month = t._month;
		_day = t._day;
	}

	template<class... Args>
	Date(Args&&... args)
	{}
	
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	sfpy::list<Date> l1;
	l1.emplace_back(2023, 10, 21);
	return 0;
}

1.emplace系列:
2深拷贝的类对象,减少一次移动构造。
3.浅拷贝的类对象,减少一次拷贝构造。
3.对于深拷贝的类对象其实减少一次移动构造优化不是特别明显!

过程总结:
1.emplace_back去传参数包–>参数是万能引用的模板参数包。
2.emplace_back去调用emplace()使用完美转发这个模板参数包。
3.emplace进行节点的创建去调用节点的构造函数,使用完美转发这个模板参数包。
4.内置类型直接构造,自定义类型调用它的构造。
5.注意:需要声明一个模板参数包的一个构造要不然会导致类型不匹配的问题!
在这里插入图片描述

八.lambda表达式:

1.函数指针:

//1.函数指针:c语言常用
int cmp_1(const void* p1, const void* p2)
{
	return *((int*)p1) - *((int*)p2);
}
int main()
{
	//1.函数指针+排序:
	int arr[] = { 5,6,2,3,7,9,2,1,0 };
	int n = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, n ,sizeof(arr[0]), cmp_1);
	for (auto& e : arr)
	{
		cout << e << " ";
	}
	cout << endl;
	return 0;
}

2.仿函数:

//2.仿函数:C++
struct comper
{
	bool operator()(int& t1, int& t2)
	{
		return t1 > t2;
	}
};

int main()
{
	//2.仿函数排序+lambda表达式:
	vector<int> arr_2 = { 5,6,2,3,7,9,2,1,0 };
	sort(arr_2.begin(),arr_2.end(), comper());
	for (auto& e : arr_2)
	{
		cout << e << " ";
	}
	cout << endl;
	return 0;
}

3.lambda表达式:

int main()
{
	//3.lambda表达式:
	vector<int> arr_3 = { 5,6,2,3,7,9,2,1,0 };
	sort(arr_3.begin(), arr_3.end(), [](int t1, int t2) {return t1 > t2; });
	for (auto& e : arr_3)
	{
		cout << e << " ";
	}
	cout << endl;
	return 0;
}

1.基本使用:

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

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

2.参数列表。与普通函数的参数列表一致,如果不需要参数传递,则可以连同()一起省略mutable:默认情况下,lambda函数总是一个const函数,mutable可以取消其常量性。使用该修饰符时,参数列表不可省略(即使参数为空)。

3.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)
	{}
};




int main()
{
	vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2,
   3 }, { "菠萝", 1.5, 4 } };

	//1.使用lambda表达式显而易见如何进行比较!
	sort(v.begin(), v.end(), 
	[](const Goods& g1, const Goods& g2)
	{
		return g1._price > g2._price;
	});
}

2.捕捉列表的说明:

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

[var]:表示值传递方式捕捉变量var

[=]:表示值传递方式捕获所有父作用域中的变量(包括this)

[&var]:表示引用传递捕捉变量var

[&]:表示引用传递捕捉所有父作用域中的变量(包括this)

[this]:表示值传递方式捕捉当前的this指针

1.lambda表达式的常量性:

//捕捉数据默认是const的所以不可以进行修改因为const常变量。
//在()后加mutable关键字可以取消常量性:

在这里插入图片描述

2.各种情况的捕捉列表使用:

//1.a 和 b 的值捕捉。
在这里插入图片描述

//2.a 和 b 的引用捕捉:
在这里插入图片描述
//3.除了a是引用捕捉,其他的变量都是值拷贝捕捉。
在这里插入图片描述

//4.除了a是值捕捉其他变量都是引用捕捉。
请添加图片描述

注意:
a. 父作用域指包含lambda函数的语句块
b. 语法上捕捉列表可由多个捕捉项组成,并以逗号分割。
比如:[=, &a, &b]:以引用传递的方式捕捉变量a和b,值传递方式捕捉其他所有变量[&,a, this]:值传递方式捕捉变量a和this,引用方式捕捉其他变量。

c. 捕捉列表不允许变量重复传递,否则就会导致编译错误。

4.priority_queue使用包装器的比较函数?

使用declitype进行类型推断?

在这里插入图片描述
在这里插入图片描述

struct Goods
{
	string _name;// 名字
	double _price;// 价格
	int _evaluate;// 评价
	Goods(const char* str, double price, int evaluate)
		:_name(str)
		, _price(price)
		, _evaluate(evaluate)
	{}

	friend ostream& operator<<(ostream& cout, Goods& Date);
};

ostream& operator<<(ostream& cout, const Goods& Date)
{
	cout << "名字:" << Date._name << " - ";
	cout << "价格:" << Date._price << " - ";
	cout << "评价:" << Date._evaluate << endl;

	return cout;
}


int main()
{
	vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2,3 }, { "菠萝", 1.5, 4 } };
	//1.lambda表达式:
	auto com = [](const Goods& g1, const Goods& g2) {return g1._price > g2._price;};
	//2.priority_queue比较特性:decltype(com)推导对象类型非常有意义!
	priority_queue<Goods,vector<Goods>,decltype(com)> p1(v.begin(),v.end(),com);
	//3.结果的打印:
	while (!p1.empty())
	{
		std::cout << p1.top() << endl;
		p1.pop();
	}
}

在这里插入图片描述

总结:
1.可以在sort比较函数中去直接使用lambda表达式的对象。
2.在priority_queue这样的类型中考虑使用decltype进行类型推导。
3.并且lambda没有默认的构造函数所以只有唯一的对象可以重复使用。
4.lambda表达式优化了仿函数的操作,本质还是仿函数。

5.lambda表达式的本质?

1.lambda表达式可以认为一个对象这个对象的类型是编译器自动生成。
2.底层是这个类型的类的operator()的重载在使用的时候。
3.底层通过uuid算法==(vs2022使用了不同的命名风格)==去对类型进行命名。

int main()
{
	auto fun1 = []() {cout << "hello word" << endl; };
	auto fun2 = []() {cout << "hello word" << endl; };

	fun1();
	fun2();

	return 0;
}

在这里插入图片描述

九.包装器:

1.简单使用:

1.function包装器 也叫作适配器。C++中的function本质是一个类模板,也是一个包装器。
2.为什么需要包装器?保证模板的高效性。
3.不同的类型:函数指针,仿函数,lambda都是一个类型这些类型都可以通过模板的方式进行调用,但是通过模板调用就需要不同的模板参数。
4.可以通过包装器把不同的类型进行保证可以更好的适配模板。

//不去使用包装器:

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()
{	
	// 函数名
	cout << useF(f, 11.11) << endl;
	// 函数对象
	cout << useF(Functor(), 11.11) << endl;
	// lamber表达式
	cout << useF([](double d)->double { return d / 4; }, 11.11) << endl;
	return 0;
}

在这里插入图片描述

//观察结果表示实例化了三份代码!

//使用包装器:

请添加图片描述

1.Ret被调用的返回值类型。
2.Args调用的函数的参数。

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()
{	
	// 函数名
	function<double(double)> f1 = f;
	std::cout << useF(f1, 11.11) << endl;
	// 函数对象
	function<double(double)> f2 = Functor();
	std::cout << useF(f2, 11.11) << endl;
	// lamber表达式
	function<double(double)> f3 = [](double d)->double { return d / 4;};
	std::cout << useF(f3, 11.11) << endl;
	return 0;
}

在这里插入图片描述

2.一个题目:

在这里插入图片描述

逆波兰表达式求值

class Solution {
public:
    int evalRPN(vector<string>& tokens) {
        
        map<string,function<int(int,int)>> op = {
            {"+",[](int i , int j){return i+j;}},
            {"-",[](int i , int j){return i-j;}},
            {"*",[](int i , int j){return i*j;}},
            {"/",[](int i , int j){return i/j;}},
        };

        stack<int> s1;
        for(auto& tmp:tokens)
        {
            //1.字符栈:
            if(op.find(tmp) != op.end())
            {
                int right = s1.top();
                s1.pop();
                int left = s1.top();
                s1.pop();

                s1.push(op[tmp](left,right));
            }
            //2.数值栈:
            else
            {
                s1.push(stoi(tmp));
            }
        }
        return s1.top();
    }   
};

4.bind:

std::bind函数定义在头文件中,是一个函数模板,它就像一个函数包装器(适配器),接受一个可调用对象(callable object),生成一个新的可调用对象来“适应”原对象的参数列表。一般而言,我们用它可以把一个原本接收N个参数的函数fn,通过绑定一些参数,返回一个接收M个(M可以大于N,但这么做没什么意义)参数的新函数。同时,使用std::bind函数还可以实现参数顺序调整等操作。

在这里插入图片描述

1.一般概念:

调用bind的一般形式:auto newCallable = bind(callable,arg_list);
其中,newCallable本身是一个可调用对象,arg_list是一个逗号分隔的参数列表,对应给定的callable的参数。当我们调用newCallable时,newCallable会调用callable,并传给它arg_list中的参数。

1.控制参数的顺序:

void callable(int a, int b)
{
	cout << a - b << endl;
}


int main()
{
	//1.需要一个参数顺序:在placeholders命名空间中:
	auto fun1 = bind(callable, placeholders::_1, placeholders::_2);
	auto fun2 = bind(callable, placeholders::_2, placeholders::_1);

	fun1(10,20);
	fun2(10,20);
	return 0;
}

在这里插入图片描述

2.控制参数的个数:

void callable(int a, int b , int c)
{
	cout << a + b + c << endl;
}


int main()
{
	//1.需要一个参数顺序:在placeholders命名空间中:
	//2.存在一个默认绑定的概念,可以控制传入函数参数的个数:
	auto fun2 = std::bind(callable, 20,placeholders::_1, placeholders::_2);
	fun2(30,20);
	return 0;
}

1.fun()函数传多少参数就在bind的时候使用,几个placeholders::_1 。
2.注意顺序匹配的问题!
3.记住:placeholders::_1 placeholders::_2 placeholders::_3 应该对应的是原来函数的参数顺序。
4.我们正常传参对应传递应该是,bind里面的placeholders::_3的顺序!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/581057.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

制作一个RISC-V的操作系统十六-系统调用

文章目录 用户态和内核态mstatus设置模式切换核心流程封装代码背景解释代码示例解析解释目的 用户态和内核态 mstatus设置 此时UIE设置为1和MPIE为1&#xff0c;MPP设置为0 代表当前权限允许UIE中断发生&#xff0c;并且在第一个mret后将权限恢复为用户态&#xff0c;同时MIE也…

易错知识点(学习过程中不断记录)

快捷键专区&#xff1a; 注释&#xff1a;ctrl/ ctrlshift/ 保存&#xff1a;ctrls 调试&#xff1a; 知识点专区&#xff1a; 1基本数据类型 基本数据类型有四类&#xff1a;整型、浮点型、字符型、布尔型&#xff08;Boolean&#xff09;&#xff0c; 分为八种&#xff…

UE5 GAS开发P40 周期性效果,持续治疗

Periodic Gameplay Effects周期性的游戏效果 它们在一段时间内以固定的间隔重复应用到目标上。这种效果通常用于表示持续性伤害、治疗或其他影响&#xff0c;例如中毒、灼烧或回复效果。 修改GE_CrystalHeal,在Period改为每0.1秒执行一次 假如同时有三个持续时间在进行,那么这…

STM32与OLED显示屏通信(四针脚和七阵脚)

系列文章目录 STM32单片机系列专栏 C语言术语和结构总结专栏 文章目录 1. 单片机调试 2. OLED简介 3. 接线 4. OLED驱动函数 4.1 四针脚版本 OLED.c OLED.h OLED_Font.h 4.2 七针脚版本 引脚连接 OLED.c OLED.h OLED_Font.h 5. 主函数 工程文件模板 1. 单片机…

linux下安装deepspeed

安装步骤 一开始安装deepspeed不可以使用pip直接进行安装。 这时我们需要利用git进行clone下载到本地&#xff1a; git clone https://github.com/microsoft/DeepSpeed.git 进入到deepspeed的安装目录下 cd /home/bingxing2/ailab/group/ai4agr/wzf/Tools/DeepSpeed 激活…

verilog 从入门到看得懂---matlab 自动生成verilog

matlab 的强大不用多说&#xff0c;以前经常用simulink 生成c&#xff0c;最近尝试用simulink进行了verilog的生成&#xff0c;方法也很简单。 一个简单的示例如下。 1&#xff0c;新建一个模型文件&#xff0c;并且根据需要进行模型搭建 2.配置HDL生成模块 3.点击 generation…

纯血鸿蒙APP实战开发——全局状态保留能力弹窗

全局状态保留能力弹窗 介绍 全局状态保留能力弹窗一种很常见的能力&#xff0c;能够保持状态&#xff0c;且支持全局控制显隐状态以及自定义布局。使用效果参考评论组件 效果图预览 使用说明 使用案例参考短视频案例 首先程序入口页对全局弹窗初始化&#xff0c;使用Globa…

Linux学习之路 -- 进程篇 -- 自定义shell的编写

前面介绍了进程程序替换的相关知识&#xff0c;接下来&#xff0c;我将介绍如何基于前面的知识&#xff0c;编写一个简单的shell&#xff0c;另外本文的所展示的shell可能仅供参考。 目录 <1>获取用户的输入和打印命令行提示符 <2>切割字符串 <3>执行这个…

qt-C++笔记之滑动条QSlider和QProgressBar进度条

qt-C笔记之滑动条QSlider和QProgressBar进度条 —— 2024-04-28 杭州 本例来自《Qt6 C开发指南》 文章目录 qt-C笔记之滑动条QSlider和QProgressBar进度条1.运行2.阅读笔记3.文件结构4.samp4_06.pro5.main.cpp6.widget.h7.widget.cpp8.widget.ui 1.运行 2.阅读笔记 3.文件结构…

智慧供热一站式热网平衡多功能集成系统

供热管理地域分散的现实&#xff0c;决定必须采用先进技术手段开发软件系统&#xff0c;使各管理单位互联互通。在多年技术积累的基础上&#xff0c;公司采用目前成熟而且领先的技术架构&#xff0c;研发了适用于多个组织机构集中式管理的供热管理软件。使管理在技术上不再受地…

经典的目标检测算法有哪些?

一、经典的目标检测算法有哪些&#xff1f; 目标检测算法根据其处理流程可以分为两大类&#xff1a;One-Stage&#xff08;单阶段&#xff09;算法和Two-Stage&#xff08;两阶段&#xff09;算法。以下是一些经典的目标检测算法&#xff1a; 单阶段算法: YOLO (You Only Loo…

Java集合框架-Collection-queue

目录 一、Deque二、ArrayDequeArrayDeque层次结构图ArrayDeque概述ArrayDeque底层数据结构ArrayDeque常用方法(简略) 三、PriorityQueuePriorityQueue层次结构图PriorityQueue概述PriorityQueue 底层数据结构PriorityQueue常用方法(详细) Java里有一个叫做Stack的类&#xff0c…

[tkinter实现]汉字笔顺小软件

软件简介 本软件旨在帮助小学生通过互动式学习掌握汉字的基本笔画和笔顺。软件采用Tkinter库构建&#xff0c;提供了一个用户友好的图形界面&#xff0c;适合小学生使用。 主要功能&#xff1a; 汉字展示&#xff1a;软件能够展示单个汉字&#xff0c;并以动画形式演示其标准…

SAP SALV研究

1.SALV SALV用的是类cl_salv_table下的相关方法,一般用于对话框或者功能要求比较简单的数据显示,比如明细数据显示,不能单元格编辑或者某行某列编辑,可以实现全部数据编辑(实用性不强),如要实现编辑可参考这篇博文 https://mp.weixin.qq.com/s?__biz=MzU1MDAzMDY3MQ==…

车载系统的 加减串器应用示意

overview 车载系统上使用加减串器来实现camera&#xff0c; led液晶显示屏等 图像数据的远距离传输&#xff0c;将原先在短距离传输视频信号的mipi csi&#xff0c;dsi 等的TX&#xff0c;RX中间&#xff0c;插入加减串器&#xff0c;实现长距离的可靠传输。 示意图如下 往往…

开发 Chrome 浏览器插件入门

前言 简介 Chrome 插件是扩展 Chrome 浏览器的功能的软件程序。它们可以执行各种任务&#xff0c;例如阻止广告、增强隐私、添加新功能等等。 要开始编写 Chrome 插件&#xff0c;你需要掌握以下&#xff1a; 1.JavaScript语言 2.html 3.css 4.会使用chrome扩展开发手册…

C#设计树形程序界面的方法:创建特殊窗体

目录 1.TreeView控件 2.实例 &#xff08;1&#xff09;Resources.Designer.cs &#xff08;2&#xff09;Form1.Designer.cs &#xff08;3&#xff09;Form1.cs &#xff08;4&#xff09;生成效果 以树形来显示程序的菜单&#xff0c;可以更直观、更快捷地对窗体进行…

PostgreSQL大版本如何升级?

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是【IT邦德】&#xff0c;江湖人称jeames007&#xff0c;10余年DBA及大数据工作经验 一位上进心十足的【大数据领域博主】&#xff01;&#x1f61c;&am…

算法训练营day25

零、回溯算法理论 参考链接13.1 回溯算法 - Hello 算法 (hello-algo.com) 1.尝试与回退 之所以称之为回溯算法&#xff0c;是因为该算法在搜索解空间时会采用“尝试”与“回退”的策略。当算法在搜索过程中遇到某个状态无法继续前进或无法得到满足条件的解时&#xff0c;它会…

vue使用海康控件开发包——浏览器直接查看海康监控画面

1、下载控件开发包 2、安装插件&#xff08;双击/demo/codebase/HCWebSDKPlugin.exe进行安装&#xff09; 3、打开/demo/index.html文件 4、在页面上输入你的海康监控的登录信息进行预览 如果有监控画面则可以进行下面的操作 注意&#xff1a;以下操作都在Vue项目进行 5、复…
最新文章