C++在类的声明中初始化成员变量

在C++中,从C++11标准开始,可以在类的声明中直接初始化成员变量。这种语法被称为成员变量的默认初始化。这使得代码更清晰、更简洁,并且减少了构造函数中的初始化代码。

#include <iostream>

class MyClass
{
public:
    int x = 10; // 声明中初始化
    double y = 1.5; // 声明中初始化
    
    MyClass() = default;
};

int main()
{
    MyCalss obj;
    std::cout << obj.x << ' ' << obj.y << '\n';
    return 0;
}

默认初始化在很多场景下都可以用到,对于类中的基本类型变量,如果既未在类声明中初始化,又没在构造函数中进行初始化,它的值将是未定义的,后续会导致未定义行为,而频繁的在多个构造函数中对齐进行同样的初始化又略显冗杂。于是我们可以考虑在类的声明中对其进行初始化。

1. 初始化的顺序

我们知道,在C++11后,三种初始化方法(声明中初始化、列表初始化、构造函数初始化)都可以使用且不会冲突,所以我们也需要注意初始化的顺序:
声明中初始化->初始化列表->构造函数初始化
在下面的代码中,x、y在声明中的初始化首先被列表初始化覆盖,最终x在构造函数中的初始化又将列表初始化覆盖,最终x、y的值分别为20、5.5.

#include <iostream>

class MyClass
{
public:
    int x = 10; // 声明中初始化
    double y = 1.5; // 声明中初始化
    
    MyClass() : x(5), y(5.5) // 列表初始化
    {
        x = 20; // 构造函数初始化
    }
};

int main()
{
    MyCalss obj;
    std::cout << obj.x << ' ' << obj.y << '\n';
    return 0;
}

2. 静态成员变量

一般的静态成员变量不能在类内直接初始化,必须在类外进行初始化:

#include <iostream>

class MyClass
{
public:
    static int count;
    
    MyClass() = default;
};

int MyClass::count = 10;

int main()
{
    MyCalss obj;
    std::cout << obj.count << '\n';
    return 0;
}

但如果成员变量可以在编译时计算其值,那么可以使用 constexpr 进行初始化(C++11引入):

#include <iostream>

class MyClass
{
public:
    static constexpr int count = 10; // constexpr 静态成员变量在类声明中初始化
    
    MyClass() = default;
};

int main()
{
    MyCalss obj;
    std::cout << obj.count << '\n';
    return 0;
}

注意事项

  1. 编译时常量:constexpr 静态成员变量必须是编译时常量,且其值必须是常量表达式。
  2. 内联初始化:在类内初始化 constexpr 静态成员变量时,实际上是在进行内联初始化。因此,这些变量在所有使用它们的翻译单元中都可用。
  3. 类型限制:constexpr 静态成员变量可以是整数、浮点数等基本类型,也可以是支持编译时常量表达式的用户定义类型。

3. 常量成员变量、引用成员变量

常量成员变量(const)和引用成员变量(&)必须在构造函数初始化列表中初始化,因为它们需要在对象创建时就被赋值。

#include <iostream>

class MyClass
{
public:
    const int x;
    double &ref;
    
    MyClass(int a, int &b) : x(a), ref(b) {} // 列表初始化
};

int main()
{
    MyCalss obj;
    std::cout << obj.x << ' ' << obj.ref << '\n';
    return 0;
}

关于列表初始化的诸多细节在此不做详谈,在之后的文章中会进行介绍。