我应该在C++构造函数中使用初始化列表还是执行赋值操作?

9
class Node
{
public:

    Node *parent; // used during the search to record the parent of successor nodes
    Node *child; // used after the search for the application to view the search in reverse

    float g; // cost of this node + it's predecessors
    float h; // heuristic estimate of distance to goal
    float f; // sum of cumulative cost of predecessors and self and heuristic

    Node() :
            parent( 0 ),
            child( 0 ),
            g( 0.0f ),
            h( 0.0f ),
            f( 0.0f )
    {
    }

    UserState m_UserState;
};

为什么我们应该使用构造函数

    Node() :
            parent( 0 ),
            child( 0 ),
            g( 0.0f ),
            h( 0.0f ),
            f( 0.0f )
    {
    }

代替
    Node()
    {
        parent = null;
        child = null;
        g = 0.0f;
        h = 0.0f;
        f = 0.0f;
    }

谢谢 :)
5个回答

14

对于普通旧数据(POD),这没有太多好处,但一旦开始使用引用或组合类,它就有所不同:

class Foo {
    Bar bar;

  public:
    // construct bar from x
    Foo(int x) : bar(x) { }
};

对比

Foo::Foo(int x)
{
    // bar is default-constructed; how do we "re-construct" it from x?
    bar = x;  // requires operator=(int) on bar; even if that's available,
              // time is wasted default-constructing bar
}
有时,即使一个对象已经被构造,你也没有办法"重新构造"它,因为一个类可能不支持setters或operator=。const成员肯定无法"重新构造"或重置:
class FooWithConstBar {
    const Bar bar;

  public:
    Foo(int x) {
        // bar is cast in stone for the lifetime of this Foo object
    }
};

编辑: 感谢 @Vitus 指出引用问题。


啊,你在我写这篇文章的时候更新了有关constness的帖子。我真的对“西部最快枪手”综合症感到厌烦了。 - rjnilsson
3
同时,构造函数的引用参数不能在构造函数体内分配,并且必须在初始化列表中初始化。 - Vitus

6
由于存在一些情况,您实际上需要使用初始化列表来初始化成员变量,或者出于性能原因应该使用它,因此您应该保持一致并始终使用它。
需要使用初始化列表的示例包括聚合体没有默认构造函数或具有const成员(是的,不常见,但允许)。
应该(但不强制)使用初始化列表的示例是当您具有聚合对象且以下几点成立时:
- 聚合体具有默认构造函数。 - 您需要使用非默认值设置对象。 - 聚合体具有构造函数,使您可以设置所需的非默认值。
那么,为什么不应该使用它呢?

1
我可以想到一些完全人工的情况,其中您希望两个对象的初始化顺序根据构造函数的某个标志而不同。 if (a) { b = new X(NULL); c = new Y(b); } else { c = new Y(NULL); b = new X(c); }。即使是在示例形式中,这对我来说也很糟糕 - 但它无法使用初始化列表完成。 - user79758
@Joe:你能举一个不涉及资源分配的例子吗?如果在成员初始化列表中初始化期间抛出异常,行为是明确定义的,但在你提供的示例中,如果X、Y的构造函数或“new”调用抛出异常,你将不得不编写相当多的代码来确保内存正确释放。 - Richard Corden
在成员初始化列表中抛出异常时,包含有关保证的信息可能是值得的。 - Richard Corden
@Richard:我想不出有哪个不需要分配资源的,但是说句轻浮的话,在我的例子中bc已经是智能指针了,所以内存释放是有保障的。 ;) - user79758
@Joe:哦,带有接受原始指针的隐式单参数构造函数的智能指针...真是罕见:)不过有一点需要注意:我实际上认为可以使用初始化列表和返回init值的辅助方法来实现您的人工案例,但这样做会更加丑陋。 - rjnilsson

3

2

主要是由于性能问题。

这里讨论了这个问题


1

主要原因是对象有效性和类不变量。另一个原因是易用性。

如果您将所有内容放入构造函数中,则在构造函数完成时,可以保证获得有效的节点。 如果您必须单独设置所有实例变量,则可能会出现未初始化某些变量的节点,因为程序员忘记或者其中一个赋值引发异常。后者在您的情况下不会发生,但通常可能会发生。如果节点未完全初始化,则无法保证其行为符合您的预期。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接