文章目录
- 统一的初始化
- 使用细节
- 初始化列表
-
- 任意长度的初始化
- 防止类型收窄
统一的初始化
对于普通数组和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