目录
写在前面
c++11标准中,initializer_list对于标准库的编写影响非常大,说一个简单的例子,在c++11之前,max函数的源程序是这样的:
template <class T> const T& max (const T& a, const T& b);
template <class T, class Compare>
const T& max (const T& a, const T& b, Compare comp); //支持自己编写的比较函数
也就是max函数只能比较两者之间的大小,但是有了initializer_list后,c++11标准库中添加另外一种实现方式:
template <class T> T max (initializer_list<T> il);
template <class T, class Compare>
T max (initializer_list<T> il, Compare comp);
这就说明,c++11之后,max函数可以传递更多的参数,栗子:
cout << max({ 54,16,48,5 }) << endl; //输出54
可以说:initializer_list是某种类型的数组,但是内部数据都是const T类型,可以整体作为参数传递,由{}进行初始化
详述
c++11中统一了初始化列表(Uniform Initiaization),即均可以使用{}来对对象进行初始化。例如:
int value[]{1,2,3};
vector<int> v{2,3,4,5,6,7};
vector<string> cities{"Beijing","Dezhou"};
int i; //i has undefined value
int j{}; //j is initialized by 0
int *p; //p has undfined value
int *q{}; //q is initialized by nullptr
我们使用{}可以给上面进行赋值,那如果是结构体呢?
struct Peo
{
std::string name;
int pos;
};
Peo p[2]{ {"abo",1},{"abo2",2} };
综上,上面的初始化列表背后就是使用initializer_list来进行实现的,比如vector和map的构造函数中新增了这样一种新的构造函数:
vector (initializer_list<value_type> il,
const allocator_type& alloc = allocator_type()); //vector使用initializer_list进行初始化
map (initializer_list<value_type> il,
const key_compare& comp = key_compare(),
const allocator_type& alloc = allocator_type()); //map使用initializer_list进行初始化
这样才支持使用初始化列表来对其进行初始化
并且初始化列表可以放置类型转换精度的损失,比如:
nt x{1.1}; //[Warning] narrowing conversion of '1.1000000000000001e+0' from 'double' to 'int' inside { } [-Wnarrowing] 在dev c++ GCC4.9.2 条件下编译
//可能在其他编译器条件下编译会产生错误
initializer_list部分源码
public:
typedef _E value_type;
typedef const _E& reference;
typedef const _E& const_reference;
typedef size_t size_type;
typedef const _E* iterator;
typedef const _E* const_iterator;
private:
iterator _M_array; //起点指针
size_type _M_len; //总共长度
// The compiler can call a private constructor. 编译器可以调用的构造函数版本
constexpr initializer_list(const_iterator __a, size_type __l)
: _M_array(__a), _M_len(__l) { }
public:
constexpr initializer_list() noexcept //public的构造函数版本
: _M_array(0), _M_len(0) { }
可以看到在initializer_list内部数据部分只包含一个起点指针,以及一个总共长度,在侯捷老师的课程中提及到initializer_list背后是由array实现的,声明一点:The initializer_list object refers to the elements of this array without containing(内含) them(initializer_list并没有内涵这些元素)所以我们在做拷贝的时候,特别注意是否为浅拷贝,并且保存的数据均为const T类型,不可以对数据进行修改
方法
constructor方法 |
构造函数 |
size | 返回initializer_list的长度 |
begin | 开始元素指针 或者使用begin(initializer_list<T> __ils)方法获取 |
end | 结尾元素指针 或者使用 end(initializer_list<T> __ils)方法获取 |
使用
initializer_list对象的构造方式:当编译器看到{t1,t2...tn}遍做出一个initializer_list(同时关联到一个array<T,n> n表示个数),调用函数时,array中的元素可被编译器分解逐一传给参数,但是如果函数参数是一个initializer_list,则会统一传入参数。
for(auto x:{1,2,3,4,5})
cout << x << endl;
使用比较笨拙的方式自己实现了_vector,主要为了说明initializer_list的用法
#include <iostream>
#include <initializer_list>
#include <algorithm>
#include <array>
#include <map>
#include <queue>
using namespace std;
template<typename T>
class _vector
{
public:
_vector(initializer_list<T> _tmp)
{
len = _tmp.size();
_a = new int [ len+1 ];
int index = 0;
for(T _x:_tmp)
_a[index++] = _x;
}
~_vector()
{
delete []_a; //new时使用[],delete中可要使用[]
}
void append(initializer_list<T> _tmp)
{
int newlen = len + _tmp.size();
T *_now = new int [newlen]; //比较笨拙的方法重新开辟空间
copy(_a,_a+len,_now); //之前内容拷贝
delete []_a; //空间释放
for(T _x:_tmp) //内容衔接
_now[len++] = _x;
_a = _now; //将_a指向新空间首地址
}
void show()
{
for_each(_a,_a+len,[](T _tmp){
cout << _tmp << endl;
});
}
private:
T *_a; //直接使用数组模拟实现,实质vector内部内存分配非常巧妙
int len;
};
int main()
{
_vector<int> vec = {1,2,3,4,5};
vec.append({6,7,8});
vec.show();
return 0;
}
参考文献
http://www.cplusplus.com/reference/
侯捷老师关于C++2.0新特性的讲解,视频来自B站 https://www.bilibili.com/video/av20944273,原视频网址 http://boolan.com/