【问题标题】:Initializing fields in constructor - initializer list vs constructor body [duplicate]初始化构造函数中的字段-初始化列表与构造函数主体[重复]
【发布时间】:2012-04-11 19:02:53
【问题描述】:

我已经在 c++ 中工作了一段时间,但我不确定两者之间的区别

public : Thing(int _foo, int _bar): member1(_foo), member2(_bar){}

public : Thing(int _foo, int _bar){
    member1 = _foo;
    member2 = _bar;
}

我感觉它们做同样的事情,但这两种语法之间是否存在实际差异。其中一个是否比另一个更安全,它们处理默认参数的方式是否不同。

不完全习惯第一个例子,所以如果我在上面犯了错误,我深表歉意。

【问题讨论】:

    标签: c++ parameters


    【解决方案1】:

    如果member1member2 是非POD(即非Plain Old

    strong>Data) 类型:

    public : Thing(int _foo, int _bar){
        member1 = _foo;
        member2 = _bar;
    }
    

    等价于

    public : Thing(int _foo, int _bar) : member1(), member2(){
        member1 = _foo;
        member2 = _bar;
    }
    

    因为它们会在构造函数体开始执行之前被初始化,所以基本上完​​成了两次工作。这也意味着,如果这些成员的类型没有默认构造函数,那么您的代码将不会编译。

    【讨论】:

    • 我一直在使用自制的数据类型,即使我没有默认构造函数,并且使用我的第二个示例,我也从未在 gcc 或 VS2008/2010 上遇到任何此类编译器错误
    • @gardian06 如果你没有构造函数,编译器会为你生成一个默认的。尝试添加带有一些参数的构造函数,它会失败。
    • 我的意思是有一个参数化的构造函数,但没有默认值,然后用我的第二个例子做工作,给自定义对象的构造函数一个参数。没有错误。在制作 ADT 时,没有默认构造函数并非闻所未闻
    • @gardian06 听起来不太对劲。你有一个可编译的小例子吗?
    • 工作完成了两次,不是真的。这只是更多的工作,而不是两倍的工作。
    【解决方案2】:

    第一个是推荐的最佳实践,因为它更惯用并且避免为具有默认构造函数的类型(即非原始类型)重新初始化字段。

    当你只在构造函数体中初始化一个成员时,如果编译器可以的话,编译器会为你生成一个默认的成员初始化语句,所以你最终会双重初始化它。在某些情况下这可能不是什么大问题,但如果构造对象的成本很高,则可能会导致严重的性能开销。

    更新

    但是,没有(n 显式定义或生成的)默认构造函数的用户定义类型不能以这种方式初始化,因此会产生编译器错误。 const 和 reference 字段也是如此——它们只能在成员初始化器列表中显式初始化。

    【讨论】:

    • 所以如果我使用第二个示例,这是否意味着成员在技术上被初始化了两次,那么如果这些类型不存在默认构造函数呢
    • 那么编译器就不高兴了。另外 - 初始化列表是初始化对象的const 字段的唯一方法。
    • @gardian06,是的,如果有默认构造函数的话。否则对于原始类型没有技术差异,但对于用户定义的类型会产生编译器错误,因为编译器无法初始化这些字段。 const 和引用字段也是如此 - 这些只能在成员初始化器列表中初始化。
    • 那么是否可以将这两种方法结合起来,使用 const 和本机类型的初始化列表,然后在主体中进行更深入的工作?我只见过它们分开使用
    • 我使用用户定义类型已经有一段时间了,无论有没有默认构造函数,并且没有使用初始化列表,我都没有遇到过问题。
    【解决方案3】:

    唯一要添加到Péter Török 答案的是初始化列表是初始化对象的 const 成员的唯一方法,即:

    class foo
    {
    public:
    
        foo(int value)
            : myConstValue(value)
        {};
    
        foo()
        {
            myConstValue = 0; // <=== Error! myConstValue is const (RValue), you can't assign!
        };
    
    private:
        const int myConstValue;
    }
    

    【讨论】:

    • 这是因为const变量是不能被改变的,而且在创建的时候还要给它们一个初始值。引用类型也一样。
    【解决方案4】:

    在您的示例代码中,第一个是构造函数初始化,第二个是构造函数体内的赋值。

    构造函数初始化列表是进行所有成员初始化的最佳方式,因为它提高了性能。

    class A
    {
    string name;
    public:
    A(string myname):name(myname) {}
    }
    

    在上述情况下,编译器不会创建一个临时对象来进行初始化。但是在以下情况下:

    A::A()
    {
        name = myname;
    }
    

    创建一个单独的临时对象,并将这个临时对象传递给string 的赋值运算符以赋值给name。然后临时对象被销毁,效率不高。

    注意:必须在构造函数初始化列表中初始化引用或 const 成员。它们不能在构造函数的主体中“赋值”。

    【讨论】:

      【解决方案5】:

      除了其他答案,我想提一下构造函数初始化仅用于初始化 成员变量。

      class Demo
      {
          int a;
          int b;
      public:
          Demo(int a,int b):a(a),b(b)
          {
      
          }
      
      };
      

      如果我们在构造函数中初始化 a 和 b ,那将是自赋值。

      【讨论】:

      • 不错,如果要在构造函数体内进行赋值,也可以使用this-&gt;a = a;区分成员变量和参数。
      【解决方案6】:

      首先是初始化,使用初始化列表,其次是默认构造,然后是赋值。第一个至少和第二个一样快,并且比第二个更好。

      【讨论】:

      • 如果说没有要初始化的类型的默认构造函数,使用第二个会产生问题吗?
      • 是的(除非它是像 int 之类的内置类型),如果我没记错的话应该会出现编译器错误。
      猜你喜欢
      • 2012-03-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多