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