现代c++ 新特性 之 列表初始化

初始化 和 赋值在很多语言中都是没什么区分的。
c++中则很强调初始化的重要性,在初始化上有关的语法很多。

  • c类型的初始化(整型浮点型、指针、数组、struct)
  • 默认、移动、复制、转换、代理、合成的构造函数
  • 构造函数的成员初始值列表constructor( arg ): m_1(arg1), m_2(arg2){ }
  • 类内初值
  • (阻止/显式指定)合成的函数 =default =delete
  • 显示指定子类重写了哪个虚函数 overide
  • 列表初始化(类似初始化数组元素的花括号初始化)

这些语法提供了方便,也带来了选择困难。
类内初值写起来挺方便的,但是阅读代码的时候要检查成员默认值就要检查两个地方了。以往只需要检查构造函数的声明即可,现在还需要检查类成员定义的地方有无提供类内初值。

显示指定函数为delete,就可以阻止编译器合成的版本。典型用于不可复制的对象。以往workaround的方法是把复制构造和赋值符声明为私有,通过产生编译错来阻止类外部使用。并且把函数体留空,通过链接错阻止类内部用。有了新语法就无需以前这种曲折的做法了。

构造函数的成员初始值列表,这个还是很多时候是必要的。 例如类成员是引用、const常量、父类的初始化。 再者为了性能,类成员是需要复杂构造的对象时,若函数体内给初值,相当于在构造成员时用成员的默认构造,然后函数体内用新值赋值,多了一次不必要的开销。

列表初始化(braced initialization syntax) 这个东西当初是为了提供统一的初始化界面引入的。

  • int x{0};
  • int x{};
  • int x=0;
  • int x={0}
  • Rect r1(1920,1080); //初始化长宽
  • Rect r2 = r1; //复制构造
  • Rect r1{1920,1080} //这个形式就有很多种可能了
  • Rect r1( {1920,1080} ) //调用 Rect( std::initializer_list il)

Rect r1{1920,1080}

  • 如果 Rect是聚合类,那么就是按成员顺序初始化前两个成员。
  • 如果 定义了std::initializer_list< T >的转换构造,且1920,1080这些参数可以转换为T类型,则优先选这个构造函数。如果转换失败,才尝试匹配其它版本的构造函数。

也就是说对于类类型的初始化,用{ } 形式会优先匹配 std::initializer_list< T > 形式的构造函数,即使不是最优匹配。
导致初始化时调用的构造函数不是你想要的那个。

因此为了稳妥起见,如果类存在std::initializer_list的构造函数,则Rect r1{1920,1080} 这样的形式少用为妙。最好用圆括号的形式指定调用哪个构造函数。

  • Rect r1 (1920,1080) ; //指定两个参数的构造函数 Rect r1; //默认构造
  • Rect r1 ({1920,1080}) //指定调用std::initializer_list版本的构造
  • vector< int > v(10,20) //十个元素的vector,每个是20
  • vector< int > v( {10,20} ) //2个元素的vector

正如Meyers《effective modern c++》所说,两种风格各有优缺,选一种用下去就好了。

{ }的风格有一种好处,类型缩窄截断的发生会报错,例如 int x{5.1};浮点型转整型就是缩窄了。
花括号的初始化也可以用在构造函数的初始值列表中,constructor( arg ): m_1{arg1}, m_2{arg2} { }
更多细节 direct list initialization and copy list initialization


至于 initializer_list 具体是个什么东西?按vector( initializer_list il) 的声明来看应当是一个只读的序列,不应当存储参数本身,而是参数的只读引用/指针,避免过多开销。

在vs2017中, 工具>选项>调试>常规>Just My Code 关闭掉,即可单步进入vector的构造函数。单步的结果是会根据{} 内的参数构造出一个临时数组,然后initializer_list 保存的是临时数组的 [ first, last )区间指针, 然后通过移动构造逐个把元素塞进vector中 或者 memmove 整块移过去

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(nullptr), _Last(nullptr)
		{	// empty list
		}

	constexpr initializer_list(const _Elem *_First_arg,
		const _Elem *_Last_arg) noexcept
		: _First(_First_arg), _Last(_Last_arg)
		{	// construct with pointers
		}

	_NODISCARD constexpr const _Elem * begin() const noexcept
		{	// get beginning of list
		return (_First);
		}

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

	_NODISCARD constexpr size_t size() const noexcept
		{	// get length of list
		return (static_cast<size_t>(_Last - _First));
		}

private:
	const _Elem *_First;
	const _Elem *_Last;
	};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值