13.9 — 默认成员初始化

当我们定义一个结构体(或类)类型时,我们可以为每个成员提供一个默认的初始化值,作为类型定义的一部分。对于未标记为 static 的成员,这个过程有时被称为非静态成员初始化。初始化值被称为默认成员初始化器

相关内容

我们在课程 15.6 -- 静态成员变量 中讨论静态成员和静态成员初始化。

这是一个例子

struct Something
{
    int x;       // no initialization value (bad)
    int y {};    // value-initialized by default
    int z { 2 }; // explicit default value
};

int main()
{
    Something s1; // s1.x is uninitialized, s1.y is 0, and s1.z is 2

    return 0;
}

在上面的 Something 定义中,x 没有默认值,y 默认进行值初始化,z 的默认值为 2。当用户在实例化 Something 类型的对象时没有提供显式初始化值时,将使用这些默认成员初始化值。

我们的 s1 对象没有初始化器,因此 s1 的成员被初始化为它们的默认值。s1.x 没有默认初始化器,因此它保持未初始化。s1.y 默认进行值初始化,因此它获得值 0。而 s1.z 用值 2 初始化。

请注意,尽管我们没有为 s1.z 提供显式初始化器,但由于提供了默认成员初始化器,它被初始化为一个非零值。

关键见解

使用默认成员初始化器(或我们稍后将介绍的其他机制),结构体和类即使在没有提供显式初始化器的情况下也能自初始化!

致进阶读者

CTAD(我们将在课程 13.14 -- 类模板参数推导 (CTAD) 和推导指南 中介绍)不能用于非静态成员初始化。

显式初始化值优先于默认值

列表初始化器中的显式值总是优先于默认成员初始化值。

struct Something
{
    int x;       // no default initialization value (bad)
    int y {};    // value-initialized by default
    int z { 2 }; // explicit default value
};

int main()
{
    Something s2 { 5, 6, 7 }; // use explicit initializers for s2.x, s2.y, and s2.z (no default values are used)
   
    return 0;
}

在上述情况下,s2 为每个成员都有显式初始化值,因此完全不使用默认成员初始化值。这意味着 s2.xs2.ys2.z 分别初始化为值 567

存在默认值时初始化列表中缺少初始化器

在上一课 (13.8 -- 结构体聚合初始化) 中,我们注意到,如果一个聚合被初始化,但初始化值的数量少于成员的数量,那么所有剩余的成员都将进行值初始化。但是,如果为给定成员提供了默认成员初始化器,则将使用该默认成员初始化器。

struct Something
{
    int x;       // no default initialization value (bad)
    int y {};    // value-initialized by default
    int z { 2 }; // explicit default value
};

int main()
{
    Something s3 {}; // value initialize s3.x, use default values for s3.y and s3.z
   
    return 0;
}

在上述情况下,s3 用空列表进行列表初始化,因此所有初始化器都缺失。这意味着如果存在默认成员初始化器,则将使用它,否则将进行值初始化。因此,s3.x(没有默认成员初始化器)进行值初始化为 0s3.y 默认进行值初始化为 0,而 s3.z 默认值为 2

初始化可能性的总结

如果聚合用初始化列表定义

  • 如果存在显式初始化值,则使用该显式值。
  • 如果初始化器缺失且存在默认成员初始化器,则使用默认值。
  • 如果初始化器缺失且不存在默认成员初始化器,则进行值初始化。

如果聚合没有初始化列表定义

  • 如果存在默认成员初始化器,则使用默认值。
  • 如果不存在默认成员初始化器,则成员保持未初始化。

成员总是按声明顺序初始化。

以下示例总结了所有可能性

struct Something
{
    int x;       // no default initialization value (bad)
    int y {};    // value-initialized by default
    int z { 2 }; // explicit default value
};

int main()
{
    Something s1;             // No initializer list: s1.x is uninitialized, s1.y and s1.z use defaults
    Something s2 { 5, 6, 7 }; // Explicit initializers: s2.x, s2.y, and s2.z use explicit values (no default values are used)
    Something s3 {};          // Missing initializers: s3.x is value initialized, s3.y and s3.z use defaults
   
    return 0;
}

我们需要注意的情况是 s1.x。因为 s1 没有初始化列表,并且 x 没有默认成员初始化器,所以 s1.x 保持未初始化(这很糟糕,因为我们应该总是初始化我们的变量)。

总是为您的成员提供默认值

为了避免未初始化成员的可能性,只需确保每个成员都有一个默认值(无论是显式默认值还是空大括号对)。这样,无论我们是否提供初始化列表,我们的成员都将以某个值初始化。

考虑以下结构体,其所有成员都已默认化

struct Fraction
{
	int numerator { }; // we should use { 0 } here, but for the sake of example we'll use value initialization instead
	int denominator { 1 };
};

int main()
{
	Fraction f1;          // f1.numerator value initialized to 0, f1.denominator defaulted to 1
	Fraction f2 {};       // f2.numerator value initialized to 0, f2.denominator defaulted to 1
	Fraction f3 { 6 };    // f3.numerator initialized to 6, f3.denominator defaulted to 1
	Fraction f4 { 5, 8 }; // f4.numerator initialized to 5, f4.denominator initialized to 8

	return 0;
}

在所有情况下,我们的成员都用值初始化。

最佳实践

为所有成员提供默认值。这确保了即使变量定义不包含初始化列表,您的成员也将被初始化。

聚合的默认初始化与值初始化

重新审视上述示例中的两行代码

	Fraction f1;          // f1.numerator value initialized to 0, f1.denominator defaulted to 1
	Fraction f2 {};       // f2.numerator value initialized to 0, f2.denominator defaulted to 1

您会注意到 f1 是默认初始化的,而 f2 是值初始化的,但结果是相同的(numerator 初始化为 0denominator 初始化为 1)。那么我们应该选择哪种呢?

值初始化(f2)的情况更安全,因为它将确保任何没有默认值的成员都进行值初始化(尽管我们应该始终为成员提供默认值,但这可以防止遗漏的情况)。

偏好值初始化还有一个好处——它与我们初始化其他类型对象的方式保持一致。一致性有助于防止错误。

最佳实践

对于聚合,首选值初始化(使用空大括号初始化器)而不是默认初始化(不使用大括号)。

尽管如此,程序员使用默认初始化而不是值初始化来初始化类类型并不少见。这部分是出于历史原因(因为值初始化直到 C++11 才引入),部分是因为在特定情况下(对于非聚合),默认初始化可能比值初始化更高效(我们在课程 14.11 -- 默认构造函数和默认参数 中介绍了这种情况)。

因此,在这些教程中,我们不会强制要求对结构体和类使用值初始化,但我们强烈推荐它。

guest
您的电子邮箱地址将不会被显示
发现错误?请在上方留言!
与勘误相关的评论在处理后将被删除,以帮助减少混乱。感谢您帮助使网站对每个人都更好!
来自 https://gravatar.com/ 的头像与您提供的电子邮箱地址相关联。
有回复时通知我:  
40 评论
最新
最早 最多投票
内联反馈
查看所有评论