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

C++11新特性解析与应用(1)

目录

一、C++11简介

二、统一的列表初始化

2.1{}初始化

2.2initializer_list

三、decltype

四、nullptr

五、array

六、左值和右值

七、左值引用和右值引用

八、移动拷贝


一、C++11简介

相比于C++98/03,C++11则带来了数量可观的变化,其中包含了约140个新特性,以及对C++03标准中约600个缺陷的修正,这使得C++11更像是从C++98/03中孕育出的一种新语言。相比较而言,C++11能更好地用于系统开发和库开发、语法更加泛华和简单化、更加稳定和安全,不仅功能更强大,而且能提升程序员的开发效率,公司实际项目开发中也用得比较多,所以我们要作为一个重点去学习

由于c++11的新特性比较多,所以小编部分讲解一下c++11标准中比较常用的一些语法


二、统一的列表初始化

2.1{}初始化
  • C++98

98允许使用花括号{}对数组或者结构体元素进行统一的列表初始值设定

struct Point
{int _x;int _y;
};
int main()
{int arr1[] = { 1, 2, 3, 4, 5 };int arr2[5] = { 0 };Point p = { 1, 2 };return 0;
}
  • C++11

C++11想统⼀数据的初始化⽅式,想实现⼀切对象皆可⽤{}初始化 ,所以ta扩大了用大括号{}括起的列表(也叫做列表初始化)的使用范围,使其可用于所有的内置类型和用户自定义的类型(本质是类型的转换,中间会产⽣临时对象,最后编译器会优化,变成直接构造),使用初始化列表时,可添加等号(=),也可不添加(小编建议添加)

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(){// C++11支持的列表初始化,这里会调用构造函数初始化Date d1(2025, 10, 1); // 调用d1的构造函数Date d2{ 2025, 10, 2 };//调用d2的构造函数Date d3 = { 2025, 10, 3 };//调用d3的构造函数//用引⽤,引用的是{ 2025, 10, 1 }构造的临时对象,所以必须加constconst Date& d4={2025,10 1};vector<Date> ve;ve.push_back({2025,10,1});return 0;
}
	Date d1 = { 2025, 10, 11 };vector<int> ve = { 1,2,3,4,5 };

这两种初始化的原理是不一样的,d1是调用了拷贝构造 ,而ve则是用了initializer_list对vector的初始化

2.2initializer_list
  • initializer_list是模板类,用于访问{}列表中的值,该{}列表是由类型为const T的元素组成的列表
int main()
{auto arr = { 1, 2, 3, 4, 5 };cout << typeid(arr).name() << endl;cout << sizeof(arr) << endl;return 0;
}

  • initializer_list的底层
template<class T>
class initializer_list
{private:const T* _start;const T* _finish;
};
  • 在c++11中使用花括号括起来的列表的类型会被编译器识别为initializer_list类型
	Date d1 = { 2025, 10, 11 };vector<int> ve = { 1,2,3,4,5 };

在这里

  1. nitializer_list的私有成员变量只有两个const T*的指针,分别指向{}花括号括起来的列表的开头和结尾的下一个位置
  2. 编译器会将花括号括起来的列表的类型进行特殊处理为initializer_list
  3. 分别将initializer_list中两个指针分别指向{}列表的开头的地址和结尾的下一个位置的地址
  4. 这样支持了迭代器的begin和end,并且两个指针相减 ,就是size() :元素的个数

然后initializer_list再去初始化ve ,vector中构造函数:

template<class T>
vector
{
public:vector(initializer_list<T> lt){reserve(lt.size());//initializer_list支持了size(),可以获取initializer_list的元素个数for(auto e : lt)//initializer_list支持迭代器,所以可以使用范围for{push_back(e);//调用尾插将列表中的元素逐个插入即可}}
};

因为vector中,或者说不少容器中都增加了用initializer_list的构造函数,如 initializer_list构造map对象:

int main()
{//map<string, string> dict = { "sort","排序","left","左边" };//{{"sort","排序"},{"left","左边"}}被识别成了initializer_list<pair>map<string, string> dict = { {"sort","排序"},{"left","左边"} };//这里调了pair的拷贝构造return 0;
}

三、decltype

int main()
{int i = 10;auto p = &i;//auto自动推导类型,但不能定义变量//auto x;cout << typeid(p).name() <<endl;//获取变量的类型cout << typeid(i).name() << endl;//只能看,不能用///typeid(i).name() ai;decltype(i) p1;//可以自己推导类型,并且不初始化 ,//这不和auto p2 = i;一个意思吗, 不是的 ,p1想当与 int p1 ,p1没有值,auto p2 = i ,p2是int p2=10 ;//decltype(i) p1;可以单纯定义一个变量//auto只能用来自动推导类型定义变量//typeid(i).name() 只能用来推导类型 ,不能定义变量//decltype(i)既可以自动推导类型定义变量,还可以只用来推导类型 或作模板实参: vector<decltype(i)> ve;//还可以推导一个表达式 : vector<decltype(x*y)> ve;return 0;
}
  • autoauto只能用来自动推导类型定义变量,但不能定义变量
  • typeid(i).name() 只能用来推导类型 ,不能定义变量如: typeid(i).name() ai;
  • decltype(i) p1;可以单纯定义一个变量 ,而不初始化如:decltype(i) p1 ,既可以自动推导类型定义变量,还可以只用来推导类型 或作模板实参: vector<decltype(i)> ve; 还可以推导一个表达式 : vector<decltype(x*y)> ve;

四、nullptr

//nullptr
int main()
{//由于NULL不够”专一“ ,ta既可以表示空指针 又可以表示0//而这里的nullptr只用来表示空指针int* p = NULL;//被替换成了 int* p=0,0可以用来赋值指针 ,但0依旧是整型return 0;
}
  • 由于NULL不够”专一“ ,ta既可以表示空指针 又可以表示0 ,当NULL用作空指针时,直接就替换成了0
  • nullptr只用来表示空指针

五、array

//array (ps: 鸡肋)
int main()
{//array为了弥补c语言的数组中对越界的不检查, array[15]在这里调用operator[] 中会强制检查array<int, 10> a1;int a2[10];a1[15] = 10;a2[15] = 10;//array[15]在这里调用operator[] 中会强制检查数字小于size()return 0;
}
  • 比上不足,比下有余

六、左值和右值

int main()
{//左值和右值的最大区别 :左值可以取地址,右值不能被取地址//左值一个表示数据的表达式和变量名或解引用的指针//右值也是一个表示数据的表达式,如字面常量,表达式返回值 ,函数返回值//右值可以出现在赋值符号的右边,但是不能出现在赋值符号的左边,左值可以出现在赋值符号的右边,也可以出现在左边常见左值int* p = new int(0);int b = 1;const int c = 2;double x = 1.1, y = 2.2;常见右值10;字面常量x + y;表达式返回值func(x, y);函数返回值"xxxxx"是左值,表达式本身是首元素地址return 0;
}
  • 左值和右值的最大区别 :左值可以取地址,右值不能被取地址
  • 左值一个表示数据的表达式和变量名或解引用的指针
  • 右值也是一个表示数据的表达式,如字面常量(10),表达式返回值 (x+y),函数返回值func()
  • 右值可以出现在赋值符号的右边,但是不能出现在赋值符号的左边,左值可以出现在赋值符号的右边,也可以出现在左边

七、左值引用和右值引用

//左值引用和右值引用
int main()
{//左值引用 : 给左值取别名//右值引用 :给右值取别名//左值引用int a = 0;int& r1 = a;//右值引用int&& r5 = 10;double x = 1.1, y = 2.2;double&& r6 = x + y;//x+y的临时变量是右值//左值引用不能直接给右值取别名 : int& z=10;(X) //要加const修饰 : const int& z=10 ,这里也可以看到实参传给函数的形参时,函数的形参要加const的修饰,这样既可以右值传过来,也可以左值传过来//右值引用不能直接给左值取别名 ,右值引用可以引用move以后的左值:int&& r7=move(a)//a(0)// move : 把左值变成右值return 0;
}
  • 左值引用 : 给左值取别名 ,右值引用 :给右值取别名
  • 左值引用不能直接给右值取别名 : int& z=10;(X)   ,要加const修饰 : const int& z=10 ,(这里也可以看到实参传给函数的形参时const的作用,函数的形参要加const的修饰,这样既可以右值传过来,也可以左值传过来)
  • 右值引用不能直接给左值取别名 ,右值引用可以引用move以后的左值:int&& r7=move(a)//a(0)
  • move : 把左值变成右值

左值引用的使用场景和价值是什么?

  • 使用场景:
    1:做参数
    2: 做返回值
  • 价值:减少拷贝
  • 左值引用的缺陷 : 局部变量不能用引用返回,局部变量的生命周期结束了

为了解决左值引用的缺陷,有了移动拷贝(将左值变成右值)

八、移动拷贝

我们来看一段代码: 

string& func()
{string str;cin >> str;//...return str;//此时str是临时变量,函数栈帧销毁,str变成野指针所以不能用string& 作返回值 ,要不然就给野指针取别名了//那返回值只能是string 要创建一个临时变量拷贝str返回
}
int main()
{string ret = func();//编译器在这里会优化,会在函数栈帧销毁前str变成野指针前,会直接用str去拷贝retreturn 0;
}
  • str是临时变量,不能够传引用返回  ,因为函数栈帧销毁,str变成野指针所以不能用string& 作返回值 ,要不然就给野指针取别名了
  • 那返回值只能是string 要创建一个临时变量拷贝str返回, 此处是深拷贝 ,在这里就会产生空间的消耗
  • 好在编译器看不下这种拷贝又拷贝的情况 ,此处编译器会优化

在有了左值和右值的知识基础上,我们来看左值和右值的函数匹配: 

void func(int& r)
{cout << "func(int& r)" << endl;
}
void func(int&& r)
{cout << "func(int&& r)" << endl;
}
int main()
{ int a = 0;int b = 1;func(a);//func(a + b);//会走更匹配的func(int&& r)return 0;
}
  • 此处a+b是右值,它会走更匹配的右值参数的函数func
string func1()
{string str("xxxxxxxxxx");//...return str;
}
int main()
{string ret = func1();// ,//移动拷贝:直接将str与ret发生swap(),不仅得到str的值,而且还把ret的值了str(str反正要销毁)/*移动拷贝的实现:string中重载operator=(string&& s)string& operator=(string&& s){swap(s);return *this;}*///以前的operator=就像是只写了void func(int& r),无论是左值还是右值,都调用这个函数,//而现在如果是右值就会调用更适合的void func(int&& r)//回到string str ,str是函数的返回值是右值,自定义类型的右值就是将亡值, 那么有了operator=(string&& s)之后//会去调用operator=(string&& s),就直接将str与ret发生swap(),不仅得到str的值,而且还把ret的值了str(str反正要销毁)//这个过程叫移动拷贝//移动拷贝降低了原来的拷贝的次数return 0;
}
  • 内置类型的右值  : 纯右值 ,自定义类型的右值: 将亡值
  • 当func1返回的是左值时是深拷贝,到ret要三次深拷贝

  • 而func1返回的是右值时是移动拷贝 ,移动拷贝:会直接将str与ret发生swap(),不仅得到str的值,而且还把ret的值了str(str反正要销毁),这样只用拷贝一次

移动拷贝的实现:在string类中重载了operator=(string&& s)   (原先的operator=是针对左值,这个是针对右值)

在string中重载operator=(string&& s)
string& operator=(string&& s)
{swap(s);return *this;
}

以前的operator=就像是只写了void func(int& r),无论是左值还是右值,都调用这个函数,而现在如果是右值就会调用更适合的void func(int&& r) ,回到string str ,str是函数的返回值是右值,自定义类型的右值就是将亡值, 那么有了operator=(string&& s)之后 ,会去调用operator=(string&& s),就直接将str与ret发生swap(),不仅得到str的值,而且还把ret的值了str(str反正要销毁) ,这个过程叫移动拷贝 ,移动拷贝降低了原来的拷贝的次数

那么,函数的返回值能是string&& 吗,str不是被识别成了右值吗

string&& func1()
{string str("xxxxxxxxxx");//...return str;//识别str是右值
}
int main()
{string ret = func1();return 0;
}
  • 答案是不行
  1. func1 返回值不能是string&& , 如果是,那么编译器就会去取别名,str是临时变量,函数栈帧销毁,str变成野指针所以不能用string&& 作返回值 ,要不然就给野指针取别名了
  2. 编译器看返回值是string,要传值拷贝才回去优化,编译器才会把str强行识别成右值,并且直接把str识别成右值 ,如果不优化,则会把返回值产生的临时变量识别成右值

总体来说:

1.编译器优化掉临时变量,直接把str当作返回值
2.把str当作返回值以后 ,有了移动拷贝,会把str强行看作右值
所以如果一开始传的string&&  ,则就没有编译器的优化,不会把str当作返回值,更没有移动拷贝
为什么是强行 : move的作用是将左值转换成右值 ,这里可没写move哦  ,但都被强行识别成了右值 ,免得去原来写好的代码中加上move,那样太麻烦了

http://www.dtcms.com/a/435085.html

相关文章:

  • 【LangChain】P7 对话记忆完全指南:从原理到实战(下)
  • 上海建设房屋网站下载好了网站模板怎么开始做网站
  • 远程智能康养实训室:训练学生驾驭物联网,服务未来居家康养新时代
  • ⚡ WSL2 搭建 s5p6818 Linux 嵌入式开发平台(part 1):环境准备与架构设计
  • 学科建设网站wordpress 主体安装
  • 如何免费建立自己的网站中国建设摩托车
  • 主机服务器网站 怎么做孝义网站建设
  • 快速搭建企业网站阿里虚拟机建设网站
  • 山西建设机械网站首页备案添加网站
  • 店面建设网站的必要性58同城装修设计师
  • C语言笔记
  • 23ICPC合肥站补题
  • LR算法中反向最右推导(Reverse RightMost Derivation)
  • 企业网站托管服务常用指南wordpress ssl证书
  • 专注于响应式网站开发高端定制网站建设高端旅游定制
  • django网站开发教程杭州最便宜的网站建设
  • rpm包的安装方法
  • 内网环境下离线安装软件的完美解决方案(以MySQL为例)
  • 构造函数和初始化列表的关系
  • 济南网站优化建设局网站打不开
  • LabVIEW 系统稳定性计算
  • Rocky Linux 8 安装与配置 TigerVNC 服务完整操作文档
  • Testify Go测试工具包入门教程
  • 南阳网站建设xihewh成都网站建设公司有哪几家
  • **标题:发散创新:探索AR开发框架的核心技术**随着增强现实(AR)技术的飞速发展,AR开发框架成为了开发者们关注的焦
  • 网站推广的优势logo制作免费版
  • 汕头网站建设制作报价网片是干什么用的
  • 江西省住房和城乡建设厅的网站网站设计权限
  • 【人工智能通识专栏】第三十三讲:知识库的构建与应用
  • 、@RequestParam 取出文件项