文章目录
- 十九、C++11
- 1. 统一的列表初始化
- {}初始化
- std::initializer_list
- 2. 声明
- auto
- decltype
- nullptr
- 4. 范围for
- 5. STL中的一些变化
- 新容器
- 新方法
- 6. 右值引用和移动语句
- 左值引用和右值引用
- 未完待续
十九、C++11
1. 统一的列表初始化
{}初始化
在C++98中,标准允许使用花括号{}对数组或者结构体元素进行统一的列表初始值设定。(注意:这里的初始化列表并不是构造函数里的初始化列表。)在C++11中,扩大了用大括号括起的初始化列表的使用范围,使其可用于所有的内置类型和用户自定义的类型,使用初始化列表时,可添加等号 (=),也可不添加。
#include <iostream>
using namespace std;
struct Point
{
int _x;
int _y;
};
int main()
{
// C++98 就支持的
int array1[] = { 1, 2, 3, 4, 5 };
int array2[5] = { 0 };
Point p = { 1, 2 };
// C++11 才支持的
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;
}
创建对象时也可以使用列表初始化方式调用构造函数初始化:
#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(2022, 1, 1);
// C++11支持的列表初始化,这里会调用构造函数初始化
Date d2{ 2022, 1, 2 };
Date d3 = { 2022, 1, 3 };
return 0;
}
std::initializer_list
链接戳这里
大部分容器已经引进了 initializer_list ,使得容器也可以通过{}用来初始化。
#include <iostream>
#include <vector>
#include <map>
#include <list>
using namespace std;
int main()
{
vector<int> v = { 1,2,3,4 };
list<int> lt = { 1,2 };
// 这里{"sort", "排序"}会先初始化构造一个pair对象
map<string, string> dict = { {"sort", "排序"}, {"insert", "插入"} };
// 使用大括号对容器赋值
v = { 10, 20, 30 };
return 0;
}
2. 声明
auto
c++11提供了多种简化声明的方式,尤其是在使用模板时。auto 的用法是自动类型推导。对于自定义类型,很容易出现类型名字特别长,不方便书写,就可以使用auto来自动推导类型名。
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <vector>
#include <map>
#include <list>
using namespace std;
int main()
{
int i = 10;
auto p = &i;
auto pf = strcpy;
cout << typeid(p).name() << endl << endl;
cout << typeid(pf).name() << endl << endl;
map<string, string> dict = { {"sort", "排序"}, {"insert", "插入"} };
map<string, string>::iterator it = dict.begin();
auto ait = dict.begin();
cout << typeid(dict).name() << endl << endl;
cout << typeid(it).name() << endl << endl;
cout << typeid(ait).name() << endl << endl;
return 0;
}
auto不仅仅可以用作变量类型名,也可以用作函数返回值。
auto func()
{
int ret;
return ret;
}
decltype
关键字decltype将变量的类型声明为表达式指定的类型。一般在模板情况下,我们不知道接下来要定义的变量是什么类型,我们就可以借助这个关键字,他会推导类型,然后根据这个类型来定义变量。
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
template<class T1, class T2>
void F(T1 t1, T2 t2)
{
// 推导类型来定义变量
decltype(t1 * t2) ret;
cout << 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;
cout << typeid(p).name() << endl;
F(1, 'a');
return 0;
}
nullptr
这个我们已经比较熟悉了。由于C++中NULL被定义成字面量0,这样就可能回带来一些问题,因为0既能指针常量,又能表示整形常量。所以出于清晰和安全的角度考虑,C++11中新增了nullptr,用于表示空指针。
4. 范围for
这个我们也很熟悉了,借助的是迭代器。只有有迭代器的容器才能使用。
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> v{ 1,2,3,4,5,6,7,8,9 };
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
return 0;
}
5. STL中的一些变化
查文档戳这里
新容器
其中 unordered_set 和 unordered_map 我们已经见过了,就是哈希表,array 则是静态数组,长度是已经固定的数组。forward_list 就是单链表。
新方法
基本每个容器中都增加了一些C++11的方法,但是其实很多都是用得比较少的。比如提供了cbegin和cend方法返回const迭代器等等,但是实际意义不大,因为begin和end也是可以返回const迭代器的,这些都是属于锦上添花的操作。还有其他新方法这里建议去看文档。
6. 右值引用和移动语句
左值引用和右值引用
传统的C++语法中就有引用的语法,而C++11中新增了的右值引用语法特性,所以从现在开始我们之前学习的引用就叫做 左值引用。无论左值引用还是右值引用,都是给对象取别名。
那什么是左值,什么是右值?左值就是我们可以获取它的地址,并且它一般情况下是可以被赋值的就是左值。右值就是一个表示数据的表达式,如:字面常量、表达式返回值,函数返回值等等。
并不是在左边就是左值,右边就是右值。在引用时,左值和右值都在右边。
左值引用就是给左值的引用,给左值取别名。
右值引用就是对右值的引用,给右值取别名。
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
int main()
{
// 以下的p、b、c、*p都是左值
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;
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);
return 0;
}
语法上:引用都是别名,不开空间,左值引用就是给左值取别名,右值引用就是给右值取别名。
底层上:引用都是指针实现的,左值引用存的是当前左值的地址。右值引用是把当前右值拷贝到栈上的一个临时空间,指向这个临时空间的地址。
左值引用只能引用左值,不能引用右值,但是const左值引用既可引用左值,也可引用右值。
右值引用只能右值,不能引用左值。但是右值引用可以move以后的左值。
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
int main()
{
// 右值引用只能右值,不能引用左值。
int&& r1 = 10;
int a = 10;
// 这里会报错
int&& r2 = a;
// 但这里会成功,右值引用可以引用move以后的左值
int&& r3 = move(a);
return 0;
}