前言
C++11是继,C++98/03之后的最大的一次更新,这次更新虽然花了很长的时间,但是真的推出了很多的干货!本期内容开始我们将介绍C++11常用的操作!
目录
一、C++11介绍
二、统一的列表初始化
1、{}初始化
在C++98中,标准允许使用花括号 {} 对数组或者结构体元素(对象)进行统一的列表初始化!
struct Point
{
int _x;
int _y;
};
int main()
{
// 对数组使用 {} 进行初始化
int arr1[] = { 1,2,3,3,4 };
int arr2[3] = { 11,22,33 };
// 对自定义类型的 结构体对象 使用 {} 进行初始化
Point p = { 3,5 };
return 0;
}
C++11扩大了用大花括号(即列表初始化)的使用范围,不仅可以使用{}来初始数组、结构体对象,还可以用于初始化自定义类型以及所有的内置类型!
注意:使用 {} 初始化时,可以带 = 也可以不带!
// C++11 可以使用 {} 初始化内置类型
int a1 = 3;
int a2 = { 3 };
int a3{ 3 };// 可以不带 =
// C++11 可以使用 {} 初始化, new 的表达式
char* ptr = new char[4]{ '0' };
创建对象时也可以使用列表初始化的方式调用构造函数初始化!
OK,着里就拿最简单的日期类举例:
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(2024, 8, 30);
Date d2 = { 2024,8,29 };
Date d3{ 2024,8,28 };
return 0;
}
这里一定要注意:这三种方式看起来一样,实则不太一样!!!
第一个:Date d1(2024, 8, 30);
这个就是常规的用()中的参数去匹配对象的构造函数,然后构造!
第二个:Date d2 = { 2024,8,29 };
这个是用 {} 中的参数走的是隐式的构造(前提是该类的构造函数是单参数或者可以接收第一个参数的构造函数),也就是使用{}中的参数去构造一个临时的Date对象,然后在用这个临时的对象去拷贝构造 d2!
我们知道临时对象具有常性,我们可以用引用验证:
这是一种验证的方式,前面我们学过一个关键字:explicit 就是防止这种隐式类型转换的!
第三个:Date d3{ 2024,8,28 };
这个就是C++11的列表初始化,去初始化的!注意这个和第一个看似很像!但是不一样,和第二个仅仅是差一个 = 但是也不一样!我们发现加上explicit后第三个是正常的!
2、std::initializer_list
initializer_list 是C++11提供的一个类型,它允许以简便的方式初始化各种容器或者其他对象!它 的主要目的是:不显示的使用构造函数的情况下,实现统一的初始化写法!它有点类似于数组,即是同一类型元素的一个序列!
int main()
{
auto il1 = { 1,2,3,4 };
auto il2 = { '1', '2', '3' };
cout << typeid(il1).name() << endl;
cout << typeid(il2).name() << endl;
return 0;
}
std::initializer_list 的使用场景
std::initializer_list 一般是作为构造函数的参数,C++11对不少的STL容器的构造函数和赋值拷贝都增加了它!这样就可以直接使用{}来初始化了!
OK,例如:vector、list、map等:
其他就不一一列举了:
vector<int> nums = { 1,2,3 };
list<int> lt = { 1,5,8 };
nums = {0,0,9};// 赋值
map<int, int> mp = { {1,1}, {2,2},{3,3} };
这里唯一注意的一个就是map,他这个是先用里面的{}去构造了临时pair对象,然后去用列表初始化去构造的!
注意:{} 和 initializer_list不是同一个东西
{}
是一种初始化语法,用于初始化对象。initializer_list
是一个类型,用于表示具有相同类型的元素列表。- 当一个类有接受
initializer_list
类型参数的构造函数时,您可以使用{}
来创建该类的对象,并且编译器会将{}
中的值转换为一个initializer_list
对象来构造该对象。
三、声明
C++11提供了很多的简化声明方式,尤其是在使用模板时;
1、auto
int a = 10;
auto pa = &a;
vector<int> nums = { 1,23,4,5 };
vector<int>::iterator it1 = nums.begin();
auto it2 = nums.begin();
for (const auto& e : nums)
{
cout << e << " ";
}
2、decltype
decltype是declaration type
的缩写,在C++中,decltype是一个类型推断的关键字,作用是将变量的类型声明为指定表达式的类型!他也是自动推导类型的;但是和auto不同的是,decltype是可以用推导出的类型定义变量的,这一点是auto做不到的!
// decltype的一些使用使用场景
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; // ret的类型是double
decltype(&x) p; // p 就是 const int*
cout << typeid(ret).name() << endl;
cout << typeid(p).name() << endl;
F(1, 'a');
return 0;
}
3、nullptr
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
但是由于一些特殊定情况:
void func(void* ptr)
{
cout << "func(void* ptr)" << endl;
}
void func(int tmp)
{
cout << "func(int tmp)" << endl;
}
int main()
{
func(0);
func(NULL);
return 0;
}
上述的调用都会匹配到整数0,如果要正确的调用就需要去强转:
四、范围for
范围for 被戏称为语法糖;是一个非常好用的语法!这个我们在一开始在学习C++基础的时候就介绍过!它的底层是迭代器,我们在前面手搓轮子的时候也验证过:
vector<int> nums = { 1,2,3,4 };
for (int i = 0; i < nums.size(); i++)
cout << nums[i] << " ";
cout << endl;
for (const auto& e : nums)
cout << e << " ";
cout << endl;
五、STL的容器新增
C++11不光新增了用initializer_list类型的构造函数,而且还新增了一些容器:
其中unordered_map 和 unordered_set 我们前面介绍并模拟实现了!这里只介绍其他两个:
array
array其实就是一个静态的数组!他可以获取静态数组的元素个数等以及加强了越界的检查等,但是我个人觉得这个东西没什么用,以为我们呢有vector!
array<int, 5> arr = { 1,2,3,4 };
cout << arr[20] << endl;
以前的静态数组尤其VS是抽查,越的远一点就检查不到了!但是这个是直接用的断言!
forward_list
字面意思就是向前的链表,其实就是单链表!前面我们学的那个list是带头双向循环链表!
OK,本期内容就介绍到这里,我是cp我们下期再见!
结束语:不畏艰难,深耕技术!