Parashift 很好地解释了初始化列表,但没有解释为什么在 ctor 主体中分配之前创建了一个额外的变量副本,但是通过初始化列表分配时没有创建额外的副本。
我什至遇到过使用 ++i 而不是 i++ 的建议,因为前者避免了在分配之前创建临时 i 。在 ctor 主体中分配的 POD 是否相同?在赋值发生之前创建了一个临时变量?
换句话说,为什么编译器需要创建一个变量的额外副本?为什么不能直接分配变量?
为什么?
Parashift 很好地解释了初始化列表,但没有解释为什么在 ctor 主体中分配之前创建了一个额外的变量副本,但是通过初始化列表分配时没有创建额外的副本。
我什至遇到过使用 ++i 而不是 i++ 的建议,因为前者避免了在分配之前创建临时 i 。在 ctor 主体中分配的 POD 是否相同?在赋值发生之前创建了一个临时变量?
换句话说,为什么编译器需要创建一个变量的额外副本?为什么不能直接分配变量?
为什么?
考虑以下:
struct C {
C() { /* construct the object */ }
};
struct X {
C member;
X() { member = C(); }
};
X()
构造函数与您所说的相同:
X() : member() { member = C(); }
首先,C
调用构造函数来构造member
数据成员。然后执行主体X
,创建第二个临时C
对象并将其分配给member
,然后销毁临时对象。
请注意,这仅适用于自动初始化的类型。如果member
是类型或 POD 类类型(作为示例),则在输入构造函数int
的主体时将未初始化。X
对于这样的类型,从性能的角度来看,是在初始化列表中初始化数据成员还是在构造函数的主体中分配给数据成员并不重要;差异完全是风格上的。在可能的情况下,为了一致性起见,仍应首选初始化列表。
为什么在ctor主体中分配之前创建变量的额外副本,但通过初始化列表分配时没有创建额外副本。
因为赋值遵循初始化。换句话说,赋值是可选的,但初始化是强制性的。您可能不会注意到 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
}
};
要专门解决您关于副本的第一个问题(不涉及“++i”与“i++”):
构造函数是一个函数,它有参数。这些参数是作为参数传递的变量的副本(除非使用引用传递)。您现在可以操作此副本或用它做任何其他事情。然后,当您分配时,您获取参数的这个副本(操纵的可能性)并将其分配给成员变量。当您使用初始化列表时,编译器可以在没有此副本的情况下立即优化分配,因为除了初始化之外您不会将其用于任何其他操作(并且在将其分配给成员变量之前不能对其进行修改)。