0

我有许多类,我提供了一个Init()函数。鉴于使用此代码的程序员可能忘记调用Init()或之前可能调用析构函数Init(),我编写了析构函数来检查对象的状态。我觉得这写得不是很好,如果可能的话,我想通过初始化和分配构造函数中所需的一切来摆脱这种情况(Scott Meyers 也建议不要使用初始化方法)。然而,当使用可选参数/多个构造函数时,我一直在努力寻找一种方法来做到这一点。

当前代码:

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 的选项。

4

7 回答 7

7

您可以为此使用条件运算符。例如,它允许你做一些你不能用if-else. 假设这个简单的类有两个构造函数,

struct Foo
{
  explicit Foo(int) {}
  Foo(int, int) {}
};

您可以根据以下条件初始化类的实例:

int i = ....;

Foo f = (i==42) ? Foo(42) : Foo(1, 2);
于 2013-10-09T14:50:49.230 回答
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);
}
于 2013-10-09T15:15:01.953 回答
1

由于太长的函数永远不会好,而不是延迟初始化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);
}
于 2013-10-09T14:49:10.407 回答
1

我会这样做:

C* c1 = null; // by default
if (somecase)
    c1 = new C(); // (etc: I don't know where your C object comes from!)

// Now there's 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);
于 2013-10-09T14:49:19.177 回答
1

如果对象是可复制的,您可以执行以下操作:

 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 而不是裸指针。

于 2013-10-09T15:00:45.870 回答
0

如果 A 具有合理的默认状态,我看不出您的原始方法有什么问题。类似于下面的代码

string s;
if (x)
{
    s = "hi";
}
else
{
    s = "bye";
}

问题是如果你有半构造的物体漂浮在周围。一个完全构造但尚未包含有用数据的对象很好(以我的拙见)

于 2013-10-09T14:57:30.310 回答
0

由于我们正在使用指针,因此使用参数的默认值将条件移动到类内部并远离用户。

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);
}
于 2013-10-09T14:58:34.240 回答