在C++98中,标准允许使用花括号{}对数组或者结构体元素进行统一的列表初始值设定。比如:
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++11中,我们都可以使用{}来对我们的元素进行初始化
C++11扩大了用大括号括起的列表(初始化列表)的使用范围,使其可用于所有的内置类型和用户自定义的类型,使用初始化列表时,可添加等号(=),也可不添加。
#include
#include
#include
#include
C++11中有了一种新的类型,initializer_list,语法上原生支持通过大括号的方式初始化给它。它就像一个顺序表一样,支持迭代器,但是不支持插入数据。
int main()
{// 调用支持list (initializer_list il)类似这样的构造函数vector v1 = { 1, 2, 3, 4, 5, 6 };vector v2 { 1, 2, 3, 4, 5, 6 };list lt1 = { 1, 2, 3, 4, 5, 6 };list lt2{ 1, 2, 3, 4, 5, 6 };//c++11这里新增了一个类型initializer_list类型来实现,是这里默认需要的一个容器auto x = { 1, 2, 3, 4, 5, 6 };cout << typeid(x).name() << endl;return 0;
}
那我们的vector等容器又是如何支持大括号的初始化的呢?
因为我们的C++11同时也对我们的库函数进行了更新,让我们的库函数都支持通过initializer_list来进行构造。
这里我们查看一下vector的构造函数中多了一个构造方法,也就是我们的initializer
那如何让我们自己之前写过的vector支持通过initializer来初始化
vector(initializer_list il):_start(nullptr),_finish(nullptr),_end_of_storage(nullptr){reserve(il.size());iterator it=begin();for(auto&e:il){push_back(e);}}
测试代码
void myvector_test18(){vector v1={1,2,3,4,5,6,7,8,9,10};vector v2{1,2,3,4,5,6,7,8,9,10};for(auto e:v2){cout<
当然,我们的map,set,pair等容器也可以通过这种方式进行构造
#include
#include
#include
#include
当然在我们的C++11的库中,还将许多赋值也进行了重载,让其能够支持initializer进行赋值
下面是我们的测试代码
#include
#include
总结:
C++11之后,一切对象都可以用列表初始化。但是我们建议普通对象还是用以前的方式初始化,容器可以采用花括号进行初始化。
在C++98中auto是一个存储类型的说明符,表明变量是局部自动存储类型,但是局部域中定义局部的变量默认就是自动存储类型,所以auto就没什么价值了。C++11中废弃auto原来的用法,将其用于实现自动类型推断。这样要求必须进行显示初始化,让编译器将定义对象的类型设置为初始化值的类型。
int main()
{int i = 10;auto p = &i;auto pf = strcpy;cout << typeid(p).name() << endl;cout << typeid(pf).name() << endl;map dict = { {"sort", "排序"}, {"insert", "插入"} };//如果我们这里要自己写的话,需要写一长串,但是使用auto的话就非常方便//map::iterator it = dict.begin();auto it = dict.begin();return 0;
}
如果我们这里使用auto的话,我们上面代码中的迭代器的类型等等,我们都不用自己手写,就非常方便,但是我们如果使用了auto进行自动类型推断,我们代码的可读性就会变差,但是有些编译器会给你标识出来(比方说clion)。
typename可以推导对象的类型,但是我们不能通过这个推导出来的类型来定义我们的对象,只是单纯地拿到这个类型的字符串。
但是如果我们想要用推导出来的类型重新定义一个新的对象呢?
int main()
{int x = 10;// typeid拿到只是类型的字符串,不能用这个再去定义对象什么的// 下面这样写会报错的,没有下面这样的用法// typeid(x).name() y = 20;decltype(x) y1 = 20.22;//auto和decltype是不一样的,我们的auto这里推导出来的是double,也就是我们右边的赋值的元素的类型是什么,我们auto推导出来也就是什么//但是我们的deltype推导出来的类型是x的类型,也就是intauto y2 = 20.22;cout << y1 << endl;cout << y2 << endl;return 0;
}
由于C++中NULL被定义成字面量0,这样就可能会带来一些问题,因为0既能指针常量,又能表示整形常量。所以出于清晰和安全的角度考虑,C++11中新增了nullptr,用于表示空指针。
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
这里的范围for和我们java中的增强for有些相似,底层是一个迭代器。
可以查看我们之前的博客中的迭代器和范围for相关的部分
int main()
{vector tmp{1,2,3,4,5,6,7,8,9};for(auto i:tmp){cout<
< array >和< forward_list >显得有些鸡肋
因为< array >是固定大小的数组容器,不支持尾插和尾删,支持[]迭代器。
C++11增加这个的初衷是为了替代c语言中的数组
int main()
{const size_t N = 100;int a1[N];// C语言数组越界检查,越界读基本检查不出来,越界写是抽查a1[N];//a1[N] = 1;a1[N+5] = 1;// 越界读写都可以被检查出来// 实际情况:array用得很少,一方面大家用c数组用惯了// 用array不如用vector + resize去替代c数组array a2;a2[N];a2[N] = 1;a2[N + 5] = 1;return 0;
}
< forward_list >是一个单链表,我们的< list >是双向链表,在使用的时候其实< forward_list >插入的是在我们当前指定位置的后一个位置插入,然后erase并不是擦除当前的位置,而是擦除当前位置的下一个位置。
1.都支持了initializer_list构造,用来支持列表初始化
2.比较鸡肋的接口,比如cbegin,cend系列
3.移动构造和移动赋值,用来对标拷贝构造和拷贝赋值,在某些场景下可以提高效率
(set&&x);
(set& operator=(set&&x)
4.右值引用的参数的插入