c ++中初始化程序和默认初始化程序列表之间的区别

时间:2011-04-28 09:07:24

标签: c++ constructor

HI, 我有一个问题,但很长一段时间都找不到答案,即 关于参数的以下2个陈述之间有什么区别 初始化?

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

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

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

提前致谢

4 个答案:

答案 0 :(得分:24)

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

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

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

  • 必须使用有效值和
  • 初始化这些值
  • 一旦建成,重新分配它们是非法的。

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

一个值得注意的警告是,成员初始化的顺序由它们的声明顺序定义,按初始化列表中列出的顺序定义。 E.g。

class Example {
  int a, b, c;

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

产生未定义的行为,因为bc之前初始化,因此具有未定义的值。为了避免混淆以及可能存在这些微妙的错误,请始终按照在类中声明的顺序列出初始化列表中的成员。

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

答案 1 :(得分:5)

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

我提供了一个样本来解释这两个初始化之间的区别:

 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;
    }

答案 2 :(得分:1)

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

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

此外,您无法在多种情况下使用赋值,例如,当声明类成员 const 时。

答案 3 :(得分:1)

来自Marshall Cline的section 10.6 of the C++ FAQ

  

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

     

考虑以下构造函数   初始化成员对象x_   使用初始化列表:   Fred :: Fred():x_(无论如何){}。该   这样做最常见的好处是   改善了表现。例如,如果   什么是相同的表达   键入成员变量x_,结果   无论表达是什么   直接在x_里面构造 -   编译器不会单独复制   对象。即使类型是   不一样,编译通常是   能够做得更好   初始化列表比用   分配。

     

另一种(效率低下)的构建方式   构造函数是通过赋值来实现的   as:Fred :: Fred(){x_ = what; }。   在这种情况下表达无论如何   导致一个单独的临时对象   被创建,这个临时对象   传递给x_对象   赋值运算符。然后   临时对象被破坏了   ;。那效率很低。

     

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

     

结论:所有其他事情都存在   等于,如果你的代码运行得更快   你使用初始化列表   而不是任务。

     

注意:没有表现   如果x_的类型是一些的差异   内置/内在类型,例如int   或char *或浮点数。但即使在这些   个案,我个人的偏好是   设置那些数据成员   初始化列表而不是via   分配一致性。另一个   对称性论证有利于使用   初始化列表甚至为   内置/内在类型:非静态   const和非静态参考数据   成员无法分配值   构造函数,所以对称它   有意义地初始化一切   在初始化列表中。

     

现在是例外。每条规则都有   例外(嗯;确实“每条规则都有   例外“有例外吗?提醒   我对哥德尔的不完整性   定理),还有几个   “使用初始化的例外”   列出“规则。底线是使用   常识:如果它更便宜,更好,   更快,等等不使用它们,然后通过   一切,不要使用它们。这有可能   当你的班级有两个时发生   需要初始化的构造函数   该对象的数据成员   不同的订单。或者它可能发生   当两个数据成员是   自我指涉。或者当一个   data-member需要引用   这个对象,你要避免一个   关于使用它的编译器警告   {开头的{之前的关键字   构造函数的主体(当你的   特定的编译器碰巧问题   特别警告)。或者当你   需要对a进行if / throw测试   变量(参数,全局等)   在使用该变量之前   初始化你的一个成员。   这份清单并非详尽无遗;请   别写我要我加   另一个“或者什么时候......”。重点是   简单地说:使用常识。