何时使用C++中的初始化列表?

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

null

C++

#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) 对于非静态常量数据成员的初始化: 必须使用初始值设定项列表初始化常量数据成员。在下面的示例中,“t”是测试类的常量数据成员,并使用初始值设定项列表进行初始化。在初始值设定项列表中初始化常量数据成员的原因是,没有为常量数据成员单独分配内存,它被折叠在符号表中,因此我们需要在初始值设定项列表中初始化它。 此外,它是一个参数化构造函数,我们不需要调用赋值运算符,这意味着我们避免了一个额外的操作。

C++

#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”是测试类的引用成员,并使用初始值设定项列表进行初始化。

C++

// 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”。

C++

#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点类似,基类的参数化构造函数只能使用初始值设定项列表调用。

C++

#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) 当构造函数的参数名与数据成员相同时 如果构造函数的参数名与数据成员名相同,则必须使用 this指针 或初始化列表。在下面的示例中,A()的成员名和参数名都是“i”。

C++

#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) 出于性能原因: 最好初始化初始化器列表中的所有类变量,而不是在主体中赋值。考虑下面的例子:

C++

// 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”调用类型的构造函数。 2.在MyClass()构造函数的主体内调用“Type”的赋值运算符进行赋值

    variable = a;

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

现在考虑与MyCype()构造函数相同的代码,使用初始化列表

C++

// 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.“Type”的析构函数被称为“a”,因为它超出了范围。 从这个例子中我们可以看到,如果我们在构造函数体中使用赋值,有三个函数调用:构造函数+析构函数+一个加法赋值运算符调用。如果我们使用初始值设定项列表,则只有两个函数调用:复制构造函数+析构函数调用。看见 就这一点发布一个运行示例。 在“真实”应用程序中,这种赋值惩罚会更大,因为其中会有许多这样的变量。幸亏 ptr 谢谢你补充这一点。 如果您发现任何不正确的地方,或者您想分享有关上述主题的更多信息,请写下评论。

© 版权声明
THE END
喜欢就支持一下吧
点赞12 分享