为什么在初始化列表中没有创建额外副本的情况下分配数据?

时间:2011-07-13 05:30:17

标签: c++ initialization-list

Parashift很好地解释了初始化列表,但没有解释为什么在ctor体中赋值之前创建了一个额外的变量副本,但是通过初始化列表。
我甚至遇到过使用++ i而不是i ++的建议,因为前者避免在赋值之前创建临时i。对于在监管机构中分配的POD,它是否相同?在赋值发生之前会创建临时变量吗?

换句话说,为什么编译器需要创建变量的额外副本?为什么不能直接分配变量?
为什么吗

3 个答案:

答案 0 :(得分:3)

请考虑以下事项:

struct C { 
    C() { /* construct the object */ }
};

struct X {
    C member;

    X() { member = C(); }
};

X()构造函数与您所说的相同:

X() : member() { member = C(); }

首先,调用C构造函数来构造member数据成员。然后执行X的主体,创建第二个临时C对象并将其分配给member,然后销毁临时对象。


请注意,这仅适用于自动初始化的类型。如果member类型为int或POD类类型(作为示例),则在输入X构造函数的主体时,它将未被初始化。

对于这样的类型,从性能角度来看,无论是初始化初始化列表中的数据成员还是分配给构造函数体中的数据成员,都无关紧要;差异完全是风格。在可能的情况下,为了保持一致性,仍应首选初始化列表。

答案 1 :(得分:2)

专门针对副本提出第一个问题(不涉及“++ i”与“i ++”):

构造函数是一个函数,它有参数。这些参数是作为参数传递的变量的副本(除非使用通过引用传递)。您现在可以操作此副本或使用它执行任何其他操作。然后在分配时,获取参数的副本(可能被操纵)并将其分配给成员变量。当您使用初始化列表时,编译器可以在没有此副本的情况下立即优化分配,因为您不会将其用于除初始化之外的任何操作(并且在将其分配给成员变量之前无法对其进行修改)。

答案 2 :(得分:2)

  

为什么在赋值之前创建变量的额外副本   ctor body,但通过a分配时不会创建额外的副本   初始化列表。

因为赋值遵循初始化。换句话说,赋值是可选的,但初始化是强制性的。您可能没有注意到POD的差异。但对于用户定义的数据类型也是如此。

  

使用++ i代替i ++的建议

对于POD而言,它并不重要。但对于用户定义的类,i++确实创建了一个临时副本。所以最好使用++i

struct A {
  A operator ++ (int)  // example of i++
  {
    A temp = *this;
    this->value ++;
    return temp;    // makes 2 copies "temp" and return value
  }
  A& operator ++ ()  // example of ++i
  {
    this->value ++;
    return *this;  // no copy
  }
};