我倾向于使用我的构造函数的成员初始化列表…但我早就忘了这背后的原因了……

在构造函数中使用成员初始化列表吗?如果有,为什么?如果不是,为什么不是?


当前回答

对于POD的成员来说,这没有什么区别,只是风格的问题。对于属于类的类成员,则避免不必要地调用默认构造函数。考虑:

class A
{
public:
    A() { x = 0; }
    A(int x_) { x = x_; }
    int x;
};

class B
{
public:
    B()
    {
        a.x = 3;
    }
private:
    A a;
};

在这种情况下,B的构造函数将调用A的默认构造函数,然后将A .x初始化为3。更好的方法是让B的构造函数直接在初始化列表中调用A的构造函数:

B()
  : a(3)
{
}

这将只调用A的A(int)构造函数,而不调用其默认构造函数。在这个例子中,差异可以忽略不计,但是想象一下A的默认构造函数做了更多的工作,比如分配内存或打开文件。你不会想做不必要的事情。

此外,如果一个类没有默认构造函数,或者你有一个const成员变量,你必须使用初始化列表:

class A
{
public:
    A(int x_) { x = x_; }
    int x;
};

class B
{
public:
    B() : a(3), y(2)  // 'a' and 'y' MUST be initialized in an initializer list;
    {                 // it is an error not to do so
    }
private:
    A a;
    const int y;
};

其他回答

除了上面提到的性能原因,如果你的类存储了作为构造函数参数传递的对象的引用,或者你的类有const变量,那么你除了使用初始化列表之外没有任何选择。

对于POD的成员来说,这没有什么区别,只是风格的问题。对于属于类的类成员,则避免不必要地调用默认构造函数。考虑:

class A
{
public:
    A() { x = 0; }
    A(int x_) { x = x_; }
    int x;
};

class B
{
public:
    B()
    {
        a.x = 3;
    }
private:
    A a;
};

在这种情况下,B的构造函数将调用A的默认构造函数,然后将A .x初始化为3。更好的方法是让B的构造函数直接在初始化列表中调用A的构造函数:

B()
  : a(3)
{
}

这将只调用A的A(int)构造函数,而不调用其默认构造函数。在这个例子中,差异可以忽略不计,但是想象一下A的默认构造函数做了更多的工作,比如分配内存或打开文件。你不会想做不必要的事情。

此外,如果一个类没有默认构造函数,或者你有一个const成员变量,你必须使用初始化列表:

class A
{
public:
    A(int x_) { x = x_; }
    int x;
};

class B
{
public:
    B() : a(3), y(2)  // 'a' and 'y' MUST be initialized in an initializer list;
    {                 // it is an error not to do so
    }
private:
    A a;
    const int y;
};

正如c++核心指南C.49中所解释的:在构造函数中首选初始化而不是赋值 它可以防止对默认构造函数的不必要调用。

在运行构造函数体之前,调用父类的所有构造函数,然后调用其字段的所有构造函数。默认情况下,调用无参数构造函数。初始化列表允许您选择调用哪个构造函数以及该构造函数接收哪些参数。

如果有引用或const字段,或者使用的类中有一个没有默认构造函数,则必须使用初始化列表。

// Without Initializer List
class MyClass {
    Type variable;
public:
    MyClass(Type a) {  // Assume that Type is an already
                     // declared class and it has appropriate 
                     // constructors and operators
        variable = a;
    }
};
 

编译器遵循以下步骤创建MyClass类型的对象:

类型的构造函数首先为“a”调用。 “Type”的赋值操作符在MyClass()构造函数体中被调用以赋值。

variable = a;

最后,Type的析构函数被调用为a,因为它超出了作用域。

现在考虑MyClass()构造函数带有初始化列表的相同代码:

    // With Initializer List
    class MyClass {
    Type variable;
    public:
    MyClass(Type a):variable(a) {   // Assume that Type is an already
                     // declared class and it has appropriate
                     // constructors and operators
    }
    };
 

使用初始化器列表,编译器执行以下步骤:

调用Type类的复制构造函数初始化:variable(a)。初始化器列表中的参数用于直接复制构造“variable”。 “Type”的析构函数被调用为“a”,因为它超出了作用域。