int i_arr[3]={1,2,3}; //数组 struct A{ int x; struct B{ int i,j; }b; }a={1,{2,3}}; //POD类型 //拷贝初始化 int i=0; class Foo{ public: Foo(int){} }foo=123; //直接初始化 int i(0) ; Foo bar(123);
这些不一样的初始化方法,都有各自的适用范围和做用。最关键的是,这些种类繁多的初始化方法,没有一种能够通用全部状况。html
为了统一初始化方式,而且让初始化行为具备肯定的效果,c++11里提出了列表初始化的概念。c++
class Foo{ public: Foo(int){} private: Foo(const Foo&){} }; int main(){ Foo a1(123); Foo a2=123; //error: 'Foo::Foo(const Foo&)' is private Foo a3={123}; Foo a4{123}; int a5={3}; int a6{3}; }
上例中,a三、a4 使用了新的初始化方式来初始化对象,效果如同a1的直接初始化;其实这在c98/03里也有使用,可是不支持全部类型,只支持POD类型和数组类型。数组
在初始化时,{ }前面的等于号是否书写对初始化行为没有影响。函数
上面并无 new操做符的影子,咱们也能够试一试指针
int *c = new int (23); //指针c 初始化为 23; int *a = new int { 123}; //指针a使用初始化列表的方式在内存初始化时指定了值为123; double b = double {12.12}; //指针 b 是对匿名对象使用列表初始化后,再进行拷贝初始化。 int *arr = new int[3]{1,2,3}; //初始化一个数组,并用初始化列表的方式进行初始化。
列表初始化还能够用在函数的返回值上:c++11
struct Foo{ Foo(int , double ){} }; Foo fun(void){ return { 123, 321.0} };
这里return就如同返回了一个Foo( 123, 321.0)。code
其实,上述类和结构体的例子能使用初始化列表,是由于它们是聚合类型。什么的类型C++会认为它是一个聚合体呢?htm
下面来看看定义:对象
(1)类型是一个普通数组。继承
(2)类型是一个类,且
例如:
struct Foo{ int x; int y; private: double z; } a= {1,2,3.4}; //error 这有私有函数
struct ST{ int x1; double y1; virtual void f(){} }s={1,2.5}; //error virtual函数
struct Base{}; struct Foo: public Base{ int x; double y; } foo = {1,2.5}; //error 继承
struct Foo{ int x; double y; int z; Foo(int ,int ){} }foo{1,2.3,4}; //error 自定义构造函数
struct Foo{ int x; double y=0.0 }foo{1,2.3}; //error y 已经直接初始化当不是聚合类型时,那么就须要自定义一个构造函数。
列表初始化还有一个很重要的做用,那就是防止类型收窄。
何为类型收窄,类型收窄指的是致使数据内容发生变化或精度丢失的隐式类型转换。如:
一、从一个浮点数隐式转换为一个整数,如 int a = 2.2;
二、从高精度浮点数转换为低精度浮点数,如从long double 隐式转换成 double 或 float 类型。
三、从一个整型数隐式转换成一个浮点数,而且超出了浮点数范围,如 float x = ( unsigned long long) - 1;
四、从一个整型数隐式转换为一个长度较短的整型数,而且超出了长度较短的整形术的表示范围,如char x=65536。
在c++中,上面所示的类型收窄并不会报错。这每每会致使一些隐藏的错误。在c++11中,能够经过列表初始化来检查即防止类型收窄。(部分编译器可能不会报错,但会有警告)int a= 1.1; int b = { 1.11}; //error float fa = 1e40; float fb = { 2e40}; //error