初始化const成员变量

Initialize const member variables

我的 C++ 代码可以归结为如下内容:

class Foo{
    bool bar;
    bool baz;
    Foo(const void*);
};
Foo::Foo(const void* ptr){
    const struct my_struct* s = complex_method(ptr);
    bar = calculate_bar(s);
    baz = calculate_baz(s);
}

从语义上讲,bar 和 baz 成员变量应该是 const,因为它们在初始化后不应更改。但是,似乎为了使它们如此,我需要在初始化列表中初始化它们而不是分配它们。明确地说,我理解为什么我需要这样做。问题是,我似乎无法找到任何方法将代码转换为初始化列表而不执行以下任何一项不良操作:

有什么方法可以让变量成为常量,同时避免这些不良情况?

您可以在 C++11 中使用委托构造函数:

class Foo{
public:
    Foo(const void* ptr) : Foo(complex_method(ptr)) {}

private:
     Foo(const my_struct* s) : bar(calculate_bar(s)), baz(calculate_baz(s)) {}

private:
    const bool bar;
    const bool baz;
};

如果您买得起 C++11 编译器,请考虑 delegating constructors:

class Foo
{
    // ...
    bool const bar;
    bool const baz;
    Foo(void const*);
    // ...
    Foo(my_struct const* s); // Possibly private
};

Foo::Foo(void const* ptr)
    : Foo{complex_method(ptr)}
{
}

// ...

Foo::Foo(my_struct const* s)
    : bar{calculate_bar(s)}
    , baz{calculate_baz(s)}
{
}

作为一般性建议,请小心将您的数据成员声明为 const,因为这会使您的 class 无法复制分配和移动分配。如果您的 class 应该与值语义一起使用,那么这些操作就变得可取了。如果不是这种情况,您可以忽略此说明。

一个选项是 C++11 委托构造函数,如其他答案中所述。 C++03兼容的方法是使用子对象:

class Foo{
    struct subobject {
        const bool bar;
        const bool baz;
        subobject(const struct my_struct* s)
            : bar(calculate_bar(s))
            , baz(calculate_baz(s))
        {}
    } subobject;
    Foo(const void*);
};
Foo::Foo(const void* ptr)
    : subobject(complex_method(ptr))
{}

您可以使 barbaz 常量,或使 subobject 常量,或两者兼而有之。

如果只使 subobject 为常量,则可以计算 complex_method 并在 subobject 的构造函数中分配给 barbaz

class Foo{
    const struct subobject {
        bool bar;
        bool baz;
        subobject(const void*);
    } subobject;
    Foo(const void*);
};
Foo::Foo(const void* ptr)
    : subobject(ptr)
{}
Foo::subobject::subobject(const void* ptr){
    const struct my_struct* s = complex_method(ptr);
    bar = calculate_bar(s);
    baz = calculate_baz(s);
}

原因 你不能在构造函数体中改变 const 成员是为了保持一致性,构造函数体与任何其他成员函数体一样被对待.请注意,您可以将代码从构造函数移动到成员函数中进行重构,并且分解出的成员函数不需要任何特殊处理。

如果您不想使用新奇的委托构造函数(我仍然不得不处理不知道它们的编译器版本),并且您不想更改 class,你可以选择一个解决方案,用返回 Foo 的静态成员函数替换带有 const void * 参数的构造函数,同时拥有一个将 complex_method 的输出作为参数的私有构造函数(后者很像委托构造函数的例子)。然后静态成员函数进行必要的初步计算,涉及 complex_method,并以 return Foo(s); 结束。这确实需要 class 有一个可访问的复制构造函数,即使它的调用(在 return 语句中)很可能被省略。