在C++ 11以前,全部对象的初始化方式是不一样的,常常让写代码的咱们感到困惑。C++ 11努力创造一个统一的初始化方式。
其语法是使用{}和std::initializer_list
int values[]{ 1, 2, 3 }; std::vector<int> v{ 2, 3, 6, 7 }; std::vector<std::string> cities{ "Berlin", "New York", "London", "Braunschweig" }; std::comples<double> c{4.0, 3.0}; //等价于 c(4.0, 3.0) auto ar = { 1, 2, 3 }; // ar 是一个std::initializer_list<int>类型 std::vector<int> v = { 1, 2, 3 }; std::list<int> l = { 1, 2, 3 }; std::set<int> s = { 1, 2, 3 }; std::map<int, std::string> m = { {1, "a"}, {2, "b"} };
针对形如"{ 1, 2, 3 }"的参数列表,系统会首先自动调用参数初始化(value initialization),将其转换成一个std::initializer_list
咱们经过一个例子来分析具体细节:
std::vector<int> v{ 2, 3, 6, 7 };
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) { }
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()); }
若是使用了std::initializer_list
int i; //i值未定义 int j{}; //j=0 int *p; //p值未定义 int *q{}; //q=nullptr
咱们能够在构造函数中主动使用std::initializer_list
class P { public: P(int, int){std::cout<<"call P::P(int,int)"<<std::endl;} P(std::initializer_list<int>){ std::cout<<"call P::P(initializer_list)"<<std::endl; } }; P p(77,5); // call P::P(int,int) P q{77,5}; // call P::P(initializer_list) P r{77,5,42}; // call P::P(initializer_list) P s = {77, 5}; // call P::P(initializer_list)
咱们知道,C++会先使用{}中的参数生成一个std::initializer_list
有一种特殊情形,咱们若是但愿对象的某个构造函数必需要被显示调用,如何作到呢?向其中添加一个explicit关键字。在stl库中出现大量的这种使用方式。请看下例:
class P { public: P(int a, int b){...} explicit P(int a, int b, int c){...} }; P x(77,5); //OK P y{77,5); //OK P z{77,5,42}; //OK p v = {77,5}; //OK,(implicit type conversion allowed) P w = {77,5,42};//Error,必需要显示调用该构造函数 void fp(const P&); fp({47,11}); //OK fp({47,11,3}); //Error fp(P{47,11}); //OK fp(P{47,11,3}); //OK
统一初始化用起来很舒爽,那它有什么局限呢?
有,在一种场景下没法使用,那就是Narrowing Initializations。
Narrowing Initializations,我翻译为“精度截断”。好比float转换为int,double转换为float。统一初始化,彻底不容许精度阶段的发生,更进一步,要求参数列表中的全部参数的精度同样。请看如下示例。orm
int x1(5.3); //OK, x1 = 5.3 int x3{5.0}; //Error int x4 = {5.3}; //Error char c1{7}; //OK char c2{99999}; //Error std::vector<int> v1{ 1, 2, 4, 5}; //OK std::vector<int> v2{ 1, 2,3, 4, 5.6};//Error
可是若是实际工程容许精度截断的发生,那么咱们应该怎么完成初始化。可使用()来完成初始化,它会调用赋值操做或者相应的构造函数。对象
int x3{5.0}; //Error int x2(5.2); //OK, x2 = 5