1.3.1 统一的初始化
C++11中,初始化列表可以用于任何类型对象的初始化。
int *a = new int {123};
double b = double { 12.12 };
int *arr = new int[3] { 1, 2, }
列表初始化可以直接用在函数的返回值上。
struct Foo
{
Foo(int, double){}
};
Foo func(void)
{
return { 123, 321.0 };
}
1.3.2 列表初始化的使用细节
聚合类型的定义:
-
类型是一个普通数组(如int[10]、char[]、long [2] [3])
-
类型是一个类(class、struct、union),且:
-
无用户自定义的构造函数
-
无私有或保护的非静态数据成员
-
无基类
-
无虚函数
-
不能有 { } 和 = 直接初始化的非静态数据成员
-
有用户自定义的构造函数:
struct Foo
{
int x;
double y;
int z;
Foo(int, int) {}
};
Foo foo {1, 1.5, 1}; //error
有私有或保护的非静态成员:
struct Foo
{
int x;
double y;
protected:
int z;
};
Foo foo {1, 1.5, 1}; //error
Foo foo {1,1.5}; //ok
有基类或者虚函数:
struct ST
{
int x;
double y;
virtual void F()
};
ST st {1, 2.5}; //error
struct Base {};
struct Foo : public Base
{
int x;
double y;
}
Foo foo {1, 2.5}; //error
有 { } 或者 = 直接初始化的非静态成员:
struct ST
{
int x;
double y = 0.0;
};
ST st {1, 2.5}; //error
对于非聚合类型的初始化列表,需要自定义构造函数:
struct ST
{
int x;
double y;
virtual void F() {}
private:
int z;
public:
ST(int i, double j, int k) : x(i), y(j), z(k) {}
};
ST st {1, 2.5, 2}; //OK
当一个类的非静态成员是非聚合类型时,这个类也有可能是聚合类型:
struct ST
{
int x;
double y;
private:
int z; //非聚合类型因为有private的非静态成员
}
struct Foo
{
ST st;
int x;
double y;
}; //虽然成员ST为非聚合类型,但Foo仍然是一个聚合类型
Foo foo { {} , 1, 2.5}; //OK,相当于调用了ST的无参构造函数
1.3.3 初始化列表(std::initializer_list)
通过初始化列表给自定义容器赋值,也可以传递同类型数据的集合:
class Foo
{
private:
std::vector<int> content;
public:
Foo(std::initializer_list<int> list)
{
for(auto it = list.begin(); it != list.end(); ++it)
{
content.push_back(*it);
}
}
};
Foo foo = { 1,2,3,4,5 };
std::initializer_list的细节:
- 是一个轻量级的容器类型,内部定义了容器必需的概念。
- 可以接收任意长度的初始化列表,但要求元素必须是同种类型。
- 有三个成员接口: size() , begin() , end()。
- 遍历时取得的迭代器时只读的,列表只能被整体初始化或赋值。
- 拥有无参构造函数,可以直接定义示例。
- 内部并不负责保存初始化列表中元素的拷贝,只保存引用,所以不能用于存储生命周期之外的参数。解决方案:在函数中用具有转移拷贝语义的物件,真正的容器来替代std::initializer_list来返回需要的结果。
1.3.4 防止类型收窄
类型收窄:导致数据内容变化或者精度丢失的隐式类型转换。
- 从浮点数隐式转换为整数。
- 从高精度浮点数转换为低精度浮点数。
- 从整数转换为浮点数,但超过了浮点数的表示范围。
- 从整数转换为长度较短的整数。
通过列表初始化防止类型收窄:
int a = 1.1; //OK
int a = { 1.1 }; //error