据我所知,在c++中有三种初始化变量的方法。
int x = 0; // C-like initialization
int x (0); // Constructor initialization
int x {0}; // Uniform initialization
统一初始化是c++ 11引入的,目的是为初始化不同类型的变量提供更统一的语法,这在c++ 03中需要不同的语法。
类c、构造函数和统一初始化之间的区别是什么?我应该总是使用统一初始化吗?首先,我建议大家看一下Herb Sutter的演讲,他在演讲中给出了一些关于这个主题的建议。大括号初始化讨论在23:00左右开始。
当您谈论基本数据类型时,所有3种类型都会产生相同的结果。我个人更喜欢坚持使用旧的int x = 0
语法,但这取决于个人偏好。
对于类类型,大括号初始化和老式构造函数初始化不能完全互换。例如:
vector<int> v (100); // Creates a 100-element vector
vector<int> v {100}; // Creates a 1-element vector, holding the value 100.
这是因为std::vector
有一个构造函数,它显式地将std::initializer_list
定义为它的唯一参数。请记住
auto var = {1, 2};
创建一个std::initializer_list
, var
作为它的标识符。
关于初始化列表的事情是,它们提供了一致性,这是一个受欢迎的变化,而不是之前可用的。例如,如果要在c++中初始化一个数组,可以使用:
int arr[] = {1, 2, 3, 4};
但是,如果您想用相同的元素初始化vector<int>
,则必须:
- 先初始化上述arr,然后传递
arr
和arr + 4
- 分别创建vector和push_back(),或者在循环中创建元素。
在c++ 11中,您可以使用
vector<int> v = {1, 2, 3, 4}; // Same syntax. Nice! Note that the = is optional
大括号初始化的另一个有用的实例是,它为c++中最令人烦恼的解析提供了一个解决方案。从谈话中,假设我们有两个类,origin
和extents
,它们的实例可以被传递来构造另一个类型为rectangle
的对象。下面的语句:
rectangle w(origin(), extents());
不允许您使用origin
和extents
临时创建rectangle
对象,因为该语句被解析为函数声明。啧啧啧啧。通常,你需要输入:
origin o;
extents e;
rectangle w(o, e);
使用大括号初始化,您可以动态地创建它们,而
rectangle w {origin(), extents()};
将按预期工作,即传递给构造函数,该构造函数重载origin
对象作为第一个参数,extents
对象作为第二个参数。
类c、构造函数和统一初始化之间的区别是什么?
对于像int
这样的基本类型,没有实际的区别;因此,让我们考虑一个类类型为T
。
第一种样式相当于
T x(T(0));
从初始化表达式创建一个临时对象,然后通过移动或复制来初始化x
。在实际操作中,将移或复制的部分省略掉,使其结果与第二种风格相同;唯一的区别是,如果没有可访问的复制或移动构造函数,第一个操作将失败。
第二个函数使用一个带一个实参的构造函数直接初始化对象,如果没有合适的构造函数则给出错误。
第三个取决于可用的构造函数。
- 如果有一个构造函数取
std::initializer_list
,它使用; - 否则,如果有一个构造函数接受一个合适类型的参数,它使用;
- 否则,如果它是只有一个成员的聚合(没有构造函数),则该成员初始化为0;
我应该总是使用统一初始化吗?
。有时需要函数式初始化来区分initializer_list
构造函数和接受其他参数类型的构造函数。例如:
std::vector<int> v1(10, 42); // 10 elements with value 42
std::vector<int> v2{10, 42}; // 2 elements with values 10 and 42
你也不应该称它为"统一初始化",因为它在任何意义上都不是"统一"的。官方术语是"大括号初始化"