{}初始化
C++98
在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
new数组的初始化
C++11扩大了用{}括起的列表(初始化列表)的使用范围,使其可用于所有的内置类型和用户自
定义的类型,(使用初始化列表时,可添加等号(=),也可不添加).
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;
}
虽然语法支持不带=
,但不带的话着实看着别扭,所以中间那块都不建议使用
其中,值得看的就是可以在new的时候对数组的多个元素进行初始换
多参数的构造函数初始化
在从C++98的时候,我们对一个多参数的构造函数初始化的时候,只能像这样:
Date d1(2022, 1, 1);
C++11为了与C98的结构体初始化
统一格式,就可以用提供了{}这样的方式,作为上面的平替
Date d2{ 2022, 1, 2 };
为了更一致,同样也可以这样
Date d3 = { 2022, 1, 3 };
这两个的实际原理是不一样的,第二个会发生隐式转换、拷贝构造,但编译器优化之后就和上面没有区别了
但如果给Date的构造函数加个explicit,阻断隐式转换,就不可以用下面这个了
报错:
总测试:
class Date
{
public:
explicit 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); // old style
// C++11支持的列表初始化,这里会调用构造函数初始化
Date d2{ 2022, 1, 2 };
Date d3 = { 2022, 1, 3 };
return 0;
}
这样,一方面可以统一初始化的形式,另一方面也可以实现在new的时候同时构造多个对象
new自定义类型的数组并初始化
C++98 new一个自定义类型并初始化:
Date* p1 = new Date(2022, 1, 3)
C++11 new一个自定义类型并初始化:
Date* p2 = new Date{ 2022, 1, 3 };
new自定义类型的数组并初始化
Date* p3 = new Date[3]{ {2022,1,3},{2022,1,4} ,{2022,1,5} };
容器的初始化
还有一个很重要的作用就是方便了容器的初始化与与赋值
通过{}
就可以想数组一样,给容器同时初始化多个值;
#include <vector>
#include <list>
#include <map>
int main()
{
vector<int> v = { 1,2,3,4 };
list<int> lt = { 1,2 };
vector<Date> vd = { {2022,1,3},{2022,1,4} ,{2022,1,5} };
// 这里{"sort", "排序"}会先初始化构造一个pair对象
map<string, string> dict = { {"sort", "排序"}, {"insert", "插入"} };
// 使用大括号对容器赋值
v = { 10, 20, 30 };
return 0;
}
自此,{}
完美解决了几乎所有类型的初始化问题,包括类型的嵌套
std::initializer_list
用initializer_list 构造一个类
上面所有的内置类型的列表初始化,都是编译器底层完成的
但是自定义类型的列表初始化则不同,
C++11在这里会做了一个语法的认定,把识别到的常量的{}列表
认为是一个initializer_list
这个initializer_list
是C++11新增加一个类型
auto il = { 10, 20, 30 }; // il 的类型是 initializer_list
像这样,如果用一个auto变量去接收,那il的类型就是initializer_list
我们就可以把它看成是一个容器,它{}里面的元素被存放到了一个只读的数组中,用一组头尾指针维护这个数组,通过迭代器就可以访问这个容器
一般我们并不会直接用这个容器
它一般被用于其他的迭代器的初始化:如vector、list、map
可以看到,C++11的vector
新增了一个initializer_list
的构造函数,和赋值重载
从而实现,用一个{}去初始化容器,或对容器进行赋值
实际上,vector内的实现就是通过initializer_list的迭代器把数组中的值一个一个push进来
我们可以模拟实现一下vector的 initializer_list 的构造和赋值
namespace yb
{
template<class T>
class vector {
public:
typedef T* iterator;
vector(initializer_list<T> l)
{
//初始化vector
_start = new T[l.size()];
_finish = _start + l.size();
_endofstorage = _start + l.size();
//通过initializer_list为vector赋值
iterator vit = _start;
typename initializer_list<T>::iterator lit = l.begin();
while (lit != l.end())
{
*vit++ = *lit++;//这里我们没有写push_back,实际调用push_back就可以
}
//for (auto e : l)
// *vit++ = e;
}
vector<T>& operator=(initializer_list<T> l) {
vector<T> tmp(l);
std::swap(_start, tmp._start);
std::swap(_finish, tmp._finish);
std::swap(_endofstorage, tmp._endofstorage);
return *this;
}
private:
iterator _start;
iterator _finish;
iterator _endofstorage;
};
}
int main()
{
yb::vector<int> v0 { 1,2,3,4 };
yb::vector<int> v = { 1,2,3,4 };
v = { 10, 20, 30 };
return 0;
}
vector<int> v0 { 1,2,3,4 };
执行的时候会把{ 1,2,3,4 }
转换成一个initializer_list
对象,然后用构造生成v0
vector<int> v = { 1,2,3,4 };
执行的时候,会把{ 1,2,3,4 }
转换成一个initializer_list
,然后通过进行隐式转换,把initializer_list构造成一个临时的
vector,最后调用
vector初始化vector`的拷贝构,生成v而事实上,编译器会直接优化为:直接用构造生成v,相当于
vector<int> v0 { 1,2,3,4 };
下面的赋值
v = { 10, 20, 30 };
,即使不去实现这个赋值重载如果不想让它发生这个隐式转化,可以在构造函数处加一个
explicit
:;当然,如果实现了,肯定会 优先调用这个重载;
接收initializer_list 的函数
在C++98中,min
/max
函数用于获取两个数的较小值/最大值
C++11扩展了它的使用方法,可以用于比较多个值的最小值
可以看到,它可以接收一个initializer_list
作为参数(如果是内置类型或者写了operator>=的自定义类型的比较,可以不传后面的comp(一个仿函数、lambda表达式或者函数指针))
使用:
int main()
{
cout << min({ 1,2,3 });
return 0;
}
这里{ 1,2,3 }
会自动构造一个initializer_list,传给min函数,min函数会通过遍历这个容器找到最小值。
这里的min只能针对initializer_list进行查找,那有没有一个函数可以针对任何一个容器进行查找呢?
min_element
/max_element
可以传入一个容器的两个迭代器(左闭右开),找出这两个迭代器中间的最小元素,并返回它的迭代器。
int main()
{
vector<int> v = { 1,2,3,4 };
auto min = min_element(v.begin(), v.end());
cout << *min;
}