为什么我要使用成员初始化列表?

时间:2021-09-01 21:20:52

I'm partial to using member initialization lists with my constructors... but I've long since forgotten the reasons behind this...

我偏爱使用成员初始化列表和构造函数。但我早就忘记了这背后的原因……

Do you use member initialization lists in your constructors? If so, why? If not, why not?

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

7 个解决方案

#1


204  

For POD class members, it makes no difference, it's just a matter of style. For class members which are classes, then it avoids an unnecessary call to a default constructor. Consider:

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

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

class B
{
public:
    B()
    {
        a.x = 3;
    }
private:
    A a;
};

In this case, the constructor for B will call the default constructor for A, and then initialize a.x to 3. A better way would be for B's constructor to directly call A's constructor in the initializer list:

在这种情况下,B的构造函数将调用A的默认构造函数,然后初始化A。x 3。对于B的构造函数来说,更好的方法是在初始化器列表中直接调用A的构造函数:

B()
  : a(3)
{
}

This would only call A's A(int) constructor and not its default constructor. In this example, the difference is negligible, but imagine if you will that A's default constructor did more, such as allocating memory or opening files. You wouldn't want to do that unnecessarily.

这只会调用A(int)构造函数,而不是它的默认构造函数。在本例中,差异是可以忽略的,但是想象一下,如果您认为A的默认构造函数做得更多,比如分配内存或打开文件。你不会想做不必要的事情。

Furthermore, if a class doesn't have a default constructor, or you have a const member variable, you must use an initializer list:

此外,如果一个类没有默认构造函数,或者您有一个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;
};

#2


34  

Apart from the performance reasons mentioned above, if your class stores references to objects passed as constructor parameters or your class has const variables then you don't have any choice except using initializer lists.

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

#3


11  

  1. Initialization of base class
  2. 初始化基类

One important reason for using constructor initializer list which is not mentioned in answers here is initialization of base class.

使用构造函数初始化列表的一个重要原因是,这里的答案中没有提到的是基类的初始化。

As per the order of construction, base class should be constructed before child class. Without constructor initializer list, this is possible if your base class has default constructor which will be called just before entering the constructor of child class.

按照建设顺序,基类应该在子类之前构建。没有构造函数初始化列表,如果您的基类有默认构造函数,在进入子类的构造函数之前调用它,这是可能的。

But, if your base class has only parameterized constructor, then you must use constructor initializer list to ensure that your base class is initialized before child class.

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

  1. Initialization of Subobjects which only have parameterized constructors

    只有参数化构造函数的子对象的初始化。

  2. Efficiency

    效率

Using constructor initializer list, you initialize your data members to exact state which you need in your code rather than first initializing them to their default state & then changing their state to the one you need in your code.

使用构造函数初始化器列表,您将您的数据成员初始化到您的代码中需要的确切状态,而不是初始化它们到它们的默认状态,然后将它们的状态更改为代码中需要的状态。

  1. Initializing non-static const data members
  2. 初始化非静态const数据成员。

If non-static const data members in your class have default constructors & you don't use constructor initializer list, you won't be able to initialize them to intended state as they will be initialized to their default state.

如果您的类中的非静态const数据成员有默认构造函数&您不使用构造函数初始化列表,那么您将无法将它们初始化为目标状态,因为它们将被初始化为默认状态。

  1. Initialization of reference data members
  2. 引用数据成员的初始化。

Reference data members must be intialized when compiler enters constructor as references can't be just declared & initialized later. This is possible only with constructor initializer list.

当编译器进入构造函数时,引用数据成员必须被激活,因为引用不能在稍后被声明和初始化。这只能通过构造函数初始化列表来实现。

#4


6  

Next to the performance issues, there is another one very important which I'd call code maintainability and extendibility.

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

If a T is POD and you start preferring initialization list, then if one time T will change to a non-POD type, you won't need to change anything around initialization to avoid unnecessary constructor calls because it is already optimised.

如果一个T是POD,并且您开始喜欢初始化列表,那么如果一个时间T将变为非POD类型,那么您就不需要改变任何初始化来避免不必要的构造函数调用,因为它已经被优化了。

If type T does have default constructor and one or more user-defined constructors and one time you decide to remove or hide the default one, then if initialization list was used, you don't need to update code if your user-defined constructors because they are already correctly implemented.

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

Same with const members or reference members, let's say initially T is defined as follows:

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

struct T
{
    T() { a = 5; }
private:
    int a;
};

Next, you decide to qualify a as const, if you would use initialization list from the beginning, then this was a single line change, but having the T defined as above, it also requires to dig the constructor definition to remove assignment:

接下来,您决定将a限定为const,如果您从一开始就使用初始化列表,那么这是一个单行更改,但是有了上面定义的T,它还需要挖掘构造函数定义来移除赋值:

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

It's not a secret that maintenance is far easier and less error-prone if code was written not by a "code monkey" but by an engineer who makes decisions based on deeper consideration about what he is doing.

如果代码不是由“代码猴子”编写的,而是由一个基于对他所做的事情的更深入考虑做出决策的工程师来编写的,那么维护工作就容易得多,而且容易出错,这并不是一个秘密。

#5


4  

Before the body of the constructor is run, all of the constructors for its parent class and then for its fields are invoked. By default, the no-argument constructors are invoked. Initialization lists allow you to choose which constructor is called and what arguments that constructor receives.

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

If you have a reference or a const field, or if one of the classes used does not have a default constructor, you must use an initialization list.

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

#6


2  

// Without Initializer List
class MyClass {
    Type variable;
public:
    MyClass(Type a) {  // Assume that Type is an already
                     // declared class and it has appropriate 
                     // constructors and operators
        variable = a;
    }
};

Here compiler follows following steps to create an object of type MyClass
1. Type’s constructor is called first for “a”.
2. The assignment operator of “Type” is called inside body of MyClass() constructor to assign

在这里,编译器遵循以下步骤创建MyClass 1的对象。类型的构造函数首先被称为“a”。2。“类型”的赋值操作符在MyClass()构造函数的内部调用。

variable = a;
  1. And then finally destructor of “Type” is called for “a” since it goes out of scope.

    最后,“类型”的析构函数被称为“a”,因为它超出了范围。

    Now consider the same code with MyClass() constructor with Initializer List

    现在,考虑与初始化器列表的MyClass()构造函数相同的代码。

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

    With the Initializer List, following steps are followed by compiler:

    在初始化器列表中,接下来是编译器:

    1. Copy constructor of “Type” class is called to initialize : variable(a). The arguments in initializer list are used to copy construct “variable” directly.
    2. “类型”类的复制构造函数被调用来初始化:变量(a)。初始化器列表中的参数用于直接复制构造“变量”。
    3. Destructor of “Type” is called for “a” since it goes out of scope.
    4. “类型”的析构函数被称为“a”,因为它超出了范围。

#7


0  

Syntax:

语法:

  class Sample
  {
     public:
         int Sam_x;
         int Sam_y;

     Sample(): Sam_x(1), Sam_y(2)     /* Classname: Initialization List */
     {
           // Constructor body
     }
  };

Need of Initialization list:

需要初始化列表:

 class Sample
 {
     public:
         int Sam_x;
         int Sam_y;

     Sample()     */* Object and variables are created - i.e.:declaration of variables */*
     { // Constructor body starts 

         Sam_x = 1;      */* Defining a value to the variable */* 
         Sam_y = 2;

     } // Constructor body ends
  };

in the above program, When the class’s constructor is executed, Sam_x and Sam_y are created. Then in constructor body, those member data variables are defined.

在上面的程序中,当类的构造函数被执行时,将创建Sam_x和Sam_y。然后在构造函数体中定义这些成员数据变量。

Use cases:

用例:

  1. Const and Reference variables in a Class
  2. 类中的常量和引用变量。

In C, variables must be defined during creation. the same way in C++, we must initialize the Const and Reference variable during object creation by using Initialization list. if we do initialization after object creation (Inside constructor body), we will get compile time error.

在C中,变量必须在创建期间定义。在c++中,我们必须在对象创建过程中使用初始化列表初始化Const和Reference变量。如果我们在对象创建之后进行初始化(在构造函数体中),我们将得到编译时错误。

  1. Member objects of Sample1 (base) class which do not have default constructor

    没有默认构造函数的Sample1(基)类的成员对象。

     class Sample1 
     {
         int i;
         public:
         Sample1 (int temp)
         {
            i = temp;
         }
     };
    
      // Class Sample2 contains object of Sample1 
     class Sample2
     {
      Sample1  a;
      public:
      Sample2 (int x): a(x)      /* Initializer list must be used */
      {
    
      }
     };
    

While creating object for derived class which will internally calls derived class constructor and calls base class constructor (default). if base class does not have default constructor, user will get compile time error. To avoid, we must have either

在为派生类创建对象时,它将内部调用派生类构造函数并调用基类构造函数(默认)。如果基类没有默认构造函数,用户将会得到编译时错误。为了避免,我们也必须这样做。

 1. Default constructor of Sample1 class
 2. Initialization list in Sample2 class which will call the parametric constructor of Sample1 class (as per above program)
  1. Class constructor’s parameter name and Data member of a Class are same:

    类构造函数的参数名和类的数据成员相同:

     class Sample3 {
        int i;         /* Member variable name : i */  
        public:
        Sample3 (int i)    /* Local variable name : i */ 
        {
            i = i;
            print(i);   /* Local variable: Prints the correct value which we passed in constructor */
        }
        int getI() const 
        { 
             print(i);    /*global variable: Garbage value is assigned to i. the expected value should be which we passed in constructor*/
             return i; 
        }
     };
    

As we all know, local variable having highest priority then global variable if both variables are having same name. In this case, the program consider "i" value {both left and right side variable. i.e: i = i} as local variable in Sample3() constructor and Class member variable(i) got override. To avoid, we must use either

我们都知道,如果两个变量都有相同的名称,那么局部变量具有最高优先级,那么全局变量。在本例中,程序考虑“i”值(左和右变量)。我。在Sample3()构造函数和类成员变量(i)中,i = i}为局部变量。为了避免,我们也必须使用。

  1. Initialization list 
  2. this operator.

#1


204  

For POD class members, it makes no difference, it's just a matter of style. For class members which are classes, then it avoids an unnecessary call to a default constructor. Consider:

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

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

class B
{
public:
    B()
    {
        a.x = 3;
    }
private:
    A a;
};

In this case, the constructor for B will call the default constructor for A, and then initialize a.x to 3. A better way would be for B's constructor to directly call A's constructor in the initializer list:

在这种情况下,B的构造函数将调用A的默认构造函数,然后初始化A。x 3。对于B的构造函数来说,更好的方法是在初始化器列表中直接调用A的构造函数:

B()
  : a(3)
{
}

This would only call A's A(int) constructor and not its default constructor. In this example, the difference is negligible, but imagine if you will that A's default constructor did more, such as allocating memory or opening files. You wouldn't want to do that unnecessarily.

这只会调用A(int)构造函数,而不是它的默认构造函数。在本例中,差异是可以忽略的,但是想象一下,如果您认为A的默认构造函数做得更多,比如分配内存或打开文件。你不会想做不必要的事情。

Furthermore, if a class doesn't have a default constructor, or you have a const member variable, you must use an initializer list:

此外,如果一个类没有默认构造函数,或者您有一个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;
};

#2


34  

Apart from the performance reasons mentioned above, if your class stores references to objects passed as constructor parameters or your class has const variables then you don't have any choice except using initializer lists.

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

#3


11  

  1. Initialization of base class
  2. 初始化基类

One important reason for using constructor initializer list which is not mentioned in answers here is initialization of base class.

使用构造函数初始化列表的一个重要原因是,这里的答案中没有提到的是基类的初始化。

As per the order of construction, base class should be constructed before child class. Without constructor initializer list, this is possible if your base class has default constructor which will be called just before entering the constructor of child class.

按照建设顺序,基类应该在子类之前构建。没有构造函数初始化列表,如果您的基类有默认构造函数,在进入子类的构造函数之前调用它,这是可能的。

But, if your base class has only parameterized constructor, then you must use constructor initializer list to ensure that your base class is initialized before child class.

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

  1. Initialization of Subobjects which only have parameterized constructors

    只有参数化构造函数的子对象的初始化。

  2. Efficiency

    效率

Using constructor initializer list, you initialize your data members to exact state which you need in your code rather than first initializing them to their default state & then changing their state to the one you need in your code.

使用构造函数初始化器列表,您将您的数据成员初始化到您的代码中需要的确切状态,而不是初始化它们到它们的默认状态,然后将它们的状态更改为代码中需要的状态。

  1. Initializing non-static const data members
  2. 初始化非静态const数据成员。

If non-static const data members in your class have default constructors & you don't use constructor initializer list, you won't be able to initialize them to intended state as they will be initialized to their default state.

如果您的类中的非静态const数据成员有默认构造函数&您不使用构造函数初始化列表,那么您将无法将它们初始化为目标状态,因为它们将被初始化为默认状态。

  1. Initialization of reference data members
  2. 引用数据成员的初始化。

Reference data members must be intialized when compiler enters constructor as references can't be just declared & initialized later. This is possible only with constructor initializer list.

当编译器进入构造函数时,引用数据成员必须被激活,因为引用不能在稍后被声明和初始化。这只能通过构造函数初始化列表来实现。

#4


6  

Next to the performance issues, there is another one very important which I'd call code maintainability and extendibility.

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

If a T is POD and you start preferring initialization list, then if one time T will change to a non-POD type, you won't need to change anything around initialization to avoid unnecessary constructor calls because it is already optimised.

如果一个T是POD,并且您开始喜欢初始化列表,那么如果一个时间T将变为非POD类型,那么您就不需要改变任何初始化来避免不必要的构造函数调用,因为它已经被优化了。

If type T does have default constructor and one or more user-defined constructors and one time you decide to remove or hide the default one, then if initialization list was used, you don't need to update code if your user-defined constructors because they are already correctly implemented.

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

Same with const members or reference members, let's say initially T is defined as follows:

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

struct T
{
    T() { a = 5; }
private:
    int a;
};

Next, you decide to qualify a as const, if you would use initialization list from the beginning, then this was a single line change, but having the T defined as above, it also requires to dig the constructor definition to remove assignment:

接下来,您决定将a限定为const,如果您从一开始就使用初始化列表,那么这是一个单行更改,但是有了上面定义的T,它还需要挖掘构造函数定义来移除赋值:

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

It's not a secret that maintenance is far easier and less error-prone if code was written not by a "code monkey" but by an engineer who makes decisions based on deeper consideration about what he is doing.

如果代码不是由“代码猴子”编写的,而是由一个基于对他所做的事情的更深入考虑做出决策的工程师来编写的,那么维护工作就容易得多,而且容易出错,这并不是一个秘密。

#5


4  

Before the body of the constructor is run, all of the constructors for its parent class and then for its fields are invoked. By default, the no-argument constructors are invoked. Initialization lists allow you to choose which constructor is called and what arguments that constructor receives.

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

If you have a reference or a const field, or if one of the classes used does not have a default constructor, you must use an initialization list.

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

#6


2  

// Without Initializer List
class MyClass {
    Type variable;
public:
    MyClass(Type a) {  // Assume that Type is an already
                     // declared class and it has appropriate 
                     // constructors and operators
        variable = a;
    }
};

Here compiler follows following steps to create an object of type MyClass
1. Type’s constructor is called first for “a”.
2. The assignment operator of “Type” is called inside body of MyClass() constructor to assign

在这里,编译器遵循以下步骤创建MyClass 1的对象。类型的构造函数首先被称为“a”。2。“类型”的赋值操作符在MyClass()构造函数的内部调用。

variable = a;
  1. And then finally destructor of “Type” is called for “a” since it goes out of scope.

    最后,“类型”的析构函数被称为“a”,因为它超出了范围。

    Now consider the same code with MyClass() constructor with Initializer List

    现在,考虑与初始化器列表的MyClass()构造函数相同的代码。

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

    With the Initializer List, following steps are followed by compiler:

    在初始化器列表中,接下来是编译器:

    1. Copy constructor of “Type” class is called to initialize : variable(a). The arguments in initializer list are used to copy construct “variable” directly.
    2. “类型”类的复制构造函数被调用来初始化:变量(a)。初始化器列表中的参数用于直接复制构造“变量”。
    3. Destructor of “Type” is called for “a” since it goes out of scope.
    4. “类型”的析构函数被称为“a”,因为它超出了范围。

#7


0  

Syntax:

语法:

  class Sample
  {
     public:
         int Sam_x;
         int Sam_y;

     Sample(): Sam_x(1), Sam_y(2)     /* Classname: Initialization List */
     {
           // Constructor body
     }
  };

Need of Initialization list:

需要初始化列表:

 class Sample
 {
     public:
         int Sam_x;
         int Sam_y;

     Sample()     */* Object and variables are created - i.e.:declaration of variables */*
     { // Constructor body starts 

         Sam_x = 1;      */* Defining a value to the variable */* 
         Sam_y = 2;

     } // Constructor body ends
  };

in the above program, When the class’s constructor is executed, Sam_x and Sam_y are created. Then in constructor body, those member data variables are defined.

在上面的程序中,当类的构造函数被执行时,将创建Sam_x和Sam_y。然后在构造函数体中定义这些成员数据变量。

Use cases:

用例:

  1. Const and Reference variables in a Class
  2. 类中的常量和引用变量。

In C, variables must be defined during creation. the same way in C++, we must initialize the Const and Reference variable during object creation by using Initialization list. if we do initialization after object creation (Inside constructor body), we will get compile time error.

在C中,变量必须在创建期间定义。在c++中,我们必须在对象创建过程中使用初始化列表初始化Const和Reference变量。如果我们在对象创建之后进行初始化(在构造函数体中),我们将得到编译时错误。

  1. Member objects of Sample1 (base) class which do not have default constructor

    没有默认构造函数的Sample1(基)类的成员对象。

     class Sample1 
     {
         int i;
         public:
         Sample1 (int temp)
         {
            i = temp;
         }
     };
    
      // Class Sample2 contains object of Sample1 
     class Sample2
     {
      Sample1  a;
      public:
      Sample2 (int x): a(x)      /* Initializer list must be used */
      {
    
      }
     };
    

While creating object for derived class which will internally calls derived class constructor and calls base class constructor (default). if base class does not have default constructor, user will get compile time error. To avoid, we must have either

在为派生类创建对象时,它将内部调用派生类构造函数并调用基类构造函数(默认)。如果基类没有默认构造函数,用户将会得到编译时错误。为了避免,我们也必须这样做。

 1. Default constructor of Sample1 class
 2. Initialization list in Sample2 class which will call the parametric constructor of Sample1 class (as per above program)
  1. Class constructor’s parameter name and Data member of a Class are same:

    类构造函数的参数名和类的数据成员相同:

     class Sample3 {
        int i;         /* Member variable name : i */  
        public:
        Sample3 (int i)    /* Local variable name : i */ 
        {
            i = i;
            print(i);   /* Local variable: Prints the correct value which we passed in constructor */
        }
        int getI() const 
        { 
             print(i);    /*global variable: Garbage value is assigned to i. the expected value should be which we passed in constructor*/
             return i; 
        }
     };
    

As we all know, local variable having highest priority then global variable if both variables are having same name. In this case, the program consider "i" value {both left and right side variable. i.e: i = i} as local variable in Sample3() constructor and Class member variable(i) got override. To avoid, we must use either

我们都知道,如果两个变量都有相同的名称,那么局部变量具有最高优先级,那么全局变量。在本例中,程序考虑“i”值(左和右变量)。我。在Sample3()构造函数和类成员变量(i)中,i = i}为局部变量。为了避免,我们也必须使用。

  1. Initialization list 
  2. this operator.