Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/148.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/clojure/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 构造函数初始值设定项列表导致的优化_C++_Performance_Constructor_Initializer List - Fatal编程技术网

C++ 构造函数初始值设定项列表导致的优化

C++ 构造函数初始值设定项列表导致的优化,c++,performance,constructor,initializer-list,C++,Performance,Constructor,Initializer List,构造函数应通过初始化其所有成员对象 初始值设定项列表(如果可能)。这比建造新的大楼更有效 构造函数通过构造函数体内部的赋值 有人能解释一下,为什么在一个例子的帮助下使用初始值设定项列表更有效?好吧,否则调用默认构造函数,然后执行赋值。这会延长一步,并且可能会变得非常低效,具体取决于初始化的性质。以防止双重初始化 class B { //whatever }; class A { B _b; public: A(B& b) }; 现在有两种情况: //only initia

构造函数应通过初始化其所有成员对象 初始值设定项列表(如果可能)。这比建造新的大楼更有效 构造函数通过构造函数体内部的赋值


有人能解释一下,为什么在一个例子的帮助下使用初始值设定项列表更有效?

好吧,否则调用默认构造函数,然后执行赋值。这会延长一步,并且可能会变得非常低效,具体取决于初始化的性质。

以防止双重初始化

class B
{
//whatever
};

class A
{
   B _b;
public:
   A(B& b)
};
现在有两种情况:

//only initializes _b once via a copy constructor
A::A(B& b) : _b(b)
{
}

//initializes _b once before the constructor body, and then copies the new value
A::A(B& b)
{
   //_b is already initialized here
   //....
   //one extra instruction:
   _b = b;
}

因为它直接初始化,而不是默认初始化然后赋值。对于POD来说,性能方面的问题可能并不重要,但如果类型构造函数正在进行繁重的工作,则会有问题

此外,在某些情况下,您必须使用init list,因此您应该始终这样做以保持一致性。

来自:

考虑以下使用初始化列表初始化成员对象x_uu的构造函数:Fred::Fred():x_uu(无论什么){}。这样做最常见的好处是提高了性能。例如,若表达式whater与成员变量x_297;的类型相同,则whater表达式的结果直接在x_297;内部构造—编译器不会生成对象的单独副本。即使类型不同,编译器在初始化列表方面的工作通常比在赋值方面做得更好

构建构造函数的另一种(低效)方法是通过赋值,例如:Fred::Fred(){x_=whatever;}。在这种情况下,表达式whater会导致创建一个单独的临时对象,并且这个临时对象会被传递到x_u对象的赋值操作符中。然后,该临时对象在;处被销毁;。那太没效率了

似乎这还不够糟糕,在构造函数中使用赋值时还有另一个低效的原因:成员对象将由其默认构造函数完全构造,例如,这可能会分配一些默认内存量或打开一些默认文件。如果whatever表达式和/或赋值操作符导致对象关闭该文件和/或释放该内存(例如,如果默认构造函数没有分配足够大的内存池,或者如果它打开了错误的文件),那么所有这些工作都可能是徒劳的


就POD类型而言,初始化和赋值应该是等效的,因为如果没有显式执行初始化,它们将保持未初始化状态,因此唯一的操作仍然是赋值

对于具有默认构造函数和赋值运算符的类,情况就不同了:与其直接在正确的状态下创建对象,不如先调用默认构造函数,然后调用赋值运算符(在构造函数体内部)。这肯定比从一开始就用正确的构造函数初始化对象效率更低(两步而不是一步,第一步——默认构造——通常是完全浪费的)

直接初始化还有一个好处,即您可以确保,如果执行到达构造函数体,则所有各个字段都已处于“正确”状态。

请考虑以下程序:

#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); }
}
现在,想想为什么要这么做。在第一种情况下,
C1::C1(int)
a
必须是默认构造的,然后才能调用
C1
的构造函数。然后必须通过
操作符=
将其分配给。在我的简单示例中,没有可用的
int
赋值运算符,因此我们必须用int构造
A
。因此,不使用初始值设定项的代价是:一个默认构造函数、一个
int
构造函数和一个赋值运算符

在第二种情况下,
C2::C2(int)
,只调用
int
构造函数。无论默认
a
构造函数的成本是多少,显然
C2:C2(int)
的成本并不大于
C1::C1(int)
的成本


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

void operator=(int) { std::cout << "A::operator=(int)\n"; }
现在不可能笼统地说哪种形式更有效。在您的特定类中,默认构造函数的成本加上赋值的成本是否比非默认构造函数更昂贵?如果是这样,则初始化列表更有效。否则就不是了


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

假设您的类中有一个数据成员,它是std::string类型。执行此类的构造函数时,将自动调用默认构造函数字符串类,因为对象在构造函数体之前初始化

如果要在构造函数的主体内分配字符串,则将创建一个临时对象并将其提供给字符串的赋值运算符。临时对象将在赋值语句结束时销毁。如果在初始值设定项列表中执行此操作,则不会创建临时对象

class Person
{
public:
    Person(string s);
    ~Person();
private:
    string name;
};


// case 1
Person::Person(string s)
{
   name = s;   // creates a temporary object
}

// case 2
Person::Person(string s):name(s) {} // whereas this will not create a temporary object.

因为如果您使用的是Nizializer列表,那么您调用的是该对象的构造函数副本

如果在构造函数体中初始化对象,则执行赋值

例如: 这里我调用int的复制构造函数

在这里,我调用操作符=(const int&)。亚洲

    
myClass::myClass( int x )
    {
         member = x;
    }

通常情况下,asignment比简单副本执行更多操作。

    
myClass::myClass( int x )
    {
         member = x;
    }

你必须在帐户中还保留临时对象

为了完整性起见-如果我们假设优化者是健全的,那么这个问题是否只会减少到“默认构造函数然后运算符=与特定构造函数相比”?初始值设定项列表是否节省CPU周期?。能够

  
myClass::myClass( int x ) : member(x) {}

    
myClass::myClass( int x )
    {
         member = x;
    }