由于构造函数初始化列表的优化

nit*_*ian 13 c++ performance constructor initializer-list

如果可能,构造函数应该通过初始化列表初始化其所有成员对象.它比通过构造函数体内的赋值构建构造函数更有效.

有人可以解释一下,为什么在一个例子的帮助下使用初始化列表更有效率?

Rob*_*obᵩ 14

考虑这个程序:

#include <iostream>

struct A {
  A() { std::cout << "A::A()\n"; }
  A(int) { std::cout << "A::(int)\n"; }
  void operator=(const A&) { std::cout << "A::operator=(const A&)\n"; }
};

struct C1 {
  A a;
  C1(int i) { 
    a = i;
  }
};

struct C2 {
  A a;
  C2(int i)  : a(i) {}
};

int main() {
  std::cout << "How expesive is it to create a C1?\n";
  { C1 c1(7); }
  std::cout << "How expensive is it to create a C2?\n";
  { C2 c2(7); }
}
Run Code Online (Sandbox Code Playgroud)

在我的系统(Ubuntu 11.10,g ++ 4.6.1)上,程序产生以下输出:

How expesive is it to create a C1?
A::A()
A::(int)
A::operator=(const A&)
How expensive is it to create a C2?
A::(int)
Run Code Online (Sandbox Code Playgroud)

现在,考虑一下为什么这样做.在第一种情况下,C1::C1(int),a必须是默认构造之前C1的构造可以被调用.然后它必须分配给via operator=.在我的简单示例中,没有int可用的赋值运算符,因此我们必须构造A一个int.因此,不使用初始化程序的成本是:一个默认构造函数,一个int构造函数和一个赋值运算符.

在第二种情况下,C2::C2(int)int调用构造函数.无论默认A构造函数的成本C2:C2(int)是多少,显然成本都不会高于成本C1::C1(int).


或者,考虑这个替代方案.假设我们将以下成员添加到A:

void operator=(int) { std::cout << "A::operator=(int)\n"; }
Run Code Online (Sandbox Code Playgroud)

然后输出将显示:

How expesive is it to create a C1?
A::A()
A::operator=(int)
How expensive is it to create a C2?
A::(int)
Run Code Online (Sandbox Code Playgroud)

现在一般不可能说哪种形式更有效率.在您的特定类中,默认构造函数的成本加上赋值的成本比非默认构造函数更昂贵吗?如果是这样,则初始化列表更有效.否则就不是.

我写过的大多数类都会在init列表中更有效地初始化.但是,这是一个经验法则,并不一定适用于所有可能的情况.

  • @LinuxPenseur:赋值运算符将"A"作为参数; 因此,语句`a = 7`首先在调用赋值运算符之前创建一个临时的`A`(使用`A :: A(int)`).文字"7"被转换为"A". (3认同)

Mic*_*ker 9

好吧,否则你调用默认构造函数然后执行赋值.这是一个更长的步骤,取决于初始化的性质可能会变得非常低效.

  • 根据默认构造的性质和我们特定对象的分配,它可以节省大量的cpu周期.同样,因为构造函数体中的赋值意味着*first*执行默认构造函数,*然后*通过分配新值来丢弃(可能)它完成的任何(或部分).对于cpu而言,这显然比将数据放在首位更有用. (2认同)

Cat*_*lus 5

因为它直接初始化,而不是默认初始化然后分配.对于POD来说,性能方面可能无关紧要,但如果类型构造函数正在进行繁重的工作,那么它将会成功.

此外,在某些情况下,您必须使用init列表,因此您应始终保持一致性.