C++日新月异的未来代码:C++11(上)
文章目录
- 1.统一的列表初始化
- 1.1 普通{ }初始化
- 1.2 initializer_list
- 2.声明
- 2.1 auto、nullptr
- 2.2 decltype
- 3.左值右值
- 3.1 概念
- 3.2 左值引用与右值引用比较
- 3.3 左值引用与右值引用的应用
- 3.4 完美转发
- 希望读者们多多三连支持
- 小编会继续更新
- 你们的鼓励就是我前进的动力!
C++11 能更好地用于系统开发和库开发、语法更加泛华和简单化、更加稳定和安全,不仅功能更强大,而且能提升程序员的开发效率,公司实际项目开发中也用得比较多,所以我们要作为一个重点去学习
1.统一的列表初始化
1.1 普通{ }初始化
struct Point { int _x; int _y; }; int main() { int array1[] = { 1, 2, 3, 4, 5 }; int array2[5] = { 0 }; Point p = { 1, 2 }; return 0; }
在 C++98 中,标准允许使用花括号{}对数组或者结构体元素进行统一的列表初始值设定
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; }
C++11 扩大了用大括号括起的列表(初始化列表)的使用范围,使其可用于所有的内置类型和用户自定义的类型,使用初始化列表时,可添加等号 =,也可不添加
简单来说,C++11 一切皆可用 {} 初始化,并且可以不写 =,建议日常定义,不要去掉 =,但是我们要能看懂
🔥值得注意的是: 像 Point p1 = { 1, 1 } ,是一种多参数隐式转化,无论哪一种初始化方式都是要调用对应的构造函数的
1.2 initializer_list
initializer_list 是 C++11 引入的一个轻量级容器,用于支持统一的初始化语法和参数列表初始化。它允许函数或类接收任意数量的同类型参数,并提供简洁的初始化方式
vector v1 = { 1,2,3 }; // 调用 vector 的 initializer_list 构造函数
stl 中的容器基本都是支持 initializer_list 初始化的,这初始化的方式和上面的隐式类型不同,具体方式如下:
首先要知道 initializer_list 主要有这三个成员
- 创建一个临时数组(存储在栈上),包含元素 {1, 2, 3}
- 生成一个 initializer_list 对象,该对象引用临时数组(内部保存数组的起始地址和长度)
- initializer_list 引用临时数组(不拥有其内存),vector 会将临时数组的内容复制元素到自己的内存空间(堆上),与临时数组无关
- 临时数组被销毁
🔥值得注意的是: vector 是复制引用数组的元素,而不是直接接收,是因为该临时数组销毁之后,如果 vector 还接收着的话会造成悬空引用
因此 initializer_list 的存在还是很有必要的:
在 C++11 之前,若想让函数接收一个类似 {1, 2, 3} 的初始化列表作为参数,需要通过数组或容器(如 vector)传递,不够直观。initializer_list 允许函数直接以初始化列表为参数,使代码更符合直觉
2.声明
2.1 auto、nullptr
该 C++ 增加的特性在前面的文章进行过详细讲解,不过多叙述
传送门:C++命运石之门代码抉择:C++入门(下)
2.2 decltype
// decltype的一些使用使用场景 template void F(T1 t1, T2 t2) { decltype(t1 * t2) ret; cout const int x = 1; double y = 2.2; decltype(x * y) ret; // ret的类型是double decltype(&x) p; // p的类型是int* cout // 以下的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; return 0; } 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; return 0; } // 左值引用只能引用左值,不能引用右值。 int a = 10; int& ra1 = a; // ra为a的别名 //int& ra2 = 10; // 编译失败,因为10是右值 // const左值引用既可引用左值,也可引用右值。 const int& ra3 = 10; const int& ra4 = a; return 0; } // 右值引用只能右值,不能引用左值。 int&& r1 = 10; // error C2440: “初始化”: 无法从“int”转换为“int &&” // message : 无法将左值绑定到右值引用 int a = 10; int&& r2 = a; // 右值引用可以引用move以后的左值 return 0; } static string a; return a; } int main() { string ret = func(); return 0; } cout cout string s1("hello world"); string s2(s1); string s3(move(s1)); return 0; } cout cout cout cout Fun(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; } cout cout cout cout Fun(forward PerfectForward(10); // 右值 int a; PerfectForward(a); // 左值 PerfectForward(std::move(a)); // 右值 const int b = 8; PerfectForward(b); // const 左值  PerfectForward(std::move(b)); // const 右值 return 0; }