关于 C++ 中的变量、数组、对象等都有不同的初始化方法,这些繁琐的初始化方法中没有任何一种方式适用于所有的情况。为了统一初始化方式,并且让初始化行为具有确定的效果,在 C++11 中提出了列表初始化的概念。
统一的初始化 在 C++98/03 中,对应普通数组可以直接进行内存拷贝 memcpy() 的对象,可以使用列表初始化来初始化数据。
1 2 3 4 5 6 7 8 9 10 int  array[] = { 1 ,3 ,5 ,7 ,9  };double  array1[3 ] = { 1.2 , 1.3 , 1.4  };struct  Person int  id;double  salary;1 , 3000  };
在 C++11 中,列表初始化变得更加灵活,初始化类对象的示例代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include  <iostream>  using  namespace  std;class  Test public :Test (int ) {}private :Test (const  Test &);int  main () Test t1 (520 )  ;520 ;520  };520  };int  a1 = { 1314  };int  a2 { 1314  };int  arr1[] = { 1 , 2 , 3  };int  arr2[] { 1 , 2 , 3  };return  0 ;
分析示例代码中使用的各种初始化方式:
使用列表初始化可以对普通类型以及对象进行直接初始化,那么使用 new 操作符创建新对象的时候使用列表初始化进行对象的初始化吗?
1 2 3 int * p = new  int  {520 };          double  b = double  {52.134 };      int * array = new  int [3 ] {1 ,2 ,3 }; 
除此之外,列表初始化还可以用在函数返回值,示例代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include  <iostream>  #include  <string>  using  namespace  std;class  Person public :Person (int  id, string name)"id: "  << id << ", name: "  << name << endl;Person func ()  return  { 9527 , "华安"  };int  main () func ();return  0 ;
代码中的 return { 9527, "华安" }; 相当于 return (9527, "华安" );,直接返回了一个匿名对象。可以看出在 C++11 使用列表初始化是非常方便的,它统一了各种对象的初始化方式,而且代码的书写更加清晰。
列表初始化细节 聚合体 在 C++11 中,列表初始化的使用范围被大大增强了,但是一些模糊的概念也随之而来。列表初始化可以用于自定义类型的初始化,但是对于一个自定义类型,列表初始化可能有两种执行结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include  <iostream>  #include  <string>  using  namespace  std;struct  T1 int  x;int  y;123 , 321  };struct  T2 int  x;int  y;T2 (int , int ) : x (10 ), y (20 ) {}123 , 321  };int  main () "a.x: "  << a.x << ", a.y: "  << a.y << endl;"b.x: "  << b.x << ", b.y: "  << b.y << endl;return  0 ;
示例代码中都是使用列表初始化的方式对对象进行了初始化,但是得到结果却不同。为什么对象 b 并没有被初始化列表中的数据初始化?
使用列表初始化时,对于什么样的类型 C++ 会认为它是一个聚合体?
1 2 3 4 5 6 7 8 int  x[] = {1 ,2 ,3 ,4 ,5 ,6 };double  y[3 ][3 ] = {1.23 , 2.34 , 3.45 },4.56 , 5.67 , 6.78 },7.89 , 8.91 , 9.99 },char  carry[] = {'a' , 'b' , 'c' , 'd' , 'e' , 'f' };"hello" , "world" , "nihao" , "shijie" };
(2)满足以下条件的类(class、struct、union)可以看做是一个聚合类型
非聚合体 对于聚合类型的类可以直接使用列表初始化进行对象的初始化。如果不满足聚合条件想使用列表初始化其实也可以,需要在类的内部自定义一个构造函数,在构造函数中使用初始化列表对类成员变量进行初始化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #include  <iostream>  #include  <string>  using  namespace  std;struct  T1 T1 (int  a, double  b, int  c) : x (a), y (b), z (c) {}virtual  void  print ()      {"x: "  << x << ", y: "  << y << ", z: "  << z << endl;private :int  x;double  y;int  z;int  main (void ) 520 , 13.14 , 1314  }; print ();return  0 ;
另外需要注意的是聚合类型的定义并非递归的,也就是说当一个类的非静态成员是非聚合类型时,这个类也可能是聚合类型,示例代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #include  <iostream>  #include  <string>  using  namespace  std;struct  T1 int  x;double  y;private :int  z;struct  T2 long  x1;double  y1;int  main (void ) 520 , 13.14  };return  0 ;
T1 并非一个聚合类型,因为它有一个 Private 的非静态成员。但是 T2 依然是一个聚合类型,可以直接使用列表初始化的方式进行初始化。
std::initializer_list 在 C++ 的 STL 容器中,可以进行任意长度的数据的初始化,使用初始化列表也只能进行固定参数的初始化。如果想要做到和 STL 一样有任意长度初始化的能力,可以使用 std::initializer_list 这个轻量级的类模板来实现。
作为普通函数的参数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 #include  <iostream>  #include  <string>  using  namespace  std;void  traversal (std::initializer_list<int > a) for  (auto  it = a.begin (); it != a.end (); ++it)" " ;int  main () int > list;"current list size: "  << list.size () << endl;traversal (list);1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,0  };"current list size: "  << list.size () << endl;traversal (list);1 ,3 ,5 ,7 ,9  };"current list size: "  << list.size () << endl;traversal (list);traversal ({ 2 , 4 , 6 , 8 , 0  });return  0 ;
std::initializer_list 拥有一个无参构造函数,它可以直接定义实例,此时将得到一个空的 std::initializer_list,在遍历这种类型的容器的时候得到的是一个只读的迭代器,因此不能修改里边的数据,只能通过值覆盖的方式进行容器内部数据的修改。虽然如此,在效率方面也无需担心,std::initializer_list 的效率是非常高的,它的内部不负责保存初始化列表中元素的拷贝,只s存储了初始化列表中元素的引用。
作为构造函数的参数 自定义的类如果在构造对象的时候要接收任意个数的实参,可以给构造函数指定为 std::initializer_list 类型,在自定义类的内部还是使用容器来存储接收的多个实参。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #include  <iostream>  #include  <string>  #include  <vector>  using  namespace  std;class  Test public :Test (std::initializer_list<string> list)for  (auto  it = list.begin (); it != list.end (); ++it)" " ;push_back (*it);private :int  main (void ) Test t ({ "jack" , "lucy" , "tom"  })  ;Test t1 ({ "hello" , "world" , "nihao" , "shijie"  })  ;return  0 ;
参考资料 https://subingwen.cn/cpp/list-init/