在构造函数初始值设定项列表中初始化字段与构造函数体

我已经在 C + + 工作了一段时间,但我不确定这两种选择之间的区别:

public : Thing(int _foo, int _bar): member1(_foo), member2(_bar){}

还有

public : Thing(int _foo, int _bar){
member1 = _foo;
member2 = _bar;
}

我有一种感觉,他们做同样的事情,但是有一个实际的区别,在这两种语法之间。其中一个比另一个更安全,它们处理默认参数的方式是否不同。

不完全习惯第一个例子,所以如果我犯了一个错误,我道歉。

76032 次浏览

如果 member1member2是非 POD (即非 < em > Plain ld Data )类型,则它们不相同:

public : Thing(int _foo, int _bar){
member1 = _foo;
member2 = _bar;
}

相当于

public : Thing(int _foo, int _bar) : member1(), member2(){
member1 = _foo;
member2 = _bar;
}

因为它们将在构造函数体开始执行之前被初始化,所以基本上完成了两次工作。这也意味着,如果这些成员的类型没有缺省构造函数,那么您的代码将 没有编译。

第一个是推荐的最佳实践,因为它更加惯用,并且避免重新初始化具有缺省构造函数的类型(即非原始类型)的字段。

如果只在构造函数体内初始化成员,编译器会尽可能为您生成默认的成员初始化语句,因此最终将双重初始化该成员。在某些情况下,这可能不是什么大问题,但是如果构造对象代价很高,则可能会带来严重的性能开销。

更新

但是,没有显式定义或生成缺省构造函数的用户定义类型不能以这种方式初始化,因此会产生编译器错误。对于 const 和 reference 字段也是如此——这些字段只能在成员初始值设定项列表中显式初始化。

首先是初始化,使用初始化器列表,其次是默认构造,然后分配。第一个至少和第二个一样快,而且比第二个更可取。

Peter Török答案中唯一需要补充的是,Initializer List 是初始化对象的 const 成员的唯一方法,即:

class foo
{
public:


foo(int value)
: myConstValue(value)
{};


foo()
{
myConstValue = 0; // <=== Error! myConstValue is const (RValue), you can't assign!
};


private:
const int myConstValue;
}

在您的示例代码中,构造函数初始化中的第一个,以及构造函数体中的赋值。

构造函数初始化列表是完成所有成员初始化的最佳方法,因为它提高了性能。

class A
{
string name;
public:
A(string myname):name(myname) {}
}

在上述情况下,编译器将不会创建一个临时对象来进行初始化:

A::A()
{
name = myname;
}

创建一个单独的临时对象,并将这个临时对象传递给 string的赋值操作符以分配给 name。然后,临时对象被销毁,这并不十分有效。

注意: 必须在构造函数初始化列表中初始化引用或常量成员。它们不能在构造函数体中“分配”。

除了其他答案之外,我还想提到构造函数初始化只是为了初始化 成员变量。

class Demo
{
int a;
int b;
public:
Demo(int a,int b):a(a),b(b)
{


}


};

如果我们在构造函数中初始化 a 和 b,它就是自赋值。