Возможно, вы захотите использовать тип Nullable, такой как boost :: необязательный.Это будет выглядеть так:
class B {
public:
B()
{
/*
* Here, a has been default constructed and is empty
* You can do computations here and then...
*/
if(/* some elaborate condition*/) {
a = A();
/* access *a from here on */
} else {
/* anything you want */
}
}
private:
boost::optional<A> a;
};
Это ответ на вопрос, но я думаю, что более подходящий ответ мог бы быть дан, если бы вы сказали, чего вы действительно хотели достичь.Я чувствую, что это скорее проблема дизайна, чем проблема языка.Далее следуют расширенные мысли.
Что входит в предложение else
в приведенном выше «решении»?Поскольку A
, очевидно, может быть сконструирован только по умолчанию, это не значит, что вы можете сделать другой вызов конструктора.Однако если вы не инициализируете a
, вы вводите (цикломатическую) сложность, поскольку это будет означать, что каждый метод должен будет проверять, активен ли a
.В качестве альтернативы вы можете бросить;Однако я бы рефакторинг кода, который выполняет проверку (или что-то еще) в функции;либо частный статический метод, либо анонимная / статическая автономная функция, подобная этой:
namespace {
A
prepare_A()
{
/* elaborate computations, possibly throw */
return A();
/*
* if A had different constructors we could also conditionally
* return an A(0) or whatever.
*/
}
} // namespace
B::B()
:
a(prepare_A())
{
/*
* a is of type A, not boost::optional<A>
*/
}
Это, однако, предполагает, что A
можно копировать.Если это не тот случай или если нельзя копировать A
, я бы посчитал первое решение приемлемым при условии, что как часть инварианта класса a
никогда не будет пустым.
Проще порекомендовать что-нибудь, если бы мы знали отношения между A
и B
.Зачем вам ставить A
член в B
, если вы хотите его условно инициализировать?Возможно, предпочтительным отношением между ними должно быть не агрегация, а ассоциация: элемент указателя (или ссылочный элемент, если вы осторожны с оператором присваивания) для A
, например:
class B {
public:
explicit
B(A& a_)
/*
* Important: not A const&,
* we only want lvalues.
* Contract on the caller: a must remain valid during
* the lifetime of *this.
*/
:
a(&a_)
{
/*
* class invariants: a != 0,
* *a remains valid for the lifetime of B
*/
}
private:
A* a;
};
Таким образом, выне вводите цикломатическую сложность, и вам все равно, как и когда строится А.Контракт добавляет небольшую нагрузку на вызывающего, но, поскольку мы можем передавать конструктору только lvalues, злоупотреблять им очень сложно (*).