c++11详解

news2024/11/27 8:32:04

目录

1.列表初始化

2.声明

3.右值引用和移动语句

4. c++11新的类功能

5. 可变参数模板

 6.lambda表达式

7.包装器

8. 后言


1. 列表初始化

1.1 {}的初始化

(1) c++98标准规定可以使用{}对数组以及结构体进行统一的列表初始化.

struct Point
{
	int _x;
	int _y;
};

int main()
{
	int a1[] = { 1, 2, 3, 4, 5 };
	int a2[5] = { 0 };
	Point p = { 1, 2 };
	return 0;
}

(2) c++11里面增加了对{}的使用范围, 可以对所以的内置类型自定义类型的类型使用初始化列表, 可以加=;也可以不加.

struct Point
{
	int _x;
	int _y;
};

int main()
{
	int x1 = 1;
	int x2{ 2 };
	int a1[]{ 1, 2, 3, 4, 5 };
	int a2[5]{ 0 };
	Point p{ 1, 2 };
	return 0;
}

(3) 创建对象也可以使用列表初始化的方式调用构造函数初始化.

class Date
{
public:
	Date(int year, int month, int day)
		:_year(year)
		, _month(month)
		,_day(day)
	{
		cout << "Date(int year, int month, int day)" << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1(2022, 1, 1);

	Date d2{ 2022, 1, 2 };
	Date d3 = { 2022, 1, 2 };
	return 0;
}

 1.2 initializer_list

 (1)initializer_list的类型:

(2)initializer_list的使用场景:

一般作为构造函数的参数; 方便初始化对象,  也可以是operator=的参数, 那么就可以用{}进行赋值.

c++11里面对STL的构造函数都添加了initializer_list; 就是允许{}进行对象的初始化的一种方法.支持范围for.


//int main()
//{
//	auto il = { 10, 20 ,30 };
//	cout << typeid(il).name() << endl;
//	return 0;
//}

//int main()
//{
//	vector<int> v{ 1, 2, 3, 4 };
//	list<int> lt{ 1, 2 };
//	map<string, string> sict = { {"sort", "排序"}, {"inset", "插入"} };
//
//	v = { 1, 2, 3 };
//	return 0;
//}

int main()
{
	//多参数构造类型转换: 构造+拷贝构造-> 直接构造.
	Date d2 = { 2023, 11, 25 };
	auto il1 = { 10, 20, 30, 40, 50 };

	initializer_list<int> il2 = { 11, 22, 33 };

	initializer_list<int>::iterator it2 = il2.begin();
	//while (it2 != il2.end())
	//{
	//	cout << *it2 << endl;
	//	it2++;
	//}
	//cout << endl;

	for (auto e : il2)
	{
		cout << e << " ";
	}
	cout << endl;
	return 0;
}

2.  声明

2.1 auto

c++98里面定义auto是一种类型局部自动存储类型, but在局部域中定义的变量就是默认是局部自动存储类型, 然而在c++11里面就是自动推断类型, 必须进行显示初始化, 以便于编译器将类型设置为初始化的类型.

 2.2 decltype

 思考一个小问题: 如果我要使用一个我不知道的类型, 怎么做? 

        使用auto吗? 那肯定是不行的, auto在初始化的时候必须给定类型才可以让编译器去推断.那么就要用到decltype. 所以decltype的作用就是将变量的类型声明为表达式指定的类型.

int main()
{
	int i = 1;
	double d = 2.2;

	/*cout << typeid(i).name() << endl;
	cout << typeid(d).name() << endl;*/

	auto j = i;
	auto ret = i * d;
	decltype(ret) x;

	vector<decltype(ret)> v;
	v.push_back(1);
	v.push_back(1.1);

	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
	return 0;
}

2.3 nullptr

c++里面定义NULL为字面常量0;  但是这个字面常量0既可以表示指针常量, 还可以表示整形常量.所以c++11里面添加nullptr表示为空指针.

 2.4 STL的一些变化

(1) array:

        这个接口其实是对应vector, 其实也没啥用处.

(2) forward_list:

      用来任意位置进行删除和插入操作.

(3) unordered_map

(4) unordered_set

上面两个序列式容器之前在哈希和红黑树里面都有出现, 这里不过多介绍了.

3. 右值引用和移动语句

3.1 左值和右值

 左值:

        一般是变量值或者是解引用的指针; 一般是在赋值符号的左边, 但是右值一定不会出现在赋值符号的左边. 可以获取到地址并且可以对它进行赋值, 如果是const就不行咧.

左值引用:

        对左值进行引用, 给左值取别名.

int main()
{
	//左值
	int* p = new int(0);
	int  b = 1;
	const int c = 2;

	//左值引用:
	int*& rp = p;
	int& rb = b;
	const int& rc = c;
	int& pvalue = *p;
	return 0;
}

右值:

        通常是字面常量, 表达式返回值, 函数返回值(不能是左值引用的返回值), 右值可以出现在赋值符号的右边, 但是不能获取到地址. 还有就是右值无法在赋值符号的左边; 因为左边必须是左值, 右值有常属性所以不行!

右值引用:

        对右值的引用, 对右值取别名.

注意:

        右值是无法取到地址的, 但是取别名之后, 被存储到特定的位置, 并且可以拿到那个位置的地址.

int main()
{
	double x = 1.1, y = 2.2;

	10;
	x + y;
	fmin(x, y);

	int&& rr1 = 10;
	int&& rr2 = x + y;
	int&& rr3 = fmin(x, y);
	return 0;
}

3.2左值引用和右值引用的区别

左值引用:

        (1) 左值引用只能用于引用左值, 不能用来引用右值;

        (2) but const修饰的左值引用可以引用左值和右值.

右值引用:

        (1) 右值引用只能引用右值, 不能引用左值;

        (2)but move修饰过的左值可以被右值引用使用.

int main()
{
	//左值引用
	int a = 10;
	int& ra1 = a;

	const int& ra2 = 10;
	const int& ra2 = a;
	
	//右值引用
	int&& aa1 = 10;
	int a = 10;
	int&& aa2 = move(a);

	return 0;
}

3.3 右值引用的使用场景

       (1) 先看看左值引用的应用场景, 那就是作为参数或者返回值, 但是如果出了作用域还在那么就可以使用左值引用. 因为左值引用是局部变量, 出了作用域之后不在那怎么把数据传回来捏?

        (2) 右值引用和移动语义的本质: 就是将参数的右值资源直接夺取过来, 然后就不用进行深拷贝了, 这就是移动构造. 进一步提高效率.

        (3) 编译器在调用构造的时候, 有移动构造就调用移动构造, 没有就调用拷贝构造.

        (4) 移动赋值:  重载operator=的时候使用右值引用.

 3.4 右值引用左值的场景分析

右值引用左值不能直接引用, 需要加move进行将左值转化为右值才可以, 但是左值的本质属性还是左值, 并没有改变.  如果是作为拷贝构造函数的参数, 那么就一定要注意, 如果拷贝赋值move之后, 就会将原来的数据交给新的对象, 原来的对象就被置空了.

注意:右值被右值引用之后属性变成了左值.

void push_back (value_type&& val);
int main()
{
 list<bit::string> lt;
 bit::string s1("1111");

 // 这里调用的是拷贝构造
 lt.push_back(s1);

 // 下面调用都是移动构造
 lt.push_back("2222");
 lt.push_back(std::move(s1));
 return 0;
}

3.5 完美转发

使用到模板以及右值引用. 模板里面的&&是表示万能引用, 既可以接受左值, 又可以接收右值,

可以看看下面的代码:

为啥都变成左值啦?  因为右值被右值引用之后属性变成了左值.

如果我们就是想要右值引用怎么办捏? 

        上完美转发, std:: forward. (保持对象原生类型的属性)

void Fun(int& x) { cout << "左值引用" << endl; }
void Fun(const int& x) { cout << "const 左值引用" << endl; }

void Fun(int&& x) { cout << "右值引用" << endl; }
void Fun(const int&& x) { cout << "const 右值引用" << endl; }

template<typename T>
void perfectForward(T&& t)
{
	//Fun(t);
    //完美转发
    Fun(forward<T>(t));
}

int main()
{
	//右值
	perfectForward(10);
	
	//左值
	int a;
	perfectForward(a);
	perfectForward(move(a));

	//左值
	const int b = 9;
	perfectForward(b);
	perfectForward(move(b));

	return 0;
}
3.5.1 完美转发使用场景
template<class T>
struct ListNode
{
	ListNode* _next = nullptr;
	ListNode* _prev = nullptr;
	T _data;
};
template<class T>
class List
{
	typedef ListNode<T> Node;
public:
	List()
	{
		_head = new Node;
		_head->_next = _head;
		_head->_prev = _head;
	

	void PushBack(T&& x)
	{
		//Insert(_head, x);
		Insert(_head, std::forward<T>(x));
	}

	void PushFront(T&& x)
	{
		//Insert(_head->_next, x);
		Insert(_head->_next, std::forward<T>(x));
	}

	void Insert(Node* pos, T&& x)
	{
		Node* prev = pos->_prev;
		Node* newnode = new Node;
		newnode->_data = std::forward<T>(x); // 关键位置
		// prev newnode pos
		prev->_next = newnode;
		newnode->_prev = prev;
		newnode->_next = pos;
		pos->_prev = newnode;
	}

	void Insert(Node* pos, const T& x)
	{
		Node* prev = pos->_prev;
		Node* newnode = new Node;
		newnode->_data = x; // 关键位置
		// prev newnode pos
		prev->_next = newnode;
		newnode->_prev = prev;
		newnode->_next = pos;
		pos->_prev = newnode;
	}

private:
	Node* _head;
};
int main()
{
	List<string> lt;
	lt.PushBack("1111");
	lt.PushFront("2222");
	return 0;
}

4. c++11新的类功能

c++里面6个默认构造的类分别是: 

        1. 构造函数; 2. 析构函数; 3. 拷贝构造函数; 4. 拷贝赋值重载; 5. 取地址重载; 6. const 取地址重载.

现在又多了移动构造移动赋值,:

注意:

        (1) 如果你没有写移动构造函数, 并且没有写析构函数, 拷贝构造函数, 拷贝赋值重载函数的话, 那么就编译器自动生成移动构造函数.

        (2)如果你没有写赋值重载函数, 并且没有写析构函数, 拷贝构造函数, 拷贝赋值重载函数那么就编译器自动生成移动赋值函数.

        (3)对于内置类型进行浅拷贝, 自定义类型如果实现了移动构造就使用移动构造, 如果没有使用的话就进行深拷贝.

        2.强制生成默认函数的关键字: default 进行显示的移动构造生成.

        3.强制不生成默认函数的关键字: delete

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;
    //强制不生成默认函数
    Person(Person&& p) = delete;

private:
	bit::string _name;
	int _age;
};
int main()
{
	Person s1;
	Person s2 = s1;
	Person s3 = std::move(s1);
	return 0;
}

5. 可变参数模板

// Args 是一个 模板参数包 args 是一个 函数形参参数包
// 声明一个参数包 Args...args ,这个参数包中可以包含 0 到任意个模板参数。
template < class ... Args >
void ShowList ( Args ... args )
{}
注意: 使用参数包是ex到家的, 还有获取参数包得值! 学的想吐血!!!
获取参数值是是用到递归调用的方法.

template <class T>
void PrintArg(T t)
{
	cout << t << " ";
}
//展开函数
template <class ...Args>
void ShowList(Args... args)
{
	int arr[] = { (PrintArg(args), 0)... };
	cout << endl;
}
int main()
{
	ShowList(1);
	ShowList(1, 'A');
	ShowList(1, 'A', std::string("sort"));
	return 0;
}

5.1 比较emplace和insert

emplate的接口是支持万能模板以及可变参数包的. 那和有啥区别捏? 而且emplace接口的优势在哪里捏?

支持了可变参数包, 以及emplace_back是直接构造, push_back是先构造再移动构造.

 6.lambda表达式

引入: lambda底层就是仿函数, 由于当我们定义自定义类型的比较的时候, 都需要写一个类来规定比较的方法(仿函数), 使用起来非常不方便.定义就弄出来lambda表达式.

 6.1lambda表达式的语法:

(1) 格式:[capture-list] (parameters) mutable -> return-type { statement }

capture-list: 捕捉列表, 捕捉上下文的变量

parameters: 参数列表; 和普通函数是一样的.

mutable: 看代码吧, 更加形象.

->returntype: 返回值类型;  和普通函数是一样的.
statement:函数体 和普通函数是一样的.
(2) lambda的类型是 class <lambda_a62159c664704dbd449a2ea762c73c4d>
lambda + uuid;
(3) lambda表达式是一个匿名函数, 如果要使用据需要auto去拿.
int main()
{
	//啥都不做
	[] {};

	//捕捉=上下的变量,返回值类型推到为int.
	int a = 3, b = 4;
	[=] {return a + 3; };

	//没有返回值, 捕捉&的上下文变量, 然后用到函数体里面
	/*auto fun1 = [&](int c) {b = a + c; };
	fun1(10);
	cout << a << " " << b << endl;*/

	/*auto fun2 = [=, &b](int c)->int{return b += a + c; };
	cout << fun2(10) << endl;*/

	int x = 10;
	auto add_x = [x](int a)mutable {x *= 2; return a + x; };
	cout << typeid(add_x).name() << endl;
	cout << add_x(10) << endl;
}

 6.2 捕捉列表

捕捉方式是传值还是传递引用都有区别:

(1) [var]: 传值捕捉

(2) [=]: 传值捕捉, 捕获子作用域里面所有变量(包括this);

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

(4) [&]:表示引用传递捕捉所有父作用域中的变量(包括this)
(5) [this]:表示值传递方式捕捉当前的this指针
注意:
(1)捕捉列表有多个参数, 就要用 ' ,' 隔开.
(2)不允许重复传递, 编译器会报错;
(3)lambda表达式之间不能相互赋值;

 6.3 仿函数和lambda表达式:

其实底层来看这两个东西是一样的;  仿函数只是在类里面重载operator()的对象.

 

7. 包装器

7.1 为啥需要包装器, 这是个啥?

(1) function是包装器也是适配器, 本质就是一个类模板.

ret = func(x);

上面这段代码可以是函数返回值, 仿函数, lambda表达式, 那你找到它到底是上面吗?

根本不知道, 而且编译器还会实例化上面出现的所有对象, 那么编译器必定效率低下.

(2) function的头文件#include<functional>

7.2 bind

bind就是一种适配器, 支持可调用对象(函数, 仿函数, lambda表达式), 生成一个新的可调用对象来适应原来的参数列表,  其中的 _n 是一种占位符, 表示可调用对象的位置.

#include <functional>
int Plus(int a, int b)
{
	return a + b;
}

class Sub
{
public:
	int sub(int a, int b)
	{
		return a - b;
	}
};

int main()
{
	//表示绑定函数plus 参数分别由调用 func1 的第一,二个参数指定
	function<int(int, int)> func1 = std::bind(Plus, placeholders::_1, placeholders::_2);

	auto func2 = std::bind(Plus, 1, 2);
	cout << func1(1, 2) << endl;
	cout << func2() << endl;
	 
	Sub s;
	 绑定成员函数
	function<int(int, int)> func3 = std::bind(&Sub::sub, s,	placeholders::_1, placeholders::_2);
	
	
	 参数调换顺序
	//std::function<int(int, int)> func4 = std::bind(&Sub::sub, s,
	//	placeholders::_2, placeholders::_1);
	//cout << func3(1, 2) << endl;
	//cout << func4(1, 2) << endl;

	return 0;
}

8. 后言

其中c++11里面的新增功能, 如果不使用/ 少使用很容易就忘掉, 就比如lambda, 老长的你能在很久不用记得很清楚阿? xdm多用多写! 还有给博主三联, 你绝对学的很好!!!

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

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

相关文章

Kubernetes:云原生时代的核心引擎

文章目录 一、Kubernetes简介&#xff1a;引领云原生潮流二、K8s的核心特性&#xff1a;自动化与智能化三、K8s的实践应用&#xff1a;打造高效云原生应用架构四、K8s的挑战与应对&#xff1a;安全与性能并重五、K8s的未来展望&#xff1a;无限可能与挑战并存《Kubernetes快速进…

YOLOv8-pose针对视频实时提取打印对应关节点序号及坐标

因为我在找如何提取YOLOv8-pose的关键点的时候&#xff0c;大多都是针对静态图像&#xff0c;视频直接套用不太行&#xff0c;因此就改进了一下&#xff0c;如下&#xff1a; 初步代码&#xff1a; import torch # 导入PyTorch库 import cv2 as cv # 导入OpenCV库并重命名为…

上位机图像处理和嵌入式模块部署(树莓派4b进行自动化测试)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 硬件、软件功能开发ok只是产品开发的第一步。怎么做到自动化测试、保证产品质量才是关键。很多时候&#xff0c;我们给客户提供了功能&#xff0c;…

Rust HTTP 客户端:易于使用、功能强大 | 开源日报 No.228

seanmonstar/reqwest Stars: 8.9k License: Apache-2.0 reqwest 是一个易于使用且功能强大的 Rust HTTP 客户端。 异步和阻塞客户端支持普通数据、JSON、urlencoded 和 multipart 数据格式可定制的重定向策略支持 HTTP 代理和系统原生 TLS 或 rustls 的 HTTPSCookie 存储功能…

一、路由基础

1.路由协议的优先级 路由器分别定义了外部优先级和内部优先级&#xff08;越小越优&#xff09; 路由选择顺序&#xff1a;外部优先级>>内部优先级&#xff08;相同时&#xff09; ①外部优先级&#xff1a;用户可以手工为各路由协议配置的优先级 ②内部优先级&#xf…

Nuxt3 实战 (五):Header 头部布局

前言 这两周一直忙公司系统的迭代需求&#xff0c;没啥时间捣鼓自己的小项目&#xff0c;趁着项目进入测试收尾阶段&#xff0c;抽空把 Layout 布局的 Header 部分先搞好。 需求拆分 顶部左侧放 Logo&#xff0c;右边放社交图标&#xff0c;暗黑模式切换提前准备好 Logo 和网…

Centos8操作系统安装mysql5.7版本以及报错解决

目录 一、卸载MySql 1.首先查看已安装的mysql 2.逐个或者执行一下命令统一卸载掉 注意&#xff1a; 3. 卸载其他相关文件 二、安装MySql 1.安装mysql的rpm源 2.安装MySql 如果遇到以下错误&#xff1a; 问题一: 解决方法&#xff1a; 问题二、 解决方法&#xff1…

【一刷《剑指Offer》】面试题 9:斐波那契数列(扩展:青蛙跳台阶、矩阵覆盖)

力扣对应链接&#xff1a;LCR 126. 斐波那契数 - 力扣&#xff08;LeetCode&#xff09; 牛客对应链接&#xff1a;斐波那契数列_牛客题霸_牛客网 (nowcoder.com) 核心考点&#xff1a;空间复杂度&#xff0c;fib 理解&#xff0c;剪枝重复计算。 一、《剑指Offer》内容 二、分…

Linux多进程(二)进程通信方式三 共享内存

共享内存提供了一个在多个进程间共享数据的方式&#xff0c;它们可以直接访问同一块内存区域&#xff0c;因此比使用管道或消息队列等通信机制更高效。在多进程程序中&#xff0c;共享内存通常与信号量一起使用&#xff0c;以确保对共享内存的访问是线程安全的。 一、打开/创建…

俊杰测评:电视盒子什么牌子好?电视盒子品牌排行榜

欢迎各位来到俊杰的数码测评频道&#xff0c;每年我会进行数十次电视盒子测评&#xff0c;今年已经买过二十多款电视盒子了&#xff0c;本期的测评主题是电视盒子什么牌子好&#xff0c;通过十天的深入详细对比后我整理了电视盒子品牌排行榜&#xff0c;近期想买电视盒子的可以…

代码随想录算法训练营第五十一天| 309.最佳买卖股票时机含冷冻期,714.买卖股票的最佳时机含手续费,总结

题目与题解 参考资料&#xff1a;买卖股票总结 309.最佳买卖股票时机含冷冻期 题目链接&#xff1a;309.最佳买卖股票时机含冷冻期 代码随想录题解&#xff1a;309.最佳买卖股票时机含冷冻期 视频讲解&#xff1a;动态规划来决定最佳时机&#xff0c;这次有冷冻期&#xff01;|…

python获取文件路径

文件&#xff1a;allpath_parameter.py # 获取当前目录路径 # current_dir os.getcwd() # 获取当前目录路径 realpath00 os.path.abspath(os.path.join(os.path.dirname(os.path.split(os.path.realpath(__file__))[0]), .)) print(realpath00)# 获取当前目录的上级目录路…

Centos安装/更新Docker

首先要配置好Centos 配置好静态IP 替换yum源为阿里云 Docker是什么&#xff1f; Docker 是一种开源的应用容器引擎&#xff0c;让开发者可以打包他们的应用以及依赖包到一个可移植的容器中&#xff0c;然后部署到任何流行的 Linux 机器上。是一种虚拟化的技术&#xff0c;可以把…

linux 编译opencv遇到问题

linux编译opencv4.8的时候遇到问题 Error: suffix or operands invalid for vpand看到很多说法是升级as这个工具的版本&#xff0c;自测是从2.20升级到2.27就可以了

12G-SDI视频分配器JR104D-4K-SDI

JR104D-4K-SDI 12G-SDI分配器1分4,12G-SDI分配器1分2,12G-SDI分配器1分8,机架式12G-SDI分配器1分4&#xff0c;12G-SDI分配器4组1分4&#xff0c;12G-SDI分配器16组1分4&#xff0c; 广播级指标生产厂家。 一、产品介绍&#xff1a; JR104D-4K-SDI视频分配器&#xff0c;是按…

Docker网络模式与cgroup资源控制

前言 在 Docker 中&#xff0c;网络模式和 cgroup 资源控制作为关键功能&#xff0c;对于容器的性能优化和资源管理起着至关重要的作用。本文将介绍 Docker 的网络模式和cgroup资源控制&#xff0c;探讨不同网络模式的特点以及如何利用 cgroup 资源控制机制来有效管理容器的资…

【SSM进阶学习系列丨整合篇】Spring+SpringMVC+MyBatis 框架配置详解

文章目录 一、环境准备1.1、创建数据库和表1.2、导入框架依赖的jar包1.3、修改Maven的编译版本1.4、完善Maven目录1.5、编写项目需要的包1.6、编写实体、Mapper、Service 二、配置MyBatis环境2.1、配置mybatis的主配置文件2.2、编写映射文件2.3、测试环境是否正确 三、配置Spri…

机器学习——过拟合

一、过拟合得表现 模型在训练过程中&#xff0c;除了会出现过拟合现象&#xff0c;还有可能出现欠拟合的情况。相比而言&#xff0c;后者通常发生在建模前期&#xff0c;只要做好特征工程一般可以解决模型欠拟合问题。下图描述了模型在训练数据集上的三种情况&#xff1a; 其…

【深度学习实战(11)】搭建训练框架之dataset,dataloader

一、dataset和dataloader要点说明 在我们搭建自己的网络时&#xff0c;往往需要定义自己的dataset和dataloader&#xff0c;将图像和标签数据送入模型。 &#xff08;1&#xff09;在我们定义dataset时&#xff0c;需要继承torch.utils.data.dataset&#xff0c;再重写三个方法…

文本高效拆分内容,根据空行高效拆分文本内容,文本文档管理更轻松

文本文档是我们日常生活和工作中不可或缺的一部分。然而&#xff0c;随着文本内容的不断增加&#xff0c;如何高效、有序地管理这些文档成为了一个挑战。传统的文本编辑工具往往无法满足我们对于文档整理的需求&#xff0c;而手动整理又费时费力。现在&#xff0c;我们为您带来…