为什么我更喜欢使用成员初始化列表?

pax*_*977 212 c++ oop object-construction

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

您是否在构造函数中使用成员初始化列表?如果是这样,为什么?如果没有,为什么不呢?

Ada*_*eld 263

对于POD类成员来说,它没有任何区别,只是风格问题.对于类的类成员,它避免了对默认构造函数的不必要的调用.考虑:

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

class B
{
public:
    B()
    {
        a.x = 3;
    }
private:
    A a;
};
Run Code Online (Sandbox Code Playgroud)

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

B()
  : a(3)
{
}
Run Code Online (Sandbox Code Playgroud)

这只会调用AA(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;
};
Run Code Online (Sandbox Code Playgroud)

  • 必须也是重要的参考案例 (4认同)
  • 为什么不使用"a(3);" 或"a = A(3);" 在B的默认构造函数的主体? (4认同)
  • @Sergey,A 的默认构造函数仍会被调用。 (4认同)
  • @JonasStein POD是一组定义明确的规则,涉及简单的数据结构(而不​​是完整的类).请阅读常见问题解答:http://stackoverflow.com/questions/146452/what-are-pod-types-in-c (2认同)

Nav*_*een 42

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

  • 我相信const成员也是如此. (7认同)

yuv*_*uvi 18

  1. 基类的初始化

使用构造函数初始化列表的一个重要原因是答案中未提及的是基类的初始化.

根据构造的顺序,基类应该在子类之前构造.如果没有构造函数初始化列表,如果您的基类具有默认构造函数,则可以在进入子类的构造函数之前调用它.

但是,如果您的基类只有参数化构造函数,那么您必须使用构造函数初始化列表来确保您的基类在子类之前初始化.

  1. 初始化仅具有参数化构造函数的子对象

  2. 效率

使用构造函数初始化列表,您可以将数据成员初始化为代码中所需的准确状态,而不是先将它们初始化为默认状态,然后将其状态更改为代码中所需的状态.

  1. 初始化非静态const数据成员

如果类中的非静态const数据成员具有默认构造函数并且您不使用构造函数初始值设定项列表,则无法将它们初始化为预期状态,因为它们将初始化为其默认状态.

  1. 初始化参考数据成员

当编译器进入构造函数时,必须初始化引用数据成员,因为引用不能仅在以后声明和初始化.这仅适用于构造函数初始化列表.


mlo*_*kot 8

在性能问题旁边,还有一个非常重要的问题,我称之为代码可维护性和可扩展性.

如果T是POD并且您开始更喜欢初始化列表,那么如果一次T将更改为非POD类型,则不需要更改初始化周围的任何内容以避免不必要的构造函数调用,因为它已经过优化.

如果类型T确实具有默认构造函数和一个或多个用户定义的构造函数,并且有一次您决定删除或隐藏默认构造函数,那么如果使用了初始化列表,则如果您的用户定义的构造函数,则不需要更新代码,因为它们已经正确实施.

与const成员或引用成员相同,假设最初T定义如下:

struct T
{
    T() { a = 5; }
private:
    int a;
};
Run Code Online (Sandbox Code Playgroud)

接下来,如果您从头开始使用初始化列表,则决定限定为const,那么这是单行更改,但如上所述定义了T,它还需要挖掘构造函数定义以删除赋值:

struct T
{
    T() : a(5) {} // 2. that requires changes here too
private:
    const int a; // 1. one line change
};
Run Code Online (Sandbox Code Playgroud)

如果代码不是由"代码猴"编写,而是由工程师根据对他正在做的事情的深入考虑做出决策,那么维护更容易且不易出错也不是秘密.


Jam*_*far 5

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

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


小智 5

// Without Initializer List\nclass MyClass {\n    Type variable;\npublic:\n    MyClass(Type a) {  // Assume that Type is an already\n                     // declared class and it has appropriate \n                     // constructors and operators\n        variable = a;\n    }\n};\n \n
Run Code Online (Sandbox Code Playgroud)\n

这里编译器按照以下步骤创建类型的对象MyClass

\n
    \n
  1. Type首先为 \xe2\x80\x9c \ axe2\x80\x9d 调用 \xe2\x80\x99s 构造函数。
  2. \n
  3. \xe2\x80\x9c \xe2\x80\x9d 的赋值运算符Type在构造函数体内调用MyClass()进行赋值。
  4. \n
\n
variable = a;\n
Run Code Online (Sandbox Code Playgroud)\n
    \n
  1. 最后,为 \xe2\x80\x9c Type\xe2\x80\x9d 调用 \xe2\x80\x9c a\xe2\x80\x9d 的析构函数,因为它超出了范围。
  2. \n
\n

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

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

对于初始值设定项列表,编译器将执行以下步骤:

\n
    \n
  1. 调用 \xe2\x80\x9c Type\xe2\x80\x9d 类的复制构造函数来初始化:variable(a)。初始化列表中的参数用于variable直接复制构造 \xe2\x80\x9c \xe2\x80\x9d。

    \n
  2. \n
  3. \xe2\x80\x9c Type\xe2\x80\x9d 的析构函数被调用为 \xe2\x80\x9c a\xe2\x80\x9d,因为它超出了范围。

    \n
  4. \n
\n

  • 虽然此代码片段可以解决问题,但在代码中包含解释确实有助于提高帖子的质量。请记住,您是在为将来的读者回答问题,而那些人可能不知道您建议代码的原因。还请尽量不要在代码中添加解释性注释,这会降低代码和解释的可读性!http://meta.stackexchange.com/q/114762/308249 (2认同)
  • 请写下您自己的理解,或者只是分享原始来源的链接(此处为 geeksforgeeks.com),而不是仅仅复制粘贴它。 (2认同)