使用初始化列表来初始化字段和在构造函数中初始化它们有什么区别?
What is the difference between using initialization lists to initialize fields and initialize them inside the constructor?
在一些教程中(例如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;
}
事实上,第一个 class 没有给我任何错误,但在第二个 class 中有一个 assignment of read-only member
。所以,这些是问题:
这两种初始化方法的真正区别是什么?
使用初始化列表是初始化 class 的 const
成员的唯一方法吗?
注意:我在网上阅读了使用 delegating constructors 来避免这个问题,但我不清楚它们的用途和它们的实际作用。
您实际上从未初始化构造函数主体中的任何成员变量,这是主要区别。
所有基础成员在进入构造函数体之前被初始化。初始化列表可以帮助您做到这一点。如果初始化列表中缺少任何成员,则它们是默认构造的,除了未初始化的 POD 类型。如果成员没有可访问的默认构造函数,那么您必须将其包含在带有适当参数的初始化列表中。
请注意,基本成员初始化列表中成员的初始化顺序是而不是它们在初始化列表中出现的顺序本身,但它们出现在 class 声明中的顺序:如果你写了 MyClass1(int a, int b) : b(b), a(a) {};
,那么 a
将 still 首先初始化。记住这一点非常重要。
尽可能使用基本成员初始化是首选,因为它往往会导致更高的程序稳定性。
在第一种情况下,当在内存初始化器列表中初始化数据成员时,会调用其构造函数,即创建数据成员。
在第二种情况下,首先对数据成员进行默认初始化,然后使用相应的复制赋值运算符。即在第二种情况下,有以下过程步骤:
- 正在使用默认构造函数创建对象;
- 调用复制赋值运算符给
对象。
例如,常量对象或引用应在创建时进行初始化。因此,您可能不会首先使用默认构造函数在没有初始化程序的情况下创建它们,然后重新分配它们。
或者例如一个对象可以没有默认构造函数。在这种情况下,您必须使用带参数的构造函数在 mem-initializer 列表中显式初始化它。
查看它的一种简单方法是与局部变量建立联系:
使用初始化列表等同于局部变量的这种观点:
int a = 1;
int b = 2;
第二种形式,在构造函数中赋值相当于这样:
int a;
int b;
a = 1;
b = 2;
您可以看到对于 const 或没有默认构造函数的对象,这可能是一个问题:
常量成员
好的:
const int a = 1;
const int b = 2;
不行:
const int a;
const int b;
a = 1;
b = 2;
具有已删除或不可访问的默认构造函数的类型
例如:
class X {
public:
X() = delete; // default constructor deleted
X(int){}; // constructor with an int parameter
};
好的:
X x(1);
不行:
X x;
x = X(1);
第三个选项:In-class member initializers (since c++11)
class A {
public:
const int a = 10;
};
在一些教程中(例如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;
}
事实上,第一个 class 没有给我任何错误,但在第二个 class 中有一个 assignment of read-only member
。所以,这些是问题:
这两种初始化方法的真正区别是什么?
使用初始化列表是初始化 class 的 const
成员的唯一方法吗?
注意:我在网上阅读了使用 delegating constructors 来避免这个问题,但我不清楚它们的用途和它们的实际作用。
您实际上从未初始化构造函数主体中的任何成员变量,这是主要区别。
所有基础成员在进入构造函数体之前被初始化。初始化列表可以帮助您做到这一点。如果初始化列表中缺少任何成员,则它们是默认构造的,除了未初始化的 POD 类型。如果成员没有可访问的默认构造函数,那么您必须将其包含在带有适当参数的初始化列表中。
请注意,基本成员初始化列表中成员的初始化顺序是而不是它们在初始化列表中出现的顺序本身,但它们出现在 class 声明中的顺序:如果你写了 MyClass1(int a, int b) : b(b), a(a) {};
,那么 a
将 still 首先初始化。记住这一点非常重要。
尽可能使用基本成员初始化是首选,因为它往往会导致更高的程序稳定性。
在第一种情况下,当在内存初始化器列表中初始化数据成员时,会调用其构造函数,即创建数据成员。
在第二种情况下,首先对数据成员进行默认初始化,然后使用相应的复制赋值运算符。即在第二种情况下,有以下过程步骤:
- 正在使用默认构造函数创建对象;
- 调用复制赋值运算符给 对象。
例如,常量对象或引用应在创建时进行初始化。因此,您可能不会首先使用默认构造函数在没有初始化程序的情况下创建它们,然后重新分配它们。 或者例如一个对象可以没有默认构造函数。在这种情况下,您必须使用带参数的构造函数在 mem-initializer 列表中显式初始化它。
查看它的一种简单方法是与局部变量建立联系:
使用初始化列表等同于局部变量的这种观点:
int a = 1; int b = 2;
第二种形式,在构造函数中赋值相当于这样:
int a; int b; a = 1; b = 2;
您可以看到对于 const 或没有默认构造函数的对象,这可能是一个问题:
常量成员
好的:
const int a = 1; const int b = 2;
不行:
const int a; const int b; a = 1; b = 2;
具有已删除或不可访问的默认构造函数的类型
例如:
class X {
public:
X() = delete; // default constructor deleted
X(int){}; // constructor with an int parameter
};
好的:
X x(1);
不行:
X x; x = X(1);
第三个选项:In-class member initializers (since c++11)
class A {
public:
const int a = 10;
};