【C++11】基础改变

news2024/11/24 8:27:47

1.关于C++11的介绍

  1. C++11标准为C++编程语言的第三个官方标准,正式名叫ISO/IEC 14882:2011 - Information technology -- Programming languages -- C++ 。 在正式标准发布前,原名C++0x。它将取代C++标准第二版ISO/IEC 14882:2003 - Programming languages -- C++ 成为C++语言新标准。
  2. C++11包含了核心语言的新机能,并且拓展C++标准程序库,并且加入了大部分的C++ Technical Report 1程序库(数学上的特殊函数除外)。C++ 标准委员会计划在2010年8月之前完成对最终委员会草案的投票,以及于2011年3月3召开的标准会议完成国际标准的最终草案。最终于2011年8月12日公布,并于2011年9月出版。2012年2月28日的国际标准草案(N3376)是最接近于现行标准的草案(编辑上的修正)。此次标准为13年第一次重大修正。
  3. ISO将在2014年和2017年发布C++的后续版本 。

具体修改:

  • 1.对C++核心语言的扩充
  • 2.核心语言运行期的强化(右值引用和 move 语义;泛化的常数表达式;对POD定义的修正)
  • 3.核心语言建构期表现的加强(外部模板)
  • 4.核心语言使用性的加强(初始化列表;统一的初始化;类型推导[auto关键字];以范围为基础的 for 循环;Lambda函数与表示法;另一种的函数语法;对象构建的改良;显式虚函数重载;空指针;强类型枚举;角括号;显式类型转换;模板的别名;无限制的unions)
  • 5.核心语言能力的提升(变长参数模板;新的字符串字面值;用户自定义的字面值;多任务存储器模型;thread-local的存储期限;使用或禁用对象的默认函数;long long int 类型;静态assertion;允许sizeof运算符作用在类型的数据成员上,无需明确的对象;)
  • 6.C++标准程序库的变更(标准库组件的升级;线程支持;多元组类型;散列表;正则表达式;通用智能指针;可扩展的随机数功能;包装引用;多态函数对象包装器;用于元编程的类型属性;用于计算函数对象返回类型的统一方法)。

2.统一的列表初始化 

{}初始化
  • 在C++98中,标准允许使用花括号{}对数组元素进行统一的列表初始值设定。
struct Func
{
	int _x;
	int _y;
};
int main()
{
	int arr1[] = { 1,2,3,4,5,6 };
	int arr2[4] = { 0 };  //0 0 0 0 
	Func f = { 1,2 };
}
  • C++11扩大了用大括号括起来的列表{初始化列表}的使用范围,使其可用于所有的内置类型和用户自定义的类型,使用初始化列表时,可添加等号,也可不添加。
//C++11
struct Func2
{
	int _x;
	int _y;
};
int main()
{
	//1.使用{}对数组元素进行初始化
	int arr1[]{ 1,2,3,4,5,6 };   
	  //int arr1[]={1,2,3,4,5,6};
	int arr2[4]{ 0 };  //0 0 0 0 

	//2.使用{}对结构体元素初始化
	Func2 f{ 1,2 };   //不加等号

	//3.使用{}对内置类型初始化
	int x1 = 10;  //一般就用这个
	int x2 = { 10 };
	int x3{ 10 };

	//4.C++11中列表初始化也可以用于new表达式中(C++98无法初始化)
	int* p1 = new int[4]{ 0 };       //0 0 0 0,不加等号
	int* p2 = new int[4]{ 1,2,3,4 }; //不加等号
     // int* p3 =new int(0);
	return 0;
}

小细节:在对new表达式初始化时不需要加=,我们以前讲new和delete那节时就没有加=。

  • 创建对象时也可以使用列表初始化方式调用构造函数初始化。
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, 12, 12);

	//C++11支持的列表初始化,这里也会调用构造函数初始化
	Date d2 = { 2022, 11, 11 }; //可添加等号
	Date d3{ 2022, 10, 10 };    //可不添加等号
	return 0;
}

  3.initializer_list容器

提起容器相比各位都不太陌生了。

在C++11中又提出了一个新容器initializer_list

初始值设定项列表

template<class T> class initializer_list; //初始值设定项列表

此类型用于访问 C++ 初始化列表中的值,该列表是 类型的元素列表。

这种类型的对象由编译器根据初始化列表声明自动构造,初始化列表声明是括在大括号中的逗号分隔元素的列表:const Tauto il = { 10, 20, 30 }; // initializer_list

3.1 三大成员函数

 nitializer_list本质就是一个大括号括起来的列表,如果用auto关键字定义一个变量来接收一个大括号括起来的列表,然后以typeid(变量名).name()的方式查看该变量的类型,此时会发现该变量的类型就是initializer_list。

 接口函数的应用:

double test_initializer_list2()
{
	initializer_list<double> ilt = { 1.2, 4.3, 6.4 };
	initializer_list<double>::iterator it = ilt.begin();
	while (it != ilt.end())
	{
		cout << *it << " ";//1.2, 4.3, 6.4
		it++;
	}
	cout << endl;
	cout << ilt.size()<< endl;
	for (auto e : ilt)
	{
		cout << e << " ";//1.2, 4.3, 6.4
	}
	return 0;
}

 3.2initializer_list的应用场景

initializer_list容器没有提供对应的增删查改等接口,因为initializer_list并不是专门用于存储数据的,而是为了让其他容器支持列表初始化的。

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()
{
	//vector
	// 使用大括号对容器赋值,{}调用构造函数构造一个vector对象,再赋值
	vector<int> v1 = { 1, 2 ,3 ,4, 5 };
	vector<int> v2{ 1, 2, 3, 4, 5 };
	vector<Date> v3 = { { 2022, 1, 1 }, { 2022, 1, 2 }, { 2022, 1, 3 } };

	//list
	list<int> lt1{ 1, 2, 3 };

	//set
	set<int> s1{ 3, 4, 5, 6, 3 };

	//map
	// 这里{"sort", "排序"}会先初始化构造一个pair对象
	map<string, string> dict = { {"string", "字符串" }, {"sort", "排序" } };
	return 0;

}
  • C++98并不支持直接用列表对容器进行初始化,这种初始化方式是在C++11引入initializer_list后才支持的。
  • 而这些容器之所以支持使用列表进行初始化,根本原因是因为C++11给这些容器都增加了一个构造函数这个构造函数就是以initializer_list作为参数的

cplusplus官网搜vector::vector

 这个新增的构造函数要做的就是遍历initializer_list中的元素,然后将这些元素依次插入到要初始化的容器当中。

我们前面模拟实现过vector迭代器,如果也想让他能够使用列表初始化,我们可以用initializer_list模拟一下:

namespace bit
{
	template<class T>
	class vector
	{
	public:
		typedef T* iterator;
		vector(initializer_list<T> il)
		{
			_start = new T[il.size()];
			_finish = _start;
			_endofstorage = _start + il.size();
			//迭代器遍历
			//typename initializer_list<T>::iterator it = il.begin();
			//while (it != il.end())
			//{
			//	push_back(*it);
			//	it++;
			//}
			//范围for遍历
			for (auto e : il)
			{
				push_back(e);
			}
		}
		vector<T>& operator=(initializer_list<T> il)
		{
			vector<T> tmp(il);
			std::swap(_start, tmp._start);
			std::swap(_finish, tmp._finish);
			std::swap(_endofstorage, tmp._endofstorage);
			return *this;
		}
	private:
		iterator _start;
		iterator _finish;
		iterator _endofstorage;
	};
}

这个模拟实现权当了解一下就可以了。

4.声明 

4.1auto

auto

在C++98中auto是一个存储类型的说明符,表明变量是局部自动存储类型,但是局部域中定义局部的变量默认就是自动存储类型,所以auto就没什么价值了。C++11中废弃auto原来的用法,将其用于实现自动类型推断。这样要求必须进行显示初始化,让编译器将定义对象的类型设置为初始化值的类型。(原来我们在写范围for时就经常用到它)。

int main()
{
	int i = 10;
	auto p = &i;
	auto pf = strcpy;
	//输出p、pf的类型
	cout << typeid(p).name() << endl;
	cout << typeid(pf).name() << endl;

	map<string, string> dict = { {"sort", "排序"}, {"insert", "插入"} };
	//map<string, string>::iterator it = dict.begin();

	auto it = dict.begin();
	//迭代器
	return 0;
}

但是虽然简短代码但是他关键字decltype可以将变量的类型声明为表达式指定的类型。也有一个致命缺陷就是不能一眼看出类型了,如果原先不知道it的类型是迭代器,那基本不能一眼识别出it的类型。

4.2decltype 

关键字decltype可以将变量的类型声明为表达式指定的类型

  • typeid().name;decltype;auto三者的区别:
  1. typeid拿到的只是类型的字符串不能再用它去定义一个变量
  2. decltype:将变量的类型声明为表达式指定的类型,可以用其去定义变量。
  3. auto必须要求显式初始化,而decltype没要求
int main()
{
	int x = 10;
	double y = 1.2;
	cout<<"typeid(x).name():"<<typeid(x).name() << endl;
	//typeid(x).name() n= 20;  不能这样用

	decltype(x) z = 2.4;   // int 2
	auto m=5.5;            // double 5.5

	cout << "decltype:" << z << endl;
	cout << "auto:"<<m << endl;
}

decltype:的应用场景:

// decltype的一些使用使用场景
template<class T1, class T2>
void F(T1 t1, T2 t2)
{
	decltype(t1 * t2) ret = t1 * t2;  //这个的意思是ret的类型是t1*t2
	//decltype(t1 * t2) ret ;
	vector<decltype(t1* t2)> v;
	v.push_back(ret);
	cout <<"typeid(ret).name()" <<typeid(ret).name() << endl;
}

int main()
{
	const int x = 1;
	double y = 2.2;

	decltype(x * y) ret;
	decltype(&x) p;
	cout << typeid(ret).name() << endl; //double
	cout << typeid(p).name() << endl;   //int const *

	F(1, 'a'); //int
	F(1, 2.2); //double

	return 0;
}

 4.3nullptr

由于C++中NULL被定义成字面量0,这样就可能回带来一些问题,因为0既能指针常量,又能表示
整形常量。所以出于清晰和安全的角度考虑,C++11中新增了nullptr,用于表示空指针

#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endi

其实NULL应该定义成第三个NULL ((void*)0)但是以前的版本都是定义成NULL 0存在安全隐患所以专门补了一个nullptr。

就比如下面这个例子:

void f(int arg)
{
	cout << "void f(int arg)" << endl;
}
void f(int* arg)
{
	cout << "void f(int* arg)" << endl;
}
int main()
{   
	f(0);       //void f(int arg)
	f(NULL);    //void f(int arg)
	f(nullptr); //void f(int* arg)
	return 0;
}

  •  NULL和nullptr的含义都是空指针,所以这里调用函数时肯定希望匹配到的都是参数类型为int*的重载函数,但最终却因为NULL本质是字面量0,而导致NULL匹配到了参数为int类型的重载函数。
  • 在C++11中,sizeof(nullptr) 与 sizeof((void*)0)所占的字节数相同。
  • 为了提高代码的健壮性,在后续表示指针空值时建议最好使用nullptr。

4.4范围for

范围for我们以前就应用过很多了,接下来我们就再简单介绍一下:

C++11提供了一个特殊版本的 for 循环,在很多情况下,它都可以简化数组的处理,这就是基于范围的 for 循环。在使用基于范围的 for 循环处理数组时,该循环可以自动为数组中的每个元素迭代一次,这就是范围for

int main()
{  //遍历数组的for循环
	int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8,  };
	for (int i = 0; i < 10; i++)
		cout << arr[i] << " ";   //1 2 3 4 5 6 7 8 0 0
	cout << endl;

	//而遍历容器类的For
	std::vector<int> vec{ 1,2,3,4,5,6,7,8, };
	for (std::vector<int>::iterator itr = vec.begin(); itr != vec.end(); itr++)
		std::cout << *itr<<" ";  //1 2 3 4 5 6 7 8
	std::cout << endl; 
}

 不管上面哪一种方法,都必须明确的确定for循环开头以及结尾条件,而熟悉C#或者python的人都知道在C#和python中存在一种for的使用方法不需要明确给出容器的开始和结束条件,就可以遍历整个容器,幸运的是C++11中引入了这种方法也就是基于范围的For(Range-Based-For),用基于范围的For 改写上面两个例子:

std::vector<int> vec{ 1,2,3,4,5,6,7,8,9};
	for (auto n : vec)
		std::cout << n<<" ";  //1 2 3 4 5 6 7 8 9
	std::cout << endl;

	int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8}; 1 2 3 4 5 6 7 8 0 0
	for (auto n : arr)
		std::cout << n << " ";
	std::cout << endl;

可以看到改写后的使用方法简单了很多,代码的可读性提升了一个档次,但是需要注意的在上述对容器的遍历是只读的,也就是说遍历的值是不可修改的。 

std::vector<int> vec{ 1,2,3,4,5,6,7,8,9,10 };
	cout << "修改前" << endl;
	for (auto n : vec)
		std::cout << n++<<" ";  //1 2 3 4 5 6 7 8 9 10
	cout << endl;

	cout << "修改后" << endl;
	for (auto j : vec)
		std::cout << j<<" ";  //1 2 3 4 5 6 7 8 9 10
	std::cout << endl;

我们遍历后对容器内元素的加1操作并没有生效,修改后的输出仍然和原来的元素值一样,因为这种遍历方法做的是值拷贝。那么如果要遍历容器内的元素的同时又要修改元素的值该怎么做呢,方法就是将遍历的变量声明为引用类型,看下面例子:

std::vector<int> vec{ 1,2,3,4,5,6,7,8,9,10 };
	cout << "修改前" << endl;
	for (auto& n : vec)  //引用类型
		std::cout << n++<<" ";   //1,2,3,4,5,6,7,8,9,10
	cout << endl;

	cout << "修改后" << endl;
	for (auto j : vec)
		std::cout << j<<" ";  //2,3,4,5,6,7,8,9,10,11
	cout << endl;

可以看到,容器内的元素每个都加了1,两者的区别仅为在修改的时候,将声明的遍历遍历n从auto 声明为auto &,使用基于范围的For循环一定要注意这一点, 基于范围for循环的遍历是只读的遍历,除非将变量变量的类型声明为引用类型。

 4.5 STL中的一些改变

C++11中新增了四个容器,分别是arrayforward_listunordered_mapunordered_set

 

 array

  • array容器本质就是一个静态数组,即固定大小的数组。
  • array容器有两个模板参数,第一个模板参数代表的是存储的类型,第二个模板参数是一个非类型模板参数,代表的是数组中可存储元素的个数。
int main()
{
	std::array<int, 10> a1;   //定义一个可存储10个int类型元素的array容器
	std::array<double, 5> a2; //定义一个可存储5个double类型元素的array容器
	return 0;
}

关于awary的补充:

  1. C语言数组越界检查中,越界读基本检查不出来越界写是抽查,不一定能检查出来。
  2. awary容器越界读写都能被检查出来。
  3. 但是在实际情况中awary用的很少,一方面是数组用惯了,很难改变,另一方面是用awary不如用vector+resize来替代C语言中的数组。
  4. array容器的对象是建立在栈区的,不适合定义大数组

 forward_list容器

这个容器本质就是一个单链表,很鸡肋,一般情况下我们都用list,这里就不在细讲了。

 后面的关于map和set的在前面已经说过了就不讲了。

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

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

相关文章

上线3天,下载4万,ChatGPT中文版VSCode插件来了

ChatGPT 这几天可谓是风头无两。作为一个问答语言模型&#xff0c;它最大的优点就是可以回答与编程相关的问题&#xff0c;甚至回复一段代码。 尽管有人指出 ChatGPT 生成的代码有错误&#xff0c;但程序员们还是对它写代码、找 bug 的功能很感兴趣&#xff0c;有人还给 VScode…

一千字认识NodeJS

在学习Node.js时&#xff0c;发现一处课程通过介绍网站开发来引入Node.js的思路很有意思&#xff0c;在此将整个过程以自己的理解记录了下来&#xff0c;供大家学习交流&#x1f600;&#x1f600;&#x1f600; 前端和后端 众所周知&#xff0c;前端程序yuan通常写的是浏览器…

数据库--------用户的授权

目录 授权例1例2例3例4例5收回例1例2例3授权 GRANT语句格式: GRANT <权限> [,<权限>]

SpringBoot打的jar包瘦身

文章目录正常打包瘦身方法一&#xff1a;Dloader.path指定依赖包位置瘦身方法二&#xff1a;配置文件里指定依赖包位置正常打包 <build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plu…

Talk预告 | 香港理工大学计算机系助理教授杨波:无监督2D/3D物体实例分割

本期为TechBeat人工智能社区第462期线上Talk&#xff01; 北京时间12月14日(周三)20:00&#xff0c;香港理工大学计算机系助理教授——杨波的Talk将准时在TechBeat人工智能社区开播&#xff01; 他与大家分享的主题是: “无监督2D/3D物体实例分割”&#xff0c;届时将介绍香港理…

PyTorch深度学习框架学习记录(1)--安装,MNIST手写数字识别

文章目录框架发展安装cpu版本GPU版本学习记录MNIST手写数字识别数据的准备工作框架发展 Caffe框架&#xff08;2015&#xff09; 优势&#xff1a;只写配置文件 劣势&#xff1a;运行环境配置繁琐 Tensorflow 1.x&#xff08;2016&#xff09; 开发成本高&#xff0c;上手较…

正则表达式教程

视频&#xff1a;null正则表达式进阶是正则表达式30分钟入门40分钟进阶&#xff08;绝对能学会&#xff09;的第2集视频&#xff0c;该合集共计2集&#xff0c;视频收藏或关注UP主&#xff0c;及时了解更多相关视频内容。https://www.bilibili.com/video/BV19t4y1y7qP/?p2&…

如何用3D流体实现逼真水流效果?

华为应用市场在2022年HDC大会期间发布了一款3D水流主题&#xff0c;基于华为HMS Core Scene Kit服务能力&#xff0c;展现立体灵动的水流岛屿&#xff0c;可跟随用户指尖实现实时流体波动效果&#xff0c;既趣味又解压。 让变幻莫测的物质来实现我们在影视和游戏等多种应用场景…

sentinel 规则持久化

原始模式 如果不做任何修改&#xff0c;Dashboard 的推送规则方式是通过 API 将规则推送至客户端并直接更新到内存中&#xff1a; 这种做法的好处是简单&#xff0c;无依赖&#xff1b;坏处是应用重启规则就会消失&#xff0c;仅用于简单测试&#xff0c;不能用于生产环境。 se…

【Android】数据存储

一、文件存储 特点&#xff1a;openFileInput()和openFileOutput()读取设备上的文件。 优点&#xff1a;适用于存储大量的数据&#xff0c;可以存储图片、视频、文本等数据。 缺点&#xff1a;如果采用内部存储的方式&#xff0c;存储过量的数据可能会导致内存的不足&#xff…

2022年度手机行业排行榜(年度手机行业分析)

如今&#xff0c;随着手机普及率的不断增长&#xff0c;当前手机市场在逐渐饱和。在这一的态势下&#xff0c;手机行业中细分市场成为发展的必然趋势&#xff0c;随着市场细分和目标人群锁定的不断明确&#xff0c;手机市场中中小品牌手机的生存空间在逐渐被挤压&#xff0c;手…

消息队列,Unix的通信机制之一

最简单的消息内存的使用流程 ①ftok函数生成键值 ②msgget函数创建消息队列 ③msgsnd函数往消息队列发送消息 ④msgrcv函数从消息队列读取消息 ⑤msgctl函数进行删除消息队列 一个消息数据应该由以下一个结构体组成&#xff0c;举个例子 struct mymesg{long int mtype; /…

教你如何使用eBPF追踪Linux内核

【推荐阅读】 浅析linux内核网络协议栈--linux bridge 深入理解SR-IOV和IO虚拟化 深入linux内核架构--进程&线程 1. 还是先进入内核目录&#xff0c;执行下面的命令&#xff0c;确保内核代码是干净的。 $ make mrproper 2. 执行以下命令&#xff0c;开始对内核进行配…

Feign的性能优化

Feign底层的客户端实现有三种模式 1&#xff09;URLConnection&#xff1a;默认实现&#xff0c;不支持连接池&#xff1b;&#xff08;Feign发送http请求时&#xff0c;默认使用的客户端&#xff09; 2&#xff09;Apache HttpClient &#xff1a;支持连接池&#xff1b; 3&…

深度学习Week10-YOLOv5-Backbone模块实现(Pytorch)

● &#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客 ● &#x1f366; 参考文章&#xff1a;Pytorch实战 |第P9周&#xff1a;YOLOv5-Backbone模块实现(训练营内部成员可读) ● &#x1f356; 原作者&#xff1a;K同学啊|接辅导、项目定制 类似于上周内…

也谈特征值和特征向量的几何意义

在当前的大数据分析时代&#xff0c;数据降维是一个重要的分析技术。而谈到数据降维&#xff0c;就离不开一门最为抽象难懂的数学学科分支——线性代数。有人可能会问&#xff1a;一堆向量和矩阵符号的线性代数到底有鸟用&#xff1f;简单地不科学地说&#xff0c;线性代数就是…

【tiktok小店运营小知识】 tiktok小店也会被封吗?有哪些原因呢?

最近开tiktok小店的小伙伴越来越多&#xff0c;运营过程中也会碰到很多问题。有小伙伴问&#xff0c; tiktok小店也会被封吗&#xff1f;有哪些原因呢&#xff1f; tiktok小店也会被封吗&#xff1f;有哪些原因呢&#xff1f; 答案是肯定的。TikTok需要进一步规范店铺经营&…

SRM是什么意思?盘点4个顶级SRM系统

SRM是什么意思&#xff1f;SRM系统&#xff0c;一般指供应商关系管理系统。供应商管理系统是采购管理系统的一个重要模块&#xff0c;强调企业与供应商之间协作共赢。相信在市场动荡的今天&#xff0c;企业与供应商之间的强关联、共命运对于企业来说不失为稳固根基、扩张业务的…

JavaScript系列之ES6默认导出与默认导入

文章の目录一、默认导出二、默认导入三、按需导出四、按需导入五、直接导入并执行模块代码写在最后一、默认导出 语法&#xff1a; export default 默认导出的成员每个模块中&#xff0c;只允许使用唯一的一次 export default&#xff0c;否则会报错&#xff01; 二、默认导入…

Generative Modeling by Estimating Gradients of the Data Distribution阅读笔记

目录概述传统score-based generative modeling介绍score matchingLangevin dynamics传统score-based generative modeling存在的问题流型假设上的问题低密度区域的问题Noise Conditional Score Network噪声条件分数网络(Noise Conditional Score Networks)annealed Langevin dy…