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

news2024/11/16 3:30:39

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.coloradmin.cn/o/1630697.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

多用户商城系统哪个好,2024多用户商城系统这样选

在2024年选择适合的多用户商城系统是一项至关重要的决策&#xff0c;因为一个优秀的商城系统不仅可以提升用户体验&#xff0c;还能够帮助企业实现业务目标并取得长期成功。然而&#xff0c;在众多的选择中挑选出最适合的一个并不容易&#xff0c;需要综合考虑各种因素&#xf…

网页模版如何用

现在的网页模版已经得到了许多人的喜爱和使用。随着人们对互联网的需求不断增加&#xff0c;更多的公司和组织需要拥有自己的网站&#xff0c;以推广他们的品牌和服务。而网页模版为他们提供了一个简单而高效的方法来创建自己的网站。 网页模版是预先设计好的网站模板&#xff…

docker容器通俗理解

前言 如果大家没使用过Docker,就在电脑上下载一个VMware Workstation Pro&#xff0c;创建一个虚拟机安装一个windows操作一下感受一下,为什么我的电脑上还以再安装一台windows主机&#xff1f;其实你可以理解为Docker就是Linux系统的一个虚拟机软件。 我的Windows也可以安装…

(学习日记)2024.05.08:UCOSIII第六十二节:常用的结构体(os.h文件)第一部分

之前的章节都是针对某个或某些知识点进行的专项讲解&#xff0c;重点在功能和代码解释。 回到最初开始学μC/OS-III系统时&#xff0c;当时就定下了一个目标&#xff0c;不仅要读懂&#xff0c;还要读透&#xff0c;改造成更适合中国宝宝体质的使用方式。在学完野火的教程后&a…

Java基础教程- 1 Java 简介

更好的阅读体验&#xff1a;点这里 &#xff08; www.doubibiji.com &#xff09; 1 Java 简介 1.1 Java语言概述 1.1.1 Java是什么 &#xff08;了解一下即可&#xff0c;对学习没影响&#xff0c;总得了解一下它是怎么来滴~&#xff09; Java是由Sun公司&#xff08;已…

五一景点预约怎么预约 预约时间用备忘录设置提醒不怕错过

五一小长假即将来临&#xff0c;相信很多人和我一样&#xff0c;已经跃跃欲试&#xff0c;准备踏上旅途&#xff0c;去探索那些心仪已久的风景名胜。但在这个旅游高峰期&#xff0c;不少热门景点都需要提前预约购票。那么&#xff0c;怎么预约才能确保顺利游览呢&#xff1f; …

在智慧城市的建设中智能电表发挥什么作用

在智慧城市的建设中&#xff0c;智能电表扮演着至关重要的角色。智慧城市是一个利用信息技术手段提升城市运行效率和质量的新型城市模式&#xff0c;旨在通过信息和通信技术的应用&#xff0c;提高城市管理、公共服务、环境保护等方面的质量和效率&#xff0c;促进城市的可持续…

金价大跳水,美梦变噩梦!2024真正适合普通人的靠谱创业项目!2024适合30-40岁轻资产小生意

4月22日晚间&#xff0c;向上“狂飙”了一个多月的金价突然就“大跳水”。当日&#xff0c;每克金价均下调14块。在这次跳水中&#xff0c;有人欢喜有人愁&#xff1a;有投资者自报做空金价一夜狂赚14万&#xff0c;也有投资者哭诉&#xff0c;头晚进货到早上就净亏损2万&#…

中电金信:GienTech动态| 获奖、合作、与伙伴共谋数字化转型…

—— —— GienTech动态 —— —— 中电金信携“源启”亮相第十二届中国电子信息博览会 4月11日&#xff0c;为期三天的“第十二届中国电子信息博览会”在深圳顺利闭幕。中国电子信息博览会是中国规模最大、最具国际影响力的电子信息产业盛会之一。本届大会以“全球视野&#x…

AI预测福彩3D第9套算法实战化测试第6弹2024年4月28日第6次测试

今天继续进行新算法的测试&#xff0c;今天是第6次测试。好了&#xff0c;废话不多说了&#xff0c;直接上图上结果。 2024年4月28日福彩3D预测结果 6码定位方案如下&#xff1a; 百位&#xff1a;5、4、9、3、1、0 十位&#xff1a;2、3、5、6、1、7 个位&#xff1a;4、5、0、…

Goby 漏洞发布|禅道 /api.php/v1/users 未授权访问漏洞

漏洞名称&#xff1a;禅道 /api.php/v1/users 未授权访问漏洞 English Name&#xff1a;Zen Road /api.php/v1/users Unauthorized Access Vulnerability CVSS core: 9.8 影响资产数&#xff1a;69265 漏洞描述&#xff1a; 禅道是一款开源的项目管理软件&#xff0c;旨在…

Leetcode—1017. 负二进制转换【中等】(string列表初始化、反向迭代器)

2024每日刷题&#xff08;120&#xff09; Leetcode—1017. 负二进制转换 实现代码 class Solution { public:string baseNeg2(int n) {string ans;while(n ! 0) {ans to_string(n & 1);n -(n >> 1);}return ans.empty() ? "0": string{ans.rbegin(),…

Linux安装Matlab运行时

一般而言&#xff0c;安装Matlab的linux系统是带桌面版的&#xff0c;如果没带&#xff0c;不在本教程范围内。 一、下载Matlab 下载地址&#xff1a;MATLAB Runtime - MATLAB Compiler - MATLAB 本教程使用R2020b(9.9) 二、linux系统中进行解压 将zip传入linux系统&#xf…

vue2主体页面进行拆分

目录 一.组件化 二.新建Header.vue页面 三.Aside.vue代码 四.Main.vue代码如下 五.Home.vue代码如下 六.index.js代码如下&#xff1a; 七.项目效果图 在Vue.js 2中&#xff0c;将主体页面进行拆分是一种常见的做法&#xff0c;它有助于提高代码的可维护性和可读性。页面…

Halcon 3D 使用3D ROI截取模型

Halcon 3D 使用3D ROI截取模型 链接:https://pan.baidu.com/s/1UfFyZ6y-EFq9jy0T_DTJGA 提取码:ewdi * 1.读取图片 ****************

新装电脑Flutter环境部署坑汇总(持续更新)

1.本地安装&#xff0c;安装fvm的坑 本人电脑使用windows &#xff0c;安装fvm则一般使用choco安装&#xff0c;那么首先需要安装choco,打开powershell/或者cmd运行以下命令&#xff1a; Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager…

word启动缓慢之Baidu Netdisk Word Addin

word启动足足花了7秒钟&#xff0c;你知道我这7秒是怎么过来的吗&#xff1f; 原因就是我们可爱的百度网盘等APP&#xff0c;在我们安装客户端时&#xff0c;默认安装了Office加载项&#xff0c;不仅在菜单栏上加上了一个丑陋的字眼&#xff0c;也拖慢了word启动速度........ 解…

STM32H750片外QSPI下载算法文件(stldr)生成

STM32H750片外QSPI下载算法文件&#xff08;stldr&#xff09;生成 &#x1f33f;相关篇《STM32H750片外QSPI启动配置简要》&#x1f4cc;参考实现资料&#xff1a; https://github.com/lchnu/STM32H750XBH_ARTPIQSPI_W25Q64JV https://gitee.com/wangchief/H750_W25QXX ✨利…

链路层安全扩展——L2TP协议

链路层安全扩展——L2TP协议 PPP协议 协议概念 说到数据链路层的安全协议&#xff0c;我们不得不先提一下PPP协议&#xff0c;后面的PAP、CHAP与L2TP协议都是围绕它展开的。&#xff08;PPP不是本文重点&#xff0c;很多细节没有提到&#xff0c;到时候会专开一篇文章讲PPP&…

Java 8 Stream API的使用

一、什么是Stream&#xff1f; Stream&#xff08;流&#xff09;是一个来自数据源的元素队列&#xff0c;它可以支持聚合操作。 数据源&#xff1a;流的数据来源&#xff0c;构造Stream对象的数据源&#xff0c;比如通过一个List来构造Stream对象&#xff0c;这个List就是数…