个性化阅读
专注于IT技术分析

C++中什么时候使用初始化列表?

初始化程序列表用于初始化类的数据成员。构造函数将要初始化的成员列表表示为逗号分隔的列表, 后跟冒号。下面是一个使用初始化列表初始化Point类的x和y的示例。

#include<iostream>
using namespace std;
  
class Point {
private :
     int x;
     int y;
public :
     Point( int i = 0, int j = 0):x(i), y(j) {} 
     /*  The above use of Initializer list is optional as the 
         constructor can also be written as:
         Point(int i = 0, int j = 0) {
             x = i;
             y = j;
         }
     */ 
      
     int getX() const { return x;}
     int getY() const { return y;}
};
  
int main() {
   Point t1(10, 15);
   cout<<"x = " <<t1.getX()<<", " ;
   cout<<"y = " <<t1.getY();
   return 0;
}
  
/* OUTPUT:
    x = 10, y = 15
*/

上面的代码只是初始化列表的语法示例。在上面的代码中, x和y也可以在构造函数中轻松初始化。但是在某些情况下, 无法初始化构造函数内部的数据成员, 而必须使用”初始化列表”。以下是这种情况:

1)对于非静态const数据成员的初始化:

const数据成员必须使用初始化列表初始化。在以下示例中, ” t”是Test类的const数据成员, 并使用初始化列表进行初始化。在初始化列表中初始化const数据成员的原因是因为没有为const数据成员单独分配内存, 因此将其折叠在符号表中, 因此我们需要在初始化列表中对其进行初始化。

另外, 它是一个复制构造函数, 我们不需要调用赋值运算符, 这意味着我们避免了一个额外的操作。

#include<iostream>
using namespace std;
  
class Test {
     const int t;
public :
     Test( int t):t(t) {}  //Initializer list must be used
     int getT() { return t; }
};
  
int main() {
     Test t1(10);
     cout<<t1.getT();
     return 0;
}
  
/* OUTPUT:
    10 
*/

2)对于参考成员的初始化:

引用成员必须使用初始化列表进行初始化。在以下示例中, ” t”是Test类的引用成员, 并使用初始化列表进行初始化。

//Initialization of reference data members
#include<iostream>
using namespace std;
  
class Test {
     int &t;
public :
     Test( int &t):t(t) {}  //Initializer list must be used
     int getT() { return t; }
};
  
int main() {
     int x = 20;
     Test t1(x);
     cout<<t1.getT()<<endl;
     x = 30;
     cout<<t1.getT()<<endl;
     return 0;
}
/* OUTPUT:
     20
     30
  */

3)对于没有默认构造函数的成员对象的初始化:

在以下示例中, 类” A”的对象” a”是类” B”的数据成员, 而” A”没有默认的构造函数。初始化列表必须用于初始化” a”。

#include <iostream>
using namespace std;
  
class A {
     int i;
public :
     A( int );
};
  
A::A( int arg) {
     i = arg;
     cout <<"A's Constructor called: Value of i: " <<i <<endl;
}
  
//Class B contains object of A
class B {
     A a;
public :
     B( int );
};
  
B::B( int x):a(x) {  //Initializer list must be used
     cout <<"B's Constructor called" ;
}
  
int main() {
     B obj(10);
     return 0;
}
/* OUTPUT:
     A's Constructor called: Value of i: 10
     B's Constructor called
*/

如果类A同时具有默认构造函数和参数化构造函数, 则如果要使用默认构造函数初始化” a”, 则不必使用”初始化列表”, 而必须使用参数化构造函数初始化” a”。

4)对于基类成员的初始化:像第3点一样, 只能使用Initializer List调用基类的参数化构造函数。

#include <iostream>
using namespace std;
  
class A {
     int i;
public :
     A( int );
};
  
A::A( int arg) {
     i = arg;
     cout <<"A's Constructor called: Value of i: " <<i <<endl;
}
  
//Class B is derived from A
class B: A {
public :
     B( int );
};
  
B::B( int x):A(x) { //Initializer list must be used
     cout <<"B's Constructor called" ;
}
  
int main() {
     B obj(10);
     return 0;
}

5)当构造函数的参数名称与数据成员相同时

如果构造函数的参数名称与数据成员名称相同, 则必须使用以下方法初始化数据成员

这个指针

或初始化列表。在以下示例中, A()的成员名称和参数名称均为” i”。

#include <iostream>
using namespace std;
  
class A {
     int i;
public :
     A( int );
     int getI() const { return i; }
};
  
A::A( int i):i(i) { }  //Either Initializer list or this pointer must be used
/* The above constructor can also be written as 
A::A(int i) { 
     this->i = i;
}
*/
  
int main() {
     A a(10);
     cout<<a.getI();
     return 0;
}
/* OUTPUT:
     10
*/

6)由于性能原因:

最好在Initializer List中初始化所有类变量, 而不是在body内部分配值。考虑以下示例:

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

在这里, 编译器按照以下步骤创建类型为MyClass的对象

1.首先为” a”调用Type的构造函数。

2.在MyClass()构造函数的内部调用”类型”的赋值运算符进行赋值

variable = a;

3.然后, 由于”类型”的析构函数超出范围, 因此最终将其称为” a”。

现在考虑使用带有初始化器列表的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
     }
};

使用初始化程序列表, 编译器执行以下步骤:

1.调用” Type”类的副本构造函数进行初始化:变量(a)。初始化程序列表中的参数用于直接复制构造”变量”。

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

从本示例可以看出, 如果在构造函数主体内使用赋值, 则有三个函数调用:构造函数+析构函数+一个附加赋值运算符调用。如果我们使用Initializer List, 则只有两个函数调用:复制构造函数+析构函数调用。看到这个在这一点上发布一个正在运行的示例。

在”实际”应用程序中会有很多这样的变量, 这种分配惩罚将更多。谢谢ptr添加这一点。

如果发现任何不正确的地方, 或者想分享有关上述主题的更多信息, 请写评论。

赞(0) 打赏
未经允许不得转载:srcmini » C++中什么时候使用初始化列表?
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!

 

觉得文章有用就打赏一下文章作者

微信扫一扫打赏