【问题标题】:Difference between initializer and default initializer list in c++ [duplicate]C ++中初始化程序和默认初始化程序列表之间的区别[重复]
【发布时间】:2011-04-28 09:07:24
【问题描述】:

嗨, 有个问题,找了半天也找不到答案,就是, 以下关于参数的两条语句有什么区别 初始化?

class A::A() 
    : a(0), b(0), c(0) 
{ 
}

class A::A() 
{ 
    a = 0 
    b = 0; 
    c = 0; 
} 

我知道有“直接初始化”和“复制初始化”,但我 不知道其他区别是什么,是否有任何描述 关于第一个声明?

提前致谢

【问题讨论】:

  • @user692270 我已编辑并在您的初始化列表示例中添加了一个缺少的冒号。
  • 注意第二个例子中的=既不是“直接初始化”也不是“复制初始化”,而是赋值。
  • user692270 接受@karthik 回答的另一个问题。停止使用两个帐户进行投票。

标签: c++ constructor


【解决方案1】:

使用初始化列表,成员只被创建和初始化一次,具有给定的值。

使用赋值,使用默认值初始化成员,然后在构造函数体中重新赋值。

在某些情况下(常量、引用)你只能使用初始化列表,因为

  • 这些必须使用有效值进行初始化,并且
  • 一旦它们被构建,重新分配它们是非法的。

在其他情况下,即使可以进行赋值,初始化列表仍然是可取的,因为它避免了额外的工作(双重初始化,这对于某些类型来说可能代价高昂)并遵循一个常见的习惯用法,使您的代码更易于理解和维护.

需要注意的一点是,成员的初始化顺序是由它们的声明顺序定义的,不是由它们在初始化列表中列出的顺序定义的。例如

class Example {
  int a, b, c;

  Example() : a(1), c(2), b(c) {}
}

产生未定义的行为,因为b 被初始化之前 c,因此具有未定义的值。为避免混淆和出现此类细微错误的可能性,请始终按照在类中声明的顺序列出初始化列表中的成员。

起初这可能看起来很模糊,但这是有原因的。在 C++ 中,可以保证类的所有成员都按照与创建它们的顺序完全相反的顺序被销毁。现在,类可以有多个构造函数,每个构造函数都有自己的初始化列表,并且(不幸的是,有人可能会说)初始化列表可以按程序员想要的任何方式排序。如果初始化列表的顺序决定了初始化的实际顺序,那么运行时应该以某种方式维护有关每个对象的数据,以记住它是用哪个构造函数创建的,以及它的成员应该以什么顺序被销毁。这将导致运行时开销,没有明显的好处,因此 - 与“只为使用的东西付费”的一般 C++ 理念一致 - 决定初始化顺序由声明顺序一劳永逸地定义。

【讨论】:

  • 这在初始化成本高昂或处理不可默认构造的类型时变得至关重要。
  • @MSalters,你是对的,也许我在这里过于细致。虽然我不禁想到肯定有人在尝试编写像C() : aConst(0) { aConst = theRealValue; } 这样的代码:-)
【解决方案2】:

默认初始化列表的目的是初始化类中的常量变量。 因为常量变量是在对象初始化之前初始化的。

我提供一个示例来解释这两种初始化之间的区别:

 class A
   {
       private:
          const int x;

  };

  A::A():x(5)        //this code works fine
  {

    }

    A::A()       //this code is wrong.const variable is not initialized once object         
   {
    x=5;
    }

【讨论】:

  • 它不仅用于初始化常量,还用于初始化所有成员。并且它也是强制性的参考。
  • ... 或没有默认构造函数的类类型成员和基类。
【解决方案3】:

主要区别在于,在第一种情况下,类成员被初始化,而在第二种情况下,它们被分配。对于非整数类型,这意味着在第二种情况下,您将使用operator= 为您的班级成员分配值。

通常,首选使用第一种情况,因为在这种情况下,类成员在构造函数主体之前初始化

此外,您不能在某些情况下使用赋值,例如,当声明类成员时 const

【讨论】:

    【解决方案4】:

    Marshall Cline 取自 section 10.6 of the C++ FAQ

    初始化列表。实际上, 构造函数应该初始化为 规则中的所有成员对象 初始化列表。一个例外是 进一步讨论。

    考虑下面的构造函数 初始化成员对象 x_ 使用初始化列表: Fred::Fred() : x_(随便) { }。这 这样做最常见的好处是 改进的性能。例如,如果 表达什么都一样 类型为成员变量 x_,结果 不管是什么表达 直接在 x_ 内部构造—— 编译器不会制作单独的副本 的对象。即使类型是 不一样,编译器通常是 能够做得更好 初始化列表比 任务。

    另一种(低效)构建方式 构造函数是通过赋值,例如 如: Fred::Fred() { x_ = 不管; }。 在这种情况下,表达式无论如何 导致一个单独的临时对象 被创建,这个临时对象 被传递到 x_ 对象的 赋值运算符。然后 临时对象在 ;.那是低效的。

    好像这还不够糟糕,还有 另一个低效率的来源 在构造函数中使用赋值: 成员对象将完全 默认构造 构造函数,这可能,对于 例如,分配一些默认金额 内存或打开一些默认文件。 如果所有这些工作都是徒劳的 任何表达和/或 赋值运算符导致对象 关闭该文件和/或释放该文件 内存(例如,如果默认 构造函数没有分配大 足够的内存池或者如果它打开了 错误的文件)。

    结论:所有其他的东西 相等,如果你的代码运行得更快 你使用初始化列表而不是 比分配。

    注意:没有表现 如果 x_ 的类型有些不同 内置/固有类型,例如 int 或 char* 或浮点数。但即使在这些 情况下,我个人的偏好是 将这些数据成员设置在 初始化列表而不是通过 分配一致性。其他 有利于使用的对称论点 初始化列表甚至为 内置/固有类型:非静态 const 和非静态参考数据 成员不能被赋值 构造函数,所以为了对称它 初始化一切是有意义的 在初始化列表中。

    现在是例外。每条规则都有 例外(嗯;是否“每条规则都有 例外”有例外吗?提醒 哥德尔不完备性的我 定理),并且有几个 “使用初始化”的例外 列表”规则。底线是使用 常识:如果它更便宜,更好, 更快等不使用它们,然后通过 无论如何,不​​要使用它们。这有可能 当你的班级有两个时发生 需要初始化的构造函数 this 对象的数据成员在 不同的订单。或者它可能会发生 当两个数据成员是 自我参照。或者当一个 数据成员需要引用 这个对象,你想避免 关于使用 this 的编译器警告 开始于 { 之前的关键字 构造函数的主体(当你的 特定的编译器恰好发出 那个特别的警告)。或者当你 需要对 a 进行 if/throw 测试 变量(参数、全局等) 在使用该变量之前 初始化您的 this 成员之一。 此列表并不详尽;请 不要给我写信要我添加 另一个“或者当……”。重点是 就是这样:使用常识。

    【讨论】:

    • 一个很好的答案,可以肯定,但可能有点……长。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-10-28
    • 2018-10-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-07
    相关资源
    最近更新 更多