C++11——一致性初始化(Uniform initialization)、初始化列表(Initializer List)

在C++11之前,初始化的形式多样,包括小括号、大括号以及赋值运算符,例子如下:

class Base 
{
public:
	Base(int a, int b, int(*area)(int, int)) { cout<<area(a, b)<<endl; }
};
int area(int a, int b) 
{
	return a*b;
}
......
void test()
{
	int a=1;//使用赋值运算符初始化
	int b(1);//使用小括号初始化
	int c[]{1,2,3};//使用大括号初始化
	Base base={a,b,&area};//使用大括号完成初始化(这里调用大构造函数,输出2)
	return 0
}

在C++11之后,为了方便,各种数据类型可以统一使用大括号完成初始化。只是虽然形式上一样,但底层的实现原理有所不同。
这里基于一个事实:当编译器看到大括号——{t1,t2…tn},会自动做出一个初始化列表——initializer_list< T>,该列表关联到标准库提供的容器——array<T,n>,T为大括号中元素类型,n为元素个数。
调用函数(通常为构造函数)完成初始化时,array中的元素会被编译器被逐一传递给函数。
若用于初始化的构造函数有一个重载版本的函数参数是initializer_list<T>,那么{}中的数值就不会逐一传递,而是整包一次性传递过去。

vector<string> city{"Beijing","Nanjing","Shanghai"};//这里会形成一个initializer_list<string>,背后关联一个array<string,3>,在调用vector<string>的构造函数完成初始化时,会找到一个接受initializer_list<string>的构造函数(C++所有容器都有这样一个构造函数),整包数据一起传递过去
complex<double> com(4.0,3.0);//在复数类complex<double>中并没有接受initializer_list<string>的构造函数,因此调用方式为:形成一个initializer_list<double>,背后关联一个array<double,2>,array中的两个元素会被分解逐一传递给构造函数
class Base {
public:
	Base(int a, int b) { cout << "①不接受initializer_list:" << a + b << endl; }
	Base(initializer_list<int> i) { cout << "②接受initializer_list:" << *(i.begin())+ *(i.begin() + 1) << endl; }
	Base(int a, int b, int(*area)(int, int)) { cout<<"③不接受initializer_list:"<<area(a, b)<<endl; }
	Base(initializer_list<int> i, int(*area)(int, int)) { cout <<"④接受initializer_list:"<< area(*(i.begin()), *(i.begin()+1)) << endl; }
};
int area(int a, int b) {
	return a*b;
}
void main()
{
	int a = 1;
	int b( 2 );
	Base base1{ a,b,&area };
	Base base5{ initializer_list<int>{a,b},&area };//或者Base base5{ {a,b},&area };也可以
	Base base2( 1, 2 );
	Base base3{ initializer_list<int>{1, 2} };
	Base base4{ 1, 2 };//这里假如没有重载版本②的构造函数,那么编译器会调用版本①的构造函数(编译器会将初始化列表中的两个参数拆开传给版本①的构造函数)
}

在这里插入图片描述
使用{}进行初始化需要注意两点:
①编译器自动赋初值:

int a;//未初始化,在没有赋值前无法使用
int b{};//同样未初始化,但编译器会自动赋初值为0,可以直接使用
int* c;//未初始化,指向非法内存(野指针,不安全)
int* d{};//编译器自动赋初值为nullptr,是安全的

2、无法实现精度收缩窄化转换

int x1(1.1);//编译可以通过,但x1变为5
int x2=1.1;//编译可以通过,但x1变为5
int x3{1.1};//在VS(VC编译器)下编译报错,无法实现收缩窄化转换,但在GCC编译器下仅仅是警告
int x4=1.1;//在VS(VC编译器)下编译报错,无法实现收缩窄化转换,但在GCC编译器下仅仅是警告

使用{}实现初始化背后是靠initializer_list<>做支撑,initializer_list<>背后是array在做支撑
initializer_list的源代码如下,其构造函数通过浅拷贝初始化头尾指针,指向编译器通过标准库提供的array定义的数组的首和尾。

template<class _Elem>
class initializer_list
{	// list of pointers to elements
public:
/*
	typedef _Elem value_type;
	typedef const _Elem& reference;
	typedef const _Elem& const_reference;
	typedef size_t size_type;

	typedef const _Elem* iterator;
	typedef const _Elem* const_iterator;

	constexpr initializer_list() _NOEXCEPT
		: _First(0), _Last(0)
		{	// empty list
		}
*/
    //这里使用浅拷贝获取数组的头与尾指针
	constexpr initializer_list(const _Elem *_First_arg,
		const _Elem *_Last_arg) _NOEXCEPT
		: _First(_First_arg), _Last(_Last_arg)
		{	// construct with pointers
		}
/*
	constexpr const _Elem *begin() const _NOEXCEPT
		{	// get beginning of list
		return (_First);
		}

	constexpr const _Elem *end() const _NOEXCEPT
		{	// get end of list
		return (_Last);
		}

	constexpr size_t size() const _NOEXCEPT
		{	// get length of list
		return ((size_t)(_Last - _First));
		}
*/
private:
	const _Elem *_First;//指向数组的头
	const _Elem *_Last;//指向数组的尾
};
/*
		// TEMPLATE FUNCTION begin
template<class _Elem> inline
	constexpr const _Elem *begin(initializer_list<_Elem> _Ilist) _NOEXCEPT
	{	// get beginning of sequence
	return (_Ilist.begin());
	}

		// TEMPLATE FUNCTION end
template<class _Elem> inline
	constexpr const _Elem *end(initializer_list<_Elem> _Ilist) _NOEXCEPT
	{	// get end of sequence
	return (_Ilist.end());
	}
*/

在C++的STL中大量使用到了initializer_list,主要是其可以接受任意数目的参数给用户提供了很大的方便。以vector为例:


class vector{
...
public:
	//构造函数
	vector(initializer_list<value_type> _l,const allocator_type& _a=allocator_type()):_Base(_a){
		_M_range_initialize(_l.begin(),_l.end(),random_access_iterator_tag());
	}
	//赋值运算符重载
	vector& operator=(initializer_list<value_type> _l){
		this->assign(_l.begin(),_l.end());
		return *this;
	}
	//插入操作
	void insert(iterator _position,initializer_list<value_type> _l){
		this->insert(_position,_l.begin(),_l.end());
	}	
...
};
void test()
{
	vector<int> v1{1,2,3,4,5};//调用构造函数
	vector<int> v2({1,2,3,4,5});//调用构造函数
	vector<int> v3;
	v3={1,2,3,4,5};//调用赋值运算符
	v3.insert(v3.begin()+2,{6,7,8});//插入操作
}

以算法min和max为例,旧版本的min和max只接受两个参数,而新版本(C++2.0以后)可以通过initializer_list接受任意多参数:

template<typename _Tp>
inline _Tp min(initializer_list<_Tp> _l)
{
	return *std::min_element(_l.begin(),_l.end());
}
template<typename _Tp>
inline _Tp max(initializer_list<_Tp> _l)
{
	return *std::max_element(_l.begin(),_l.end());
}
...
void test()
{
	cout<<min({string("1","2","3","4")})<<endl;
	cout<<max({1,2,3,4})<<endl;
}

这里多提一嘴,VS2015默认支持C++11的部分功能,但上面的**max({1,2,3,4})**却不支持,而且也无法配置。如果是VS2017就完全支持C++11,具体的配置方法参考博客

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值