Подтвердить что ты не робот

Инициализировать переменные-члены

У меня есть код на С++, который сводится к следующему:

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, так как после инициализации они не должны меняться. Однако, похоже, что для их создания мне нужно будет инициализировать их в списке инициализации, а не назначать их. Чтобы быть ясным, я понимаю, зачем мне это нужно. Проблема в том, что я не могу найти способ конвертировать код в список инициализации, не выполняя одно из следующих нежелательных действий:

  • Вызов complex_method дважды (будет плохо для производительности)
  • Добавить указатель на класс Foo (сделает размер класса ненужным большим)

Есть ли способ сделать переменные const, избегая этих нежелательных ситуаций?

4b9b3361

Ответ 1

Если вы можете позволить себе компилятор С++ 11, рассмотрите делегирование конструкторов:

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, потому что это делает ваш класс невозможным для копирования и назначения. Если ваш класс предполагается использовать с семантикой значения, эти операции становятся желательными. Если это не так, вы можете игнорировать эту заметку.

Ответ 2

Один из вариантов - конструктор делегирования С++ 11, как описано в других ответах. С++ 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))
{}

Вы можете сделать bar и baz const, или создать subobject const, или и то, и другое.

Если вы создаете только subobject const, вы можете рассчитать complex_method и назначить bar и baz внутри конструктора subobject:

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 членов внутри тела конструктора, состоит в том, что тело конструктора рассматривается как любое другое тело функции-члена для согласованности. Обратите внимание, что вы можете переместить код из конструктора в функцию-член для рефакторинга, а функция-член факторинга не нуждается в особой обработке.

Ответ 3

Вы можете использовать конструктор делегата в С++ 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;
};

Ответ 4

Если вы не хотите использовать newfangled делегирующие конструкторы (мне все еще приходится иметь дело с версиями компилятора, которые не знают о них), и вы не хотите менять макет своего класса, вы можете выбрать для решения, которое заменяет конструктор аргументом const void * статической функцией-членом, возвращающей Foo, имея частный конструктор, который выводит результат из complex_method в качестве аргумента (этот последний очень похож на примеры конструктора делегирования). Статическая функция-член затем выполняет необходимые предварительные вычисления с участием complex_method и заканчивается на return Foo(s);. Это требует, чтобы класс имел доступный конструктор копирования, хотя его вызов (в инструкции return), скорее всего, будет удален.