使用初始化列表初始化字段并在构造函数内初始化它们之间有什么区别?

时间:2015-11-04 11:29:00

标签: c++ class constructor const initializer-list

在某些教程(例如http://www.tutorialspoint.com/cplusplus/cpp_constructor_destructor.htm)上,我读到以下两个代码是等效的。

第一个代码:

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

第二段代码:

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

MyClass2::MyClass2(int a, int b){
    this->a = a;
    this->b = b;
}

事实上,他们给了我相同的结果。但是,如果我使用const成员,我将无法再编译代码。

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

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

MyClass2::MyClass2(int a, int b){
    this->a = a;
    this->b = b;
}

事实上,第一堂课没有给我任何错误,但在第二堂课中却有assignment of read-only member。所以,这些是问题:

两种初始化方法的真正区别是什么?

使用初始化列表是初始化类const成员的唯一方法吗?

注意:我在线阅读使用delegating constructors来避免这个问题,但我不清楚它们的用途以及它们的用途。

3 个答案:

答案 0 :(得分:7)

查看它的一种简单方法是与局部变量建立连接:

  1. 使用初始化列表等效于此局部变量视图:

    int a = 1;
    int b = 2;
    
  2. 第二种形式,在构造函数中分配它们等同于:

    int a;
    int b;
    
    a = 1;
    b = 2;
    
  3. 你可以看到这对于const或者没有默认构造函数的对象有什么问题:

    Const成员

    1. 好的:

      const int a = 1;
      const int b = 2;
      
    2. 不行:

      const int a;
      const int b;
      a = 1;
      b = 2;
      
    3. 具有已删除或无法访问的默认构造函数的类型

      e.g:

      class X {
      public:
         X() = delete; // default constructor deleted
         X(int){};     // constructor with an int parameter
      };
      
      1. 确定:

        X x(1);
        
      2. 不行:

        X x;
        x = X(1);
        
      3. 第3个选项:In-class member initializers (since c++11)

        class A {
        public:
           const int a = 10;
        };
        

答案 1 :(得分:2)

你实际上并没有初始化构造函数体中的任何成员变量,这是主要区别。

在输入构造函数体之前初始化所有基本成员。初始化列表可帮助您实现此目的。如果初始化列表中缺少任何成员,则除了未初始化的POD类型之外,它们是默认构造的。如果成员没有可访问的默认构造函数,那么必须将其包含在具有适当参数的初始化列表中。

请注意,基本成员初始化列表中成员初始化的顺序 它们出现在初始化列表本身中的顺序,但是它们出现的顺序类声明:如果你写了MyClass1(int a, int b) : b(b), a(a) {};,那么a 仍然首先被初始化。记住这一点非常重要。

尽可能使用基本成员初始化是首选,因为它往往会带来更高的程序稳定性。

答案 2 :(得分:2)

在第一种情况下,在mem-initializer列表中初始化数据成员时,会调用其构造函数,即创建数据成员。

在第二种情况下,首先对数据成员进行默认初始化,然后使用相应的复制赋值运算符。这是第二种情况,即步骤的步骤:

  1. 使用默认构造函数创建对象;
  2. 调用复制赋值运算符为其赋值 对象。
  3. 例如,常量对象或引用在创建时应初始化。因此,您可能不会在没有使用默认构造函数的初始化程序的情况下创建它们,然后重新分配它们。 或者例如,对象可以没有默认的构造函数。在这种情况下,您必须使用带参数的构造函数在mem-initializer列表中显式初始化它。