天天看点

C++11之列表初始化统一的初始化使用细节初始化列表

文章目录

  • 统一的初始化
  • 使用细节
  • 初始化列表
    • 任意长度的初始化
    • 防止类型收窄

统一的初始化

       对于普通数组和POD类型(可以直接memcpy复制的对象)C++98/03可以使用初始化列表进行初始化,只有这两种数据类型可以使用初始化列表。

int arr[3]={1,2,3};
struct A
{
	int x;
	int y;
} a = {1,2};
           

       在C++11中,初始化列表可以用于任何类型对象的初始化

       在C++11中,可以直接在变量名后面跟上初始化列表,来进行对象的初始化。这是C++98/03锁不具备的。

       new操作符等可以用圆括号进行初始化的地方,也可以使用初始化列表:

int *a = new int {123};
double b = double{12.12};//对匿名对象使用初始化列表后,再进行拷贝初始化
int *arr = new int[3] {1,2,3};//堆上动态分配的数组也可以使用初始化列表进行初始化
           

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

struct Foo
{
	Foo(int, double){}
};
Foo func(void)
{
	return {123, 12.12};
}
           

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

使用细节

       聚合类型的定义:

       (1)类型是一个普通数组(如int[10]、char[]、long[2][3])。

       (2)类型是一个类(class、struct、union),且

  • 无用户定义的构造函数
  • 无私有或保护的非静态数据成员
  • 无基类
  • 无虚函数
  • 不能有{ }和=直接初始化的非静态数据成员
struct Foo
{
	int x;
	Foo(int){};
};
Foo foo {1};  //error  此时必须以自定义的构造函数来构造对象
           
struct A
{
	int x;
	double y;
protected:
	int z;
};
A a {1, 1.2, 3}; // error私有或保护的非静态数据成员

struct Foo
{
	int x;
	double y;
protected:
	static int z;
};
Foo foo {1, 1.2}; //ok
           

       此时Foo的初始化是成功的,因为它受保护成员是一个静态数据成员。Foo中的静态成员是不能通过实例foo的初始化列表进行初始化的,它的初始化遵循静态成员的初始化方式。

       有虚函数或基类的类都不能使用列表初始化。

       最后介绍“不能有{ }和=直接初始化的非静态数据成员”这条规则:

struct Foo
{
 	int x;
 	double y = 0.0;
};
Foo foo{1, 1.2};   //error
           

       在Foo中,y在声明的时候即被=直接初始化为0.0,因此,Foo不是一个聚合类型,不能直接使用初始化列表。在C++11中,非静态数据成员也可以在声明的同时进行初始化工作(即使用{ }或 = 进行初始化)。

       对于非聚合类型的情形,想要使用初始化列表的方法就是自定义一个构造函数,比如:

struct ST
{
	int x;
	double y;
	virtual void F(){}
private:
	int z;
public:
	ST(int a, double b, int c):x(a),y(b),z(c) {}
};
ST s {1, 1.2, 3};
           

       需要注意的是,聚合类型的定义并非递归的。

struct ST
{
 	int x;
 	double y ;
 private:
 	int z;
};
struct Foo
{
	ST s;
	int x;
	double y;
};
Foo foo {{}, 1, 1.2};    //ok
           

       ST是一个非聚合类型。Foo含有非聚合类的非静态成员s,它仍然是一个聚合类型,可以使用初始化列表,一对空的大括号“{}”,相当于调用ST的无参构造函数。

初始化列表

任意长度的初始化

int arr[] = {1, 2, 3};
std::map<string,int> = {{"1",1}, {"2",2}, {"3",3}};
std::set<int> ss = {1, 2, 3};
std::vector<int> v = {1, 2, 3, 4, 5};
           

       自定义的Foo却没有这种能力,实际上,stl中的容器是通过使用std::initalizer_list这个轻量级的模板来完成上述功能支持的。我们只需要对Foo添加一个std::initializer_list构造函数,也是可以拥有这种任意长度初始化的能力。

class Foo
{
	public:
		Foo(std::initializer_list<int>) {}
};
Foo foo = {1,2,3,4,5};    //ok
           

防止类型收窄

       类型收窄包括以下几种情况:

  • 从一个浮点数隐式转换为整型数,如 int ii = 2.2;
  • 从一个高精度浮点数隐式转化为低精度浮点数,如从long double隐式转换为double或float;
  • 从一个整型数隐式转换为一个浮点数,并且超出了浮点数的表示范围,如 float x = (unsigned long long)-1;
  • 从一个整型数隐式转换为一个长度较短的整型数,并且超出了长度较短的整型数的表示范围,如 char x = 65536;

           在C++98中,上述这种类型收窄的情况,编译器不会报错,或者报一个警告,早C++11中们可以通过使用列表初始化来防止类型收窄。

int a = 1.1;                                          //ok
int b = {1.1};                                        //error
float fa = 1e40;                                      //ok
float fb = {1e40};                                    //error
           

继续阅读