C++11的一些新特性|右值引用|STL中的一些变化_stl中有移动赋值-程序员宅基地

技术标签: visual studio code  C++  c++  后端  visual studio  

文章目录

1、{}初始化

2、声明

2.1auto

2.2、decltype

2.3、nullptr

2.4.范围for循环

3、STL中的一些新变化

3.1.新容器

3.2容器中的一些新方法

4.右值引用和移动语义

左值引用和右值引用

左值引用的短板:

右值引用使用场景和意义:

move的作用:

完美转发

5.新的类功能

移动构造和移动赋值

类成员变量初始化

强制生成默认函数关键字default:

禁止生成默认函数的关键字delete



1、{}初始化

在c++98中,标准允许使用{}对数组或者结构体进行初始化,如下:

struct Point
{
 int _x;
 int _y;
};

int array1[] = { 1, 2, 3, 4, 5 };
int array2[5] = { 0 };
Point p = { 1, 2 };

c++11扩大了{}的使用范围,使其可用于对所有内置类型和用户自定义类型,使用初始化列表时,可添加等号,也可不添加

struct Point
{
 int _x;
 int _y;
};
int main()
{
 int x1 = 1;
 int x2{ 2 };
 int array1[]{ 1, 2, 3, 4, 5 };
 int array2[5]{ 0 };
 Point p{ 1, 2 };
 // C++11中列表初始化也可以适用于new表达式中
 int* pa = new int[4]{ 0 };
 return 0;
}

//创建对象时,也可以使用列表初始化方式调用构造函数初始化
int main()
{
 Date d1(2022, 1, 1); // old style
 // C++11支持的列表初始化,这里会调用构造函数初始化
 Date d2{ 2022, 1, 2 };
 Date d3 = { 2022, 1, 3 };
 return 0;
}

2、声明

2.1auto

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

int i = 10;
auto p = &i;

2.2、decltype

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

const int x = 1;
double y = 2.2;
decltype(x * y) ret; // ret的类型是double
decltype(&x) p;      // p的类型是int*

2.3、nullptr

c++中Null被定义为0,这样可能会带来一些问题,因为0既能指针常量,又能表示整形常量。c++中nullptr用来表示空指针。

2.4.范围for循环

之前已经用过很多次,这里不再详细阐述。

3、STL中的一些新变化

3.1.新容器

3.2容器中的一些新方法

       比如提供了cbegin和cend方法返回const迭代器等。但是实际意义不大,因为begin和end也是可以返回const迭代器的。实际上c++11更新后,容器中新增的新方法最后用的插入接口的右值引用版本。接下来先介绍右值引用。

4.右值引用和移动语义

左值引用和右值引用

传统的c++语法中就有引用的语法,而c++11中新增了右值引用的语法特性,无论是左值引用还是右值引用,都是给对象取别名。

左值:左值是一个数据的表达(如变量名或者解引用的指针),我们可以获取它的地址,也可以对他进行赋值。左值可以出现在赋值符号的左边右值不能出现在赋值符号左边。定义时const修饰后的左值,不能给他赋值,但是可以获取他的地址。左值引用就是给左值的引用,给左值取别名。

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;

右值:右值也是一个数据表达式,如:字面变量表达式返回值函数返回值(临时变量)(这个不能是左值引用返回)等。右值可以出现在赋值符号的右边,但是不能出现在赋值符号的左边,右值不能取地址。右值引用就是对右值的引用,给右值取别名。

需要注意的是,右值是不能取地址。但给右值取别名后,会导致右值被存储到特定位置,且可以取到该位置地址。例如:不能取字面量10的地址,但是rr1引用后,可以对rr1取地址,也可以修改rr1,如果不想rr1被修改,可以用const int && rr1取引用。

double x = 1.1 , y = 2.2;

//常见的右值
10
x+y;
fmin(x,y)


//以下几个都是对右值的右值引用
int&& rr1 = 10;
double&& rr2 = x+y;
double & rr3 = fmin(x+y);



//这里编译会报错,error c2106 "=":左操作数必须为左值
10 = 1;
x+y = 1;
fmin(x,y) = 1;

左值引用和右值引用比较:

1.左值引用只能引用左值,不能引用右值

2.const左值引用既可以引用左值,也可以引用右值

右值引用总结:

1.右值引用只能引用右值,不能引用左值

2.但是右值引用可以引用move以后的左值

int && rr1 = 10;

 // error C2440: “初始化”: 无法从“int”转换为“int &&”
 // message : 无法将左值绑定到右值引用
 int a = 10;
 int&& r2 = a;

//右值引用可以引用move以后的左值
int &&rr3 = std::move(a);

左值引用的短板:

当函数返回一个局部变量,出了作用域就不存在了,就不能使用左值引用返回,只能传值返回,传值返回至少会导致一次拷贝构造(旧一些的编译器可能会两次拷贝构造)

右值引用使用场景和意义:

1.作为返回值和参数都可以提高效率。在类中增加移动构造,移动构造本质上是将参数右值的资源窃取,占为己有,那么就不用做深拷贝,所以叫它移动构造,就是窃取别人的资源来构造自己

// 移动构造
string(string&& s)
 :_str(nullptr)
 ,_size(0)
 ,_capacity(0)
{
 cout << "string(string&& s) -- 移动语义" << endl;
 swap(s);
}
int main()
{
 bit::string ret2 = bit::to_string(-1234);
 return 0;
}

2.移动赋值  类中增加移动赋值函数

// 移动赋值
string& operator=(string&& s)
{
cout << "string& operator=(string&& s) -- 移动语义" << endl;
swap(s);
return *this;
}
int main()
{
 bit::string ret1;
 ret1 = bit::to_string(1234);
 return 0;
}
// 运行结果:
// string(string&& s) -- 移动语义
// string& operator=(string&& s) -- 移动语义

move的作用:

有些场景下,可能真的需要用右值去引用左值实现移动语义。当需要用右值引用引用一个左值时,可以通过move函数将左值转化为右值C++11中,std::move()函数位于 头文件中,该函数名字具有迷惑性,并不搬移任何东西,唯一的功能就是将一个左值强制转化为右值引用,然后实现移动语义。

int main()
{
    string s1("hello world"); // 这里s1是左值,调用的是拷贝构造
    string s2(s1);
 // 这里我们把s1 move处理以后, 会被当成右值,调用移动构造
 // 但是这里要注意,一般是不要这样用的,因为我们会发现s1的 资源被转移给了s3,s1被置空了。
 string s3(std::move(s1));
 return 0;
}

完美转发

模板中的&&万能引用

void Fun(int &x){ cout << "左值引用" << endl; }
void Fun(const int &x){ cout << "const 左值引用" << endl; }
比特就业课
std::forward 完美转发在传参的过程中保留对象原生类型属性
void Fun(int &&x){ cout << "右值引用" << endl; }
void Fun(const int &&x){ cout << "const 右值引用" << endl; }
// 模板中的&&不代表右值引用,而是万能引用,其既能接收左值又能接收右值。
// 模板的万能引用只是提供了能够接收同时接收左值引用和右值引用的能力,
// 但是引用类型的唯一作用就是限制了接收的类型,后续使用中都退化成了左值,
// 我们希望能够在传递过程中保持它的左值或者右值的属性, 就需要用我们下面学习的完美转发

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; }
// std::forward<T>(t)在传参的过程中保持了t的原生类型属性。
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;
}

5.新的类功能

移动构造和移动赋值

原来c++类中,有6个默认成员函数:构造,析构,拷贝构造,拷贝赋值,取地址重载,const取地址重载。c++11新增了两个:移动构造函数和移动赋值运算符重载。

针对移动构造和移动赋值运算符重载,有一些需要注意的点:

如果没有自己实现移动构造,且没有实现析构,拷贝构造,拷贝赋值重载中的任意一个,那么编译器会自己生成一个默认移动构造。默认生成的移动构造,对内置类型成员会按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动构造。如果实现了就调用移动构造,没有实现就调用拷贝构造。移动拷贝赋值和移动构造完全类似。如果自己提供了移动构造(移动赋值),编译器不会自动提供。

类成员变量初始化

c++11允许在类定义时给成员变量初始缺省值,默认生成构造函数会使用这些缺省值初始化

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

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/jolly0514/article/details/132815694

智能推荐

使用nginx解决浏览器跨域问题_nginx不停的xhr-程序员宅基地

文章浏览阅读1k次。通过使用ajax方法跨域请求是浏览器所不允许的,浏览器出于安全考虑是禁止的。警告信息如下:不过jQuery对跨域问题也有解决方案,使用jsonp的方式解决,方法如下:$.ajax({ async:false, url: 'http://www.mysite.com/demo.do', // 跨域URL ty..._nginx不停的xhr

在 Oracle 中配置 extproc 以访问 ST_Geometry-程序员宅基地

文章浏览阅读2k次。关于在 Oracle 中配置 extproc 以访问 ST_Geometry,也就是我们所说的 使用空间SQL 的方法,官方文档链接如下。http://desktop.arcgis.com/zh-cn/arcmap/latest/manage-data/gdbs-in-oracle/configure-oracle-extproc.htm其实简单总结一下,主要就分为以下几个步骤。..._extproc

Linux C++ gbk转为utf-8_linux c++ gbk->utf8-程序员宅基地

文章浏览阅读1.5w次。linux下没有上面的两个函数,需要使用函数 mbstowcs和wcstombsmbstowcs将多字节编码转换为宽字节编码wcstombs将宽字节编码转换为多字节编码这两个函数,转换过程中受到系统编码类型的影响,需要通过设置来设定转换前和转换后的编码类型。通过函数setlocale进行系统编码的设置。linux下输入命名locale -a查看系统支持的编码_linux c++ gbk->utf8

IMP-00009: 导出文件异常结束-程序员宅基地

文章浏览阅读750次。今天准备从生产库向测试库进行数据导入,结果在imp导入的时候遇到“ IMP-00009:导出文件异常结束” 错误,google一下,发现可能有如下原因导致imp的数据太大,没有写buffer和commit两个数据库字符集不同从低版本exp的dmp文件,向高版本imp导出的dmp文件出错传输dmp文件时,文件损坏解决办法:imp时指定..._imp-00009导出文件异常结束

python程序员需要深入掌握的技能_Python用数据说明程序员需要掌握的技能-程序员宅基地

文章浏览阅读143次。当下是一个大数据的时代,各个行业都离不开数据的支持。因此,网络爬虫就应运而生。网络爬虫当下最为火热的是Python,Python开发爬虫相对简单,而且功能库相当完善,力压众多开发语言。本次教程我们爬取前程无忧的招聘信息来分析Python程序员需要掌握那些编程技术。首先在谷歌浏览器打开前程无忧的首页,按F12打开浏览器的开发者工具。浏览器开发者工具是用于捕捉网站的请求信息,通过分析请求信息可以了解请..._初级python程序员能力要求

Spring @Service生成bean名称的规则(当类的名字是以两个或以上的大写字母开头的话,bean的名字会与类名保持一致)_@service beanname-程序员宅基地

文章浏览阅读7.6k次,点赞2次,收藏6次。@Service标注的bean,类名:ABDemoService查看源码后发现,原来是经过一个特殊处理:当类的名字是以两个或以上的大写字母开头的话,bean的名字会与类名保持一致public class AnnotationBeanNameGenerator implements BeanNameGenerator { private static final String C..._@service beanname

随便推点

二叉树的各种创建方法_二叉树的建立-程序员宅基地

文章浏览阅读6.9w次,点赞73次,收藏463次。1.前序创建#include&lt;stdio.h&gt;#include&lt;string.h&gt;#include&lt;stdlib.h&gt;#include&lt;malloc.h&gt;#include&lt;iostream&gt;#include&lt;stack&gt;#include&lt;queue&gt;using namespace std;typed_二叉树的建立

解决asp.net导出excel时中文文件名乱码_asp.net utf8 导出中文字符乱码-程序员宅基地

文章浏览阅读7.1k次。在Asp.net上使用Excel导出功能,如果文件名出现中文,便会以乱码视之。 解决方法: fileName = HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8);_asp.net utf8 导出中文字符乱码

笔记-编译原理-实验一-词法分析器设计_对pl/0作以下修改扩充。增加单词-程序员宅基地

文章浏览阅读2.1k次,点赞4次,收藏23次。第一次实验 词法分析实验报告设计思想词法分析的主要任务是根据文法的词汇表以及对应约定的编码进行一定的识别,找出文件中所有的合法的单词,并给出一定的信息作为最后的结果,用于后续语法分析程序的使用;本实验针对 PL/0 语言 的文法、词汇表编写一个词法分析程序,对于每个单词根据词汇表输出: (单词种类, 单词的值) 二元对。词汇表:种别编码单词符号助记符0beginb..._对pl/0作以下修改扩充。增加单词

android adb shell 权限,android adb shell权限被拒绝-程序员宅基地

文章浏览阅读773次。我在使用adb.exe时遇到了麻烦.我想使用与bash相同的adb.exe shell提示符,所以我决定更改默认的bash二进制文件(当然二进制文件是交叉编译的,一切都很完美)更改bash二进制文件遵循以下顺序> adb remount> adb push bash / system / bin /> adb shell> cd / system / bin> chm..._adb shell mv 权限

投影仪-相机标定_相机-投影仪标定-程序员宅基地

文章浏览阅读6.8k次,点赞12次,收藏125次。1. 单目相机标定引言相机标定已经研究多年,标定的算法可以分为基于摄影测量的标定和自标定。其中,应用最为广泛的还是张正友标定法。这是一种简单灵活、高鲁棒性、低成本的相机标定算法。仅需要一台相机和一块平面标定板构建相机标定系统,在标定过程中,相机拍摄多个角度下(至少两个角度,推荐10~20个角度)的标定板图像(相机和标定板都可以移动),即可对相机的内外参数进行标定。下面介绍张氏标定法(以下也这么称呼)的原理。原理相机模型和单应矩阵相机标定,就是对相机的内外参数进行计算的过程,从而得到物体到图像的投影_相机-投影仪标定

Wayland架构、渲染、硬件支持-程序员宅基地

文章浏览阅读2.2k次。文章目录Wayland 架构Wayland 渲染Wayland的 硬件支持简 述: 翻译一篇关于和 wayland 有关的技术文章, 其英文标题为Wayland Architecture .Wayland 架构若是想要更好的理解 Wayland 架构及其与 X (X11 or X Window System) 结构;一种很好的方法是将事件从输入设备就开始跟踪, 查看期间所有的屏幕上出现的变化。这就是我们现在对 X 的理解。 内核是从一个输入设备中获取一个事件,并通过 evdev 输入_wayland

推荐文章

热门文章

相关标签