У меня есть несколько классов, которые я предоставил с помощью функции Init()
. Учитывая, что программист, использующий этот код, может забыть вызвать Init()
, или может вызвать деструктор до Init()
, я написал свой деструктор, чтобы проверить состояние объекта. Я чувствую, что это не очень хорошо написано и хочет отойти от этого, если это возможно, путем инициализации и выделения всего необходимого в конструкторе (Скотт Мейерс рекомендует также против методов инициализации). Тем не менее я пытался найти способ сделать это при использовании дополнительных аргументов/нескольких конструкторов.
Текущий код:
class A {
Init(B* some_other_object);
Init(B* some_other_object, C* an_optional_argument);
...
}
int main(int argc, const char* argv[]) {
...
A a;
if(somecase)
a.Init(b1, c1);
else
a.Init(b1);
}
Желаемый код:
class A {
explicit A(B* some_other_object);
A(B* some_other_object, C* an_optional_argument);
...
}
int main(int argc, const char* argv[]) {
...
if(somecase)
A a(b1, c1);
else
A a(b1);
}
Конечно, проблема здесь в том, что переменная a
выходит из сферы действия. И поэтому я прибегаю к распределению кучи.
int main(int argc, const char* argv[]) {
...
A* a;
if(somecase)
a = new A(b1, c1);
else
a = new A(b1);
...
delete a;
}
Это не здорово, если я пытался сохранить это в стеке. Я также рассмотрел следующее
A a(b1, somecase?c1:nullptr);
Затем сдвиг условной составляющей в тело одного конструктора. Это не выглядит ужасно изящным. Есть ли лучший способ сделать то, что я пытаюсь сделать здесь? Предпочтительно дать мне возможность выделить A в стеке.
Вы можете использовать для этого условный оператор. Это позволяет вам делать то, что вы не можете сделать с if-else
, например. Предполагая этот простой класс с двумя конструкторами,
struct Foo
{
explicit Foo(int) {}
Foo(int, int) {}
};
вы можете инициализировать экземпляр класса, основываясь на таком состоянии:
int i = ....;
Foo f = (i==42) ? Foo(42) : Foo(1, 2);
Вот еще один вариант:
A handleSomeCase(bool someCase, B *b1, C *c1) {
if(somecase)
return A(b1, c1);
else
return A(b1);
}
int main() {
...
A a = handleSomeCase(someCase, b1, c1);
}
Поскольку слишком длинные функции никогда не бывают хорошими, вместо задержки инициализации с помощью Init()
и этого кода:
int main(int argc, const char* argv[]) {
...
A a;
if(somecase)
a.Init(b1, c1);
else
a.Init(b1);
}
вы можете инициализировать свой экземпляр A
с соответствующим конструктором в своей области, просто немного разложите свой код:
void caseA(B* b1, C* c1) {
A a(b1, c1);
...
}
void caseB(B* b1) {
A a(b1);
...
}
int main(int argc, const char* argv[]) {
...
if(somecase)
caseA(b1, c1);
else
caseB(b1);
}
Я бы сделал это:
C* c1 = null; // by default
if (somecase)
c1 = new C(); // (etc: I don't know where your C object comes from!)
// Now there only 1 place you create this object, which is far cleaner
A a(b1, c1);
И дополнительно (не то, что он применяется в моем коде выше) добавьте аргумент по умолчанию, а затем создайте один конструктор для class A
:
A(B* some_other_object, C* an_optional_argument = null);
Если объект скопирован, вы можете сделать следующее:
class A
{
public:
A();
A(B* b);
A(B* b, C* c);
// you'll likely want to specify pointer ownership semantics
A& operator=(const A& a);
};
A a;
if(somecond)
a = A(b);
else
a = A(b, c);
Чтобы избежать проблем с правами на указатель, рассмотрите std :: shared_pointer вместо обычных указателей во время копирования объекта.
Если A имеет разумное состояние по умолчанию, я не вижу, что неправильно с вашим оригинальным методом. Он аналогичен следующему коду
string s;
if (x)
{
s = "hi";
}
else
{
s = "bye";
}
Проблема в том, что у вас есть наполовину построенные объекты, плавающие вокруг. Полностью построенный, но еще не содержащий полезные данные, объект в порядке (по моему скромному мнению)
Поскольку мы работаем с указателями, перемещаем условия внутри класса и от пользователей со значением по умолчанию для параметра.
class A {
A(B* some_other_object, C* an_optional_argument = NULL) {
if(an_optional_argument) {
//...
} else {
//...
}
}
...
}
int main(int argc, const char* argv[]) {
...
A a(b1);
A b(b1, c1);
}