聚合初始化

在介绍指定初始化器之前,我们先介绍下C++ 20之前的聚合初始化。聚合初始化允许我们直接在声明对象时以初始化列表的形式初始化所有成员变量,或者只初始化部分成员,其余成员默认初始化。如果我们只想要指定某些成员变量的值,则聚合初始化将无能为力。

#include <iostream>

struct Point
{
    int x;
    int y;
    int z;
};

int main()
{
    // 全部初始化
    Point p1{0, 0, 0};

    // 部分初始化
    Point p2{66, 99};

    return 0;
}


什么是指定初始化器

为了解决聚合初始化无法指定某些成员变量进行初始化的问题,C++ 20中引入了指定初始化器。指定初始化器允许在初始化结构体、联合体或类的对象时,明确指定成员变量的初始化。这一特性比较适用于大型或复杂的结构体,当我们只想初始化部分成员,或者想明确指出每个成员的初始值时,非常有用。

注意:并非所有的类和结构体都能使用指定初始化器。为了使用该特性,类和结构体必须满足以下两个条件。

1、必须是聚合类型,这就意味着,没有自定义构造函数,没有私有或受保护的非静态数据成员,没有基类,也没有虚函数。

2、所有成员变量必须是公开的。


初始化结构体

结构体的指定初始化器使用.符号紧跟着成员名称,后面跟着=符号,然后是成员的初始值。在下面的示例代码中,我们只显式初始化了x和y。指定初始化器不仅让初始化的意图更加明确,也避免了因成员顺序错误而导致的潜在问题。

struct Point
{
    int x;
    int y;
    int z;
};

int main()
{
    // 指定初始化器
    Point p{.y = 99, .x = 66};

    return 0;
}

对于嵌套的结构体,指定初始化器也能轻松应对。通过下面示例代码中的指定初始化器,我们可以看到ComplexPoint中每个子结构体成员的具体初始化值,这在处理多层嵌套的数据结构时尤为有用。

struct Point
{
    int x;
    int y;
    int z;
};

struct Color
{
    int r;
    int g;
    int b;
};

struct ComplexPoint
{
    Point pt;
    Color color;
};

int main()
{
    ComplexPoint cp{
        .pt = {.x = 66, .y = 99, .z = 100},
        .color = {.r = 255, .g = 0, .b = 0}
    };

    return 0;
}


初始化数组

在C++ 20中,我们可以使用指定初始化器来初始化数组中的特定元素,即使它们不是连续的。这种初始化方式允许我们直接指定数组索引和对应的值,语法上通过方括号[]来指定索引,紧随其后的是=符号, 然后是该位置的值。

在下面的示例代码中,我们创建了一个包含三个元素的整数数组arr。然后,我们使用指定初始化器来初始化其中的两个元素,索引分别为0和2。未明确指定的索引为1的元素,将被隐式初始化为0。

#include <iostream>

int main()
{
    // arr[1]将被隐式初始化为0
    int arr[3] = {[0] = 66, [2] = 99};
    std::cout << arr[0] << ", " << arr[1] << ", " << arr[2] << std::endl;

    return 0;
}


初始化类

指定初始化器不仅适用于结构体和数组,也适用于类,但有一些额外的限制(前面已经提到过)。另外,指定初始化器还可以与默认值结合使用,为未指定的成员提供默认初始化。

在下面的示例代码中,我们定义了一个名为MyClass的类,其中包含两个公共成员变量m_num1和m_num2。m_num1在类内被初始化为66,而m_num2没有给出初始值。这意味着,如果不通过构造函数或其他方式显式初始化,它的值将是未定义的。

在main函数中,我们通过指定初始化器创建了MyClass的一个实例obj。这里,我们只指定了m_num2的值为99,而没有直接指定m_num1的值。由于MyClass的定义中已经为m_num1提供了默认值66,所以在初始化obj时,m_num1自动采用了这个默认值,而m_num2则根据指定初始化器设置为99。

#include <iostream>

class MyClass
{
public:
    int m_num1 = 66;
    int m_num2;
};

int main()
{
    // m_num1使用默认值66
    MyClass obj{.m_num2 = 99};
    std::cout << obj.m_num1 << ", " << obj.m_num2 << std::endl;
    return 0;
}


注意事项

1、指定初始化器只能初始化public或protected成员,private成员无法直接指定初始化。

2、指定初始化器不会改变成员的初始化顺序,成员仍然按照它们在类或结构体中声明的顺序初始化。

3、指定初始化器提供了严格的类型检查,确保初始化的值与成员的类型相匹配。


💡 如果想阅读最新的文章,或者有技术问题需要交流和沟通,可搜索并关注微信公众号“希望睿智”。