列表初始化

在c++98/03里有很多的对象初始化的方法:
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);

这些不同的初始化方法,都有各自的适用范围和作用。最关键的是,这些种类繁多的初始化方法,没有一种可以通用所有情况。

为了统一初始化方式,并且让初始化行为具有确定的效果,c++11里提出了列表初始化的概念。

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};
}

上例中,a3、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};            //初始化一个数组,并用初始化列表的方式进行初始化。

列表初始化还可以用在函数的返回值上:

struct Foo{

    Foo(int , double ){}

};

Foo fun(void){ return { 123, 321.0} };

这里return就如同返回了一个Foo( 123, 321.0)。

其实,上述类和结构体的例子能使用初始化列表,是因为它们是聚合类型。什么的类型C++会认为它是一个聚合体呢?

下面来看看定义:

(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 已经直接初始化
当不是聚合类型时,那么就需要自定义一个构造函数。

列表初始化还有一个很重要的作用,那就是防止类型收窄。

何为类型收窄,类型收窄指的是导致数据内容发生变化或精度丢失的隐式类型转换。如:

1、从一个浮点数隐式转换为一个整数,如 int a = 2.2;

2、从高精度浮点数转换为低精度浮点数,如从long double 隐式转换成 double 或 float 类型。

3、从一个整型数隐式转换成一个浮点数,并且超出了浮点数范围,如 float x = ( unsigned long long) - 1;

4、从一个整型数隐式转换为一个长度较短的整型数,并且超出了长度较短的整形术的表示范围,如char x=65536。

在c++中,上面所示的类型收窄并不会报错。这往往会导致一些隐藏的错误。在c++11中,可以通过列表初始化来检查即防止类型收窄。(部分编译器可能不会报错,但会有警告)
int a= 1.1;            
int b = { 1.11};        //error
float fa = 1e40;
float fb = { 2e40};        //error



你可能感兴趣的:(c++)