C++11常用的一部分新特性(上)

简介: C++11常用的一部分新特性(上)

统一的列表初始化

{}初始化

C++11扩大了用大括号括起的列表(初始化列表)的使用范围,使其可用于所有的内置类型和用户自

定义的类型,使用初始化列表时,可添加等号(=),也可不添加。

#include<iostream>
using namespace std;
int main()
{
  int arr1[] = { 1,2,3,4,5,6 };
  int arr2[]{ 1,2,3,4,5,6 };
  for (auto e : arr1)
  {
    cout << e << " ";
  }
  cout << endl;
  for (auto e : arr2)
  {
    cout << e << " ";
  }
  cout << endl;
  return 0;
}

#include<iostream>
using namespace std;
struct Point
{
 int _x;
 int _y;
};
int main()
{
  Point* p1 = new Point[2]{ {1,1},{2,2} };
  return 0;
}

#include<iostream>
using namespace std;
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(2023, 5, 25);
  Date d2{ 2023, 5, 25 };
  Date d3 = { 2023, 5, 25 };
  return 0;
}

也就是说这里用花括号进行初始化调用的是类的构造。

也就是说,C++11几乎可以一切都可以用花括号初始化,包括变量(但是不建议这样)。

std::initializer_list

来看下面这段代码:

#include<iostream>
#include<vector>
#include<list>
using namespace std;
int main()
{
  vector<int> arr{1, 2, 3, 4, 5, 6};//这里的初始化为什么可以随意改变元素数量呢?
  auto a = { 10,20,30 };//来看看这个花括号初始化成了什么类型
  cout << typeid(a).name() << endl;//这里拿到的是类型的字符串
  return 0;
}

这是initializer_list类型的使用文档https://cplusplus.com/reference/initializer_list/initializer_list/

这个类似一个常量数组,有两个指针指向数组的开始和结束(其实也是迭代器)。

并且这个vector可以利用这个类型进行初始化的。

其实就相当于将initializer_list类型中的数据遍历然后push_back()到vector里面。

这种类型的实用处就是:

//这里就不用初始化一个pair类型的然后在插入map中了,因为里面是匿名对象的初始化
map<string, string> str = { {"字符串","string"},{"排序","sort"} };//里面的两个小花括号也可以理解为一个pair类型的initializer_list数组

声明

auto

这个经常用,自动推导左边对象类型。

在C++98中auto是一个存储类型的说明符,表明变量是局部自动存储类型,但是局部域中定义局

部的变量默认就是自动存储类型,所以auto就没什么价值了。C++11中废弃auto原来的用法,将

其用于实现自动类型腿断。这样要求必须进行显示初始化,让编译器将定义对象的类型设置为初

始化值的类型。

decltype

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

#include<iostream>
using namespace std;
int main()
{
  int x = 0;
  cout << typeid(x).name() << endl;
  decltype(x) y;
  cout << typeid(y).name() << endl;
  return 0;
}

那么decltype使用的地方在哪里呢?

#include<iostream>
using namespace std;
template<class T1, class T2>
void F(T1 t1, T2 t2)
{
  decltype(t1 * t2) ret = t1 * t2;//这里万一涉及到整型提升,不知道提升到哪个类型就可以自动推导,不至于丢失精度
  cout << typeid(ret).name() << endl;
  cout << ret << endl;
}
int main()
{
  F(1, 2);
  F(1, 2.2);
  return 0;
}

nullptr

这个之前也经常用。

由于C++中NULL被定义成字面量0,这样就可能回带来一些问题,因为0既能指针常量,又能表示

整形常量。所以出于清晰和安全的角度考虑,C++11中新增了nullptr,用于表示空指针。

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

STL中一些变化

新容器

array

https://legacy.cplusplus.com/reference/array/array/

这个新容器和数组的功能没什么区别,不如vector好用,比普通数组多一个越界检查的报错。

forward_list

https://legacy.cplusplus.com/reference/forward_list/forward_list/

这是个单链表。

这里的区别就是,每个节点少一个指针的大小,并且没有头插头删(并不是那么好用)

已有容器的新接口

这里以vector举例:

这四个其实就是上面的正迭代器和反迭代器,c只是为了显示是const版本的而已,看起来更容易辨别。

这个接口是缩容的接口,如果空间浪费的实在是太大,可以用一下(用时间换空间)。

还有这两个接口,与右值引用和可变模板参数有关,下面会结合这个接口讲解。

右值引用和移动语义

左值引用和右值引用

什么是左值?

左值是一个表示数据的表达式(如变量名或解引用的指针),我们可以获取它的地址+可以对它赋值,左值可以出现赋值符号的左边,右值不能出现在赋值符号左边。

定义时const修饰符后的左值,不能给他赋值,但是可以取它的地址。左值引用就是给左值的引用,给左值取别名。

什么是右值?

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

右值引用就是对右值的引用,给右值取别名。

注意:

  1. 左值引用只能引用左值,不能引用右值。
  2. 但是const左值引用既可引用左值,也可引用右值。
  3. 右值引用只能右值,不能引用左值。
  4. 但是右值引用可以move以后的左值。
#include<iostream>
using namespace std;
int main()
{
  // 左值引用只能引用左值,不能引用右值。
  int a = 10;
  int& ra1 = a;//ra为a的别名
  //int& ra2 = 10;//编译失败,因为10是右值
  //const左值引用既可引用左值,也可引用右值。
  const int& ra3 = 10;
  const int& ra4 = a;
  int a = 10;
  int&& r2 = a;
  // error C2440: “初始化”: 无法从“int”转换为“int &&”
  // message : 无法将左值绑定到右值引用
  // 右值引用可以引用move以后的左值
  int&& r3 = std::move(a);
  return 0;
}

需要注意的是右值是不能取地址的,但是给右值取别名后,会导致右值被存储到特定位置,且可

以取到该位置的地址,也就是说例如:不能取字面量10的地址,但是rr1引用后,可以对rr1取地

址,也可以修改rr1。如果不想rr1被修改,可以用const int&& rr1 去引用。

右值引用使用场景和意义

左值引用最大的意义就是函数传参,返回值,减少拷贝。

那么左值引用的缺点是什么?

看下面代码:

template<class T>
T func(const int x)
{
  T ret;
  return ret;//这里ret是局部变量,出作用域就会销毁,所以是一个传值返回
}

右值引用的价值之一就是补齐这最后一块短板。

相关文章
|
14天前
|
C语言 C++ 开发者
深入探索C++:特性、代码实践及流程图解析
深入探索C++:特性、代码实践及流程图解析
|
2月前
|
算法 数据处理 C++
【C++ 20 新特性 算法和迭代器库的扩展和泛化 Ranges】深入浅出C++ Ranges库 (Exploring the C++ Ranges Library)
【C++ 20 新特性 算法和迭代器库的扩展和泛化 Ranges】深入浅出C++ Ranges库 (Exploring the C++ Ranges Library)
109 1
|
1天前
|
自然语言处理 编译器 C语言
【C++】C++ 入门 — 命名空间,输入输出,函数新特性
本文章是我对C++学习的开始,很荣幸与大家一同进步。 首先我先介绍一下C++,C++是上个世纪为了解决软件危机所创立 的一项面向对象的编程语言(OOP思想)。
23 1
|
10天前
|
存储 程序员 C语言
深入理解C++:从语言特性到实践应用
深入理解C++:从语言特性到实践应用
22 3
|
2月前
|
存储 安全 编译器
【C++】类的六大默认成员函数及其特性(万字详解)
【C++】类的六大默认成员函数及其特性(万字详解)
38 3
|
2月前
|
算法 调度 数据库
【C++20 新特性 Calendar (C++20) − Time zone (C++20)】时间的艺术与科学: C++中的现代日期和时区处理
【C++20 新特性 Calendar (C++20) − Time zone (C++20)】时间的艺术与科学: C++中的现代日期和时区处理
86 3
|
2月前
|
算法 编译器 C++
【C++ 14 新特性 std::integer_sequence 】了解 std::integer_sequence 的使用
【C++ 14 新特性 std::integer_sequence 】了解 std::integer_sequence 的使用
56 1
|
2月前
|
算法 安全 编译器
【C++ 17 新特性 折叠表达式 fold expressions】理解学习 C++ 17 折叠表达式 的用法
【C++ 17 新特性 折叠表达式 fold expressions】理解学习 C++ 17 折叠表达式 的用法
30 1
|
2月前
|
编译器 程序员 C++
【C++ 17 新特性 结构化绑定】深入理解C++ 17 结构化绑定[key, value] 的处理
【C++ 17 新特性 结构化绑定】深入理解C++ 17 结构化绑定[key, value] 的处理
47 1
|
3天前
|
C语言 C++
【C++】string类(常用接口)
【C++】string类(常用接口)
13 1