成员初始化列表是构造函数的声明部分还是定义部分?

31
请解释如何使用成员初始化列表。 我在一个.h文件和一个.cpp文件中声明了一个类,就像这样:
class Example
{
private:
    int m_top;
    const int m_size;
    /* ... */
public:
    Example(int size, int grow_by = 1) : m_size(5), m_top(-1);
    /* ... */
    ~Example();
};

我在对象创建时初始化m_size,因为它是常量。我应该如何编写构造函数?我应该重复写上:m_size(5), m_top(-1),还是可以省略这一步骤?
Example::Example(int size, int grow_by)
{
    /* ... */
}

或者

Example::Example(int size, int grow_by) : m_size(5), m_top(-1)
{
    /* ... some code here */
}

3
在定义构造函数时,需要指定初始化列表,而不是在声明构造函数时。 - wkl
6个回答

65
只是为了澄清一些其他答案中出现的问题... 没有要求初始化列表必须在源文件(.cpp)或头文件(.h)中。 实际上,编译器并不区分这两种类型的文件。重要的区别在于构造函数的声明和定义。初始化列表与定义相关联,而不是声明。
通常情况下,声明位于头文件中,定义位于源文件中,但这并不是语言的要求(即它可以编译通过)。 当构造函数为空或很短时,将构造函数定义内联在类声明中并不罕见。在这种情况下,初始化列表将放在类声明内部,该声明可能位于头文件中。

MyClass.h

class MyClass
{
public:
    MyClass(int value) : m_value(value)
    {}
private:
    int m_value;
};

注意:一些框架和语言,如Qt的C++,要求它们的类和结构体派生自“QObject”,以便使用自己的头文件来利用其“Q_OBJECT”宏,从而充分发挥其潜力。 - kayleeFrye_onDeck

27
这是初始化列表:
Example::Example( int size, int grow_by) : m_size(5), m_top(-1)
{
    /* ... */
}

只能在.cpp文件中完成。
如果你像例子中那样在头文件中完成,会出现错误。

请注意,初始化的顺序应与声明的顺序相同,否则会出现警告(使用[-Weffc ++]编译)。 - Muhammad Khuzaima Umair

15
成员初始化列表应该是构造函数定义的一部分,位于源文件中。 在.cpp文件中写入以下内容:
Example(int size, int grow_by) : m_size(5), m_top(-1)
{
    /* ... */
}

头文件应该只包含以下内容:
Example(int size, int grow_by = 1);

头文件只声明构造函数,成员初始化列表不是声明的一部分。

即初始化列表只能放在构造函数定义中。 - Cat Plus Plus
@CatPlusPlus:哦,有点晚了,在你的评论之前已经编辑好了 :P - Alok Save
我认为 = 1 默认参数应该只在头文件中,而不是 cpp 文件中。 - Mooing Duck
@MooingDuck:正确!啊,错误的是 cntrl + ccntrl + v。这是个坏习惯 :P! - Alok Save
此外,数据成员的初始化顺序与它们声明的顺序相同,因此最好按照相同的方式对初始化列表进行排序以保持一致。在您的示例中,应该是 : m_top(-1), m_size(5)。请参阅 Meyers 的 Effective C++ 第4项建议。 - David Alber

9
除了其他人的回答之外,关于初始化列表,一个最重要的事情需要记住:
初始化的顺序是根据数据成员的声明顺序决定的,而不是根据你使用初始化列表初始化数据成员的顺序。
考虑你的例子:
class Example
{
private:
    int m_top;
    const int m_size;
    /* ... */
public:
    Example(int size, int grow_by = 1) : m_size(5), m_top(-1) {}
    /* m_size appears to be initialized with a value first,
     * but m_top is initialized to the value -1 before
     * m_size is initialized to 5. */
    
    /* ... */
    ~Example(){}
};

如果一个人对上述情况不了解,那将会带来非常严重的影响。

1
许多编译器会在初始化顺序可能导致问题的歧义时发出警告。如果尚未启用此类警告,请启用并注意此类警告。 - emsr
是的,但仍然值得知道警告的真正含义 :) - Arunmu

1
在C++11中,您可以使用非静态数据成员初始化。如果您有多个需要成员变量共同值的构造函数,则特别有用。
class Example
{
private:
    int m_top = -1;
    const int m_size = 5;
    ...
public:
    Example ( int size, int grow_by = 1 );
    ...
    ~Example();
};

...

Example::Example( int size, int grow_by )
{
    ... some code here
}

如果需要,您可以在构造函数中覆盖值。


1
你不能在头文件和源文件中都有初始化列表。列表应该放在定义构造函数的文件中。此外,即使是空的,定义构造函数时也必须包含一个主体。
另外,你只应该在函数原型中指定默认参数,而不是在定义中。

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